[HN Gopher] Hexagonal Architecture vs. Free Monad
___________________________________________________________________
Hexagonal Architecture vs. Free Monad
Author : revskill
Score : 21 points
Date : 2022-11-23 06:46 UTC (16 hours ago)
(HTM) web link (deque.blog)
(TXT) w3m dump (deque.blog)
| NM-Super wrote:
| 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.
| headbee wrote:
| I had read this article more as a "from-first-principles"
| example, but it's a good note for people who might be
| unfamiliar with practical Haskell. Using an effects system also
| promotes the domain DSL from an initial-tagless language (only
| the interpreter is extensible) to a tagless-final language
| (both the language and the interpreter are extensible).
|
| It may be worth noting that effectful, polysemy, and the other
| "new kids" of effects systems are not the only option: record-
| of-functions, mtl, and type-class effects aren't cutting edge,
| but they're a little more approachable from an OOP dependency
| injection pattern.
___________________________________________________________________
(page generated 2022-11-23 23:01 UTC)