我正在尝试重新学习系统分析.我有很多面向对象的思想,但是我无法找到Haskell中的等价物.
一个虚构的系统包括救护站,救护车和船员.(它已经得到了对象y.)所有这些状态都可以包含在一个大的SystemState类型中.SystemState [Stations] [Ambulances] [Crew].然后我可以创建带有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)
Run Code Online (Sandbox Code Playgroud)
这是一个ghci会话,我在其中创建一些数据.
*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}]
Run Code Online (Sandbox Code Playgroud)
您已经可以在这里看到一些问题:
我现在可以创建一个函数,它接受一个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 /等中),因为缺少引用.只有一个权衡 - 一些任务在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
Run Code Online (Sandbox Code Playgroud)
这将使您进入我认为您想要达到的状态,但不要尝试使用派生Show实例:-)更新这样的状态是另一种豆类; 我会考虑一下,看看我想出了什么.
编辑:我已经考虑了一些,这是我可能会做的:
我会通过使用键来打破对象图中的循环.这样的东西可以工作(我在构建真实图形时使用了类似的方法):
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
Run Code Online (Sandbox Code Playgroud)
然后,您可以开始定义自己的状态修改组合器.正如你所看到的,现在国家的建设更加冗长,但是show再次运作良好!
此外,我可能会设置一个类型类,以使等Station和类之间的链接StationKey更明确,如果这变得太麻烦.我没有在我的图形代码中这样做,因为我只有两个键类型,它们也是不同的,因此新类型不是必需的.
在你开始讨论继承和子类型多态之前,它没有得到面向对象的y.在构想OO之前,程序包含称为"救护车"和"车站"的数据结构; OO没有垄断数据抽象和封装.FP设计也将是"域驱动",命令式编程也是如此.
您遇到的问题是如何管理状态,这是Haskell中的一个长期问题(实际上,在任何编程系统中,请参阅SICP的3.1.3节(Abelson和Sussman的结构和计算机程序的解释http:// mitpress). mit.edu/sicp/ (不要被大的,学术的词语,域名,它的可读性推迟 - 他们的例子是银行帐户).
你的问题是你引用并保持过时的状态.我建议你编写采用当前状态,修改它并返回新状态的函数.就像是:
addStation state station =
let (SystemState stations ambs crews) = state
in SystemState (station:stations) ambs crews)
Run Code Online (Sandbox Code Playgroud)
如果你使用ghci解释器,那么知道它的变量会很方便,它包含了最后一次计算的结果.
你最终会在State Monad结束,但听起来就好像......