你如何在Haskell中管理对象图?

fad*_*bee 15 oop haskell

我正在尝试重新学习系统分析.我有很多面向对象的思想,但是我无法找到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)

您已经可以在这里看到一些问题:

  1. 我无法创建一致的SystemState - 某些值是'旧'值,例如s或s',而不是s''.
  2. 许多对"相同"数据的引用都有单独的副本.

我现在可以创建一个函数,它接受一个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中更简单(也更不安全).

yat*_*975 8

小提示:如果你使用递归letwhere(在.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更明确,如果这变得太麻烦.我没有在我的图形代码中这样做,因为我只有两个键类型,它们也是不同的,因此新类型不是必需的.


ja.*_*ja. 5

在你开始讨论继承和子类型多态之前,它没有得到面向对象的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结束,但听起来就好像......