Hierarchical Free Monads - Polysemy

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Sridhar Ratnakumar

How does all of this relate (in terminology) to polysemy?


Posted in r/haskell by u/effectfully • 4 points and 1 comment

Well, polysemy uses final encoding of free monad, with interface that resembles that of final tagless approach, so comparing to those approaches it's sort of hybrid:

  • because actions are embedded into free encoding (in Sem) instead of directly into concrete final monad, we're free to introduce multiple interpretations of same effect without having to do "newtype wrangling" on final monad
  • because membership of effects in application code is described in constraint instead of final type, embedding of "subsystems" happens sort of "automatically" - final row just gets more constraints to satisfy

That example from "The straw man argument" section would be something like:

import System.Random

import Polysemy
import Control.Category ((>>>))

data LogLevel = Info
type Message  = String

data Log :: Effect where
  LogMessage :: LogLevel -> Message -> Log m ()

mkSem ''Log

data RandomInt :: Effect where
  GetRandomInt :: (Int, Int) -> RandomInt m Int

mkSem ''RandomInt

type App r = Members '[Log, RandomInt] r

runApp :: Sem '[Log, RandomInt, Embed IO] a -> IO a
runApp = interpret (\(LogMessage _ m) -> embed $ putStrLn m )
     >>> interpret (\(RandomInt r   ) -> embed $ randomRIO r)
     >>> runM

logInfo :: App r => Message -> Sem r ()
logInfo = logMessage Info

printRandomFactorial :: App r => Sem r ()
printRandomFactorial = do
  n <- getRandomInt (1, 100)
  logInfo $ show $ product [1..n]

in polysemy (haven't typechecked though :big_smile:)