Polysemy: Introduction to Effect interpretation

In my last log we defined the Trace effect:

{-# LANGUAGE TemplateHaskell #-}

module EffectDefinitionIntro
  ( Trace (..),
    trace,
  )
where

import Data.Kind
import Polysemy

data Trace (m :: Type -> Type) a where
  Trace :: String -> Trace m ()

makeSem ''Trace

One of the main interest of effects systems is to be able to define multiple interpretations for the same effects.

The simplest interpreter we can write is to drop simply the action:

ignoreTrace :: Sem (Trace ': r) a -> Sem r a
ignoreTrace = interpret $ \case
  Trace _ -> pure ()

interpret will "consume" Trace contructor-by-constructor.

Note: we have a useful type alias InterpreterFor for interpreters definitions.

However, Trace is simple enough to have a dropping interpreter, let's come with a more useful definition:

traceToStdout :: Member (Embed IO) r => InterpreterFor Trace r
traceToStdout = interpret $ \case
  Trace m -> embed $ putStrLn m

We can also compose interpreters:

traceToStderr :: Member (Embed IO) r => Sem (Trace ': r) a -> Sem r a
traceToStderr = traceToHandle stderr

traceToHandle :: Member (Embed IO) r => Handle -> Sem (Trace ': r) a -> Sem r a
traceToHandle handle = interpret $ \case
  Trace m -> embed $ hPutStrLn handle m

See the full the code here.