我正在尝试重新学习系统分析。我有很多面向对象的思想,但是我还无法在Haskell中找到对应的思想。

一个虚构的系统由救护站,救护车和乘员组组成。 (它已经成为对象了。)所有这些状态都可以包装成一个大的SystemState类型。 SystemState [工作站] [救护车] [乘员组]。然后,我可以创建采用SystemState并返回新SystemState的函数。

module AmbSys
    ( version
    , SystemState
    , Station
    , Ambulance
    , Crew
    ) where

version = "0.0.1"

data SystemState = SystemState [Station] [Ambulance] [Crew] deriving (Show)

data Station = Station { stName :: String
                       , stAmbulances :: [Ambulance]
                       } deriving (Show)

data Ambulance = Ambulance { amCallSign :: String
                           , amStation :: Station
                           , amCrew :: [Crew]
                           } deriving (Show)

data Crew = Crew { crName :: String
                 , crAmbulance :: Ambulance
                 , crOnDuty :: Bool
                 } deriving (Show)

这是一个ghci session ,我在其中创建一些数据。
*AmbSys> :load AmbSys
[1 of 1] Compiling AmbSys           ( AmbSys.hs, interpreted )
Ok, modules loaded: AmbSys.
*AmbSys> let s = Station "London" []
*AmbSys> let a = Ambulance "ABC" s []
*AmbSys> let s' = Station "London" [a]
*AmbSys> let c = Crew "John Smith" a False
*AmbSys> let a' = Ambulance "ABC" s [c]
*AmbSys> let s'' = Station "London" [a']
*AmbSys> let system_state = SystemState [s''] [a'] [c]
*AmbSys> system_state
SystemState [Station {stName = "London", stAmbulances = [Ambulance {amCallSign = "ABC",
 amStation = Station {stName = "London", stAmbulances = []}, amCrew = [Crew
 {crName = "John Smith", crAmbulance = Ambulance {amCallSign = "ABC",
 amStation = Station {stName = "London", stAmbulances = []}, amCrew = []},
 crOnDuty = False}]}]}] [Ambulance {amCallSign = "ABC", amStation = Station {
 stName = "London", stAmbulances = []}, amCrew = [Crew {crName = "John Smith",
 crAmbulance = Ambulance {amCallSign = "ABC", amStation = Station {stName = "London",
 stAmbulances = []}, amCrew = []}, crOnDuty = False}]}] [Crew {crName = "John Smith",
 crAmbulance = Ambulance {amCallSign = "ABC", amStation = Station {stName = "London",
 stAmbulances = []}, amCrew = []}, crOnDuty = False}]

您已经在这里看到了几个问题:
  • 我无法创建一致的SystemState-一些值是“旧”值,例如s或s',而不是s''。
  • 对“相同”数据的大量引用具有单独的副本。

  • 现在,我可以创建一个带有SystemState和Crew成员姓名的函数,该函数返回一个新的SystemState,其中该机组成员处于“值班”状态。

    我的问题是我必须在救护车中找到并更换机组人员,并在SystemState中找到并更换机组人员(相同副本)。

    对于小型系统而言,这是可能的,但实际系统具有更多的链接。它看起来像一个n平方的问题。

    我非常清楚我正在以一种面向对象的方式来思考系统。

    在Haskell中如何正确创建这样的系统?

    编辑:感谢大家的回答,也感谢reddit上的回答http://www.reddit.com/r/haskell/comments/b87sc/how_do_you_manage_an_object_graph_in_haskell/

    我现在的理解似乎是我可以在Haskell中做我想做的事情。不利的一面似乎是对象/记录/结构图在Haskell中不是“一流”对象(就像在C / Java / etc中一样),因为必需缺少引用。只是需要权衡取舍-有些任务在语法上更简单,在Haskell中,有些在C中更简单(更不安全)。

    最佳答案

    小提示:如果您使用递归letwhere(在.hs文件中,我认为它不适用于ghci),则至少可以更轻松地设置初始图形,如下所示:

    ambSys = SystemState [s] [a] [c] where
        s = Station "London" [a]
        a = Ambulance "ABC" s [c]
        c = Crew "John Smith" a False
    

    这将使您进入我认为您要达到的状态,但不要尝试使用派生的Show实例:-)更新这些状态是另一 jar bean;我会考虑一下,然后看看我想出了什么。

    编辑:我已经考虑了更多,这是我可能会做的:

    我会通过使用键来打破对象图中的循环。这样的事情会起作用(在构建实图时,我使用了类似的方法):
    import qualified Data.Map as M
    
    version = "0.0.1"
    
    newtype StationKey = StationKey String deriving (Eq,Ord,Show)
    newtype AmbulanceKey = AmbulanceKey String deriving (Eq,Ord,Show)
    newtype CrewKey = CrewKey String deriving (Eq,Ord,Show)
    
    data SystemState = SystemState (M.Map StationKey Station) (M.Map AmbulanceKey Ambulance) (M.Map CrewKey Crew) deriving (Show)
    
    data Station = Station { stName :: StationKey
                           , stAmbulances :: [AmbulanceKey]
                           } deriving (Show)
    
    data Ambulance = Ambulance { amCallSign :: AmbulanceKey
                               , amStation :: StationKey
                               , amCrew :: [CrewKey]
                               } deriving (Show)
    
    data Crew = Crew { crName :: CrewKey
                     , crAmbulance :: AmbulanceKey
                     , crOnDuty :: Bool
                     } deriving (Show)
    
    ambSys = SystemState (M.fromList [(londonKey, london)]) (M.fromList [(abcKey, abc)]) (M.fromList [(johnSmithKey, johnSmith)]) where
        londonKey = StationKey "London"
        london = Station londonKey [abcKey]
        abcKey = AmbulanceKey "ABC"
        abc = Ambulance abcKey londonKey [johnSmithKey]
        johnSmithKey = CrewKey "John Smith"
        johnSmith = Crew johnSmithKey abcKey False
    

    然后,您可以开始定义自己的状态修改组合器。如您所见,状态的构建现在更加冗长,但是show ing再次可以很好地工作!

    同样,我可能会设置一个类型类,以使StationStationKey等类型之间的链接更加明确,如果这变得太麻烦了。我没有在图形代码中执行此操作,因为那里只有两个键类型,它们也是不同的,因此不需要新类型。

    10-02 23:30