4 ms·
This paper is over my head, but in case anyone is wondering what Free monads are I can give a teaser for how they're useful. Say you're automating a bio lab. Y
by seagreen 11y ago
This paper is over my head, but in case anyone is wondering what Free monads are I can give a teaser for how they're useful.
Say you're automating a bio lab. You're writing software to control evacuations. The lab machinery can execute two actions (1) Alarm (sets off an evacuation alarm in an area) (2) Sterilize (burns an area with fire). Obviously we would like to call (1) for an area well before we call (2).
To these two actions we can add one of our own: (3) Wait (for some number of seconds).
Now we write a function (using Haskell types, but they're easy to read):
evacuate :: ContaminationInfo -> IO ()
This function takes in the state of a lab (what areas have been contaminated, what areas are at risk, etc.) makes an evacuation plan, and executes it.
Now, the first things you notice is that this is a horrible function. How could we test it? We would need an actual lab (or mocks, but that's no fun).
Let's try again.
makePlan :: ContaminationInfo -> [Action]
(Where action is either `Alarm Area`, `Sterilize Area`, or `Wait Seconds`)
This is way better! Now we can write interpreters for `[Action]`.
testContaminationPlan :: [Action] -> TestResults
prettyPrintContaminationPlan :: [Action] -> String
graphContaminationPlan :: [Action] -> SVG
runContaminationPlan :: [Action] -> IO ()
Note that all of this can be done in Python, which is pretty cool! Sadly we can't write `makePlan :: ContaminationPlan -> [Action]` in Python though. It would have to be `makePlan :: ContaminationPlan -> IO [Action]`, which means that there could be side effecting functions that slip by the programmer and aren't reflected in the return type. If one of these is Sterilize someone could have a bad day:(
Now lets say our wonderful engineers build us an excellent new action: (4) Scan (check an area for risk of contamination).
Now our [Action] plan starts to break down. Before an [Action] was very simple. An example might have been [Alarm Area1, Alarm Area2, Wait 200 Seconds, Sterilize Area1, Sterilize Area2].
But our scanner allows later actions to depend on the result of previous actions! [Action] isn't sufficient to model that -- how would our interpreters know how to thread things together (our goal is to be able to do things like start scanning an at-risk room like crazy, but not actually evacuate it unless contamination appears).
If only we had some way for our data type to reflect that some actions might need to be fed into others? Hmm, I wonder what we could use for that ...
- seagreen 11y agoPS Pretty much all my knowledge of this subject (and the pretty printing interpreter example in particular) comes from here: http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html http://www.haskellforall.com/2012/06/you-could-have-invented...
- plonh 11y agoThis is literally textbook Design Patterns stuff. Haskell gives you executable machine-checkable design patterns, instead of just comments.