[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)