我正在尝试重新学习系统分析。我有很多面向对象的思想,但是我还无法在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和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中更简单(更不安全)。
最佳答案
小提示:如果您使用递归let
或where
(在.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再次可以很好地工作!同样,我可能会设置一个类型类,以使
Station
和StationKey
等类型之间的链接更加明确,如果这变得太麻烦了。我没有在图形代码中执行此操作,因为那里只有两个键类型,它们也是不同的,因此不需要新类型。