I'm trying to re-learn systems analysis. I've got a lot of object-oriented thinking for which I'm not able to find equivalents in Haskell, yet.
A fictional system consists of Ambulance Stations, Ambulances and Crew. (It's getting object-y already.) All this state can be wrapped up in a big SystemState type. SystemState [Stations] [Ambulances] [Crew]. I can then create functions which take a SystemState, and return a new 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)
Here's a ghci session where I create some data.
*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}]
You can already see a couple of problems here:
- I have been unable to create a consistent SystemState - some of the values are 'old' values, such as s or s', rather than s''.
- lots of references to the 'same' data have separate copies.
I could now create a function which takes a SystemState and a Crew member's name which returns a new SystemState where that crew member is 'off-duty'.
My problem is that I have to find and change the crew member in the Ambulance and the (identical copy of the) crew member in the SystemState.
This is possible for small systems, but real systems have many more linkages. It looks like a n-squared problem.
I am very aware that I am thinking about the system in an object-oriented way.
How would such a system be correctly created in Haskell?
Edit: Thanks to everyone for your answers, and those on reddit too http://www.reddit.com/r/haskell/comments/b87sc/how_do_you_manage_an_object_graph_in_haskell/
My understanding now seems to be that I can do the things I want in Haskell. On the downside, it seems to be that object/record/struct graphs aren't 'first class' objects in Haskell (as they are in C/Java/etc.), because of the necessary lack of references. There's just a trade off - some tasks are syntactically simpler in Haskell, some are simpler (and more unsafe) in C.