This article is from 2017. In the meantime, the Haskell community has been playing around a lot with Effect Systems, which give you similar benefits with much better performance and generally less boilerplate. In particular, the effectful package (
https://hackage.haskell.org/package/effectful) would let you write the code in the article as:
reserve :: (SearchTrains :> es, GetTypology :> es, Log :> es, RequestReservation :> es) => ReservationRequest -> Eff es ReservationResult
reserve request = do
trains <- send (SearchTrain (_dateTime request)) -- Search for trains at date-time
forM trains $ \train -> -- Loop on all the trains
typology <- send (GetTypology train) -- Get the typology of a train
... -- Implement the reservation rules
send (Log "Confirming reservation")
confirmed <- send (RequestReservation reservation)
...
(In actual usage you won't tend to use the send function - you'll write wrappers that do it for you).
This lets you avoid having to write an interpreter for some particular ReservationExpr monad - instead, you specify individual effect handlers, then use them as you want. For example, we could write something like this:
-- Make search train never return any trains
runWithoutTrains :: Eff (SearchTrains ': es) a -> Eff es a
runWithoutTrains = interpret $ \_unusedHere (SearchTrain name) -> pure []
ignoreLogs :: Eff (Log ': es) a -> Eff es a
ignoreLogs = interpret $ \_unusedHere (Log _message) -> pure ()
What's even more fun is that you can actually
interpose effects. So, I can write this:
loggingTrainSearches :: (Log :> es, SearchTrain :> es) => Eff es a -> Eff es a
loggingTrainSearches = interpose $ \_unusedHere (SearchTrain name) -> do
send (Log ("Searching for train " <> show name))
send (SearchTrain name)
Being able to play around with handlers like this is really useful, both for testing and for adding on niceties to business logic.
This lets you avoid having to write an interpreter for some particular ReservationExpr monad - instead, you specify individual effect handlers, then use them as you want. For example, we could write something like this:
What's even more fun is that you can actually interpose effects. So, I can write this: Being able to play around with handlers like this is really useful, both for testing and for adding on niceties to business logic.