A software engineer website

Polysemy: Tagged effects

Gautier DI FOLCO December 21, 2022 [Haskell] #haskell #polysemy #design #effects systems

At some point you'll have a lot of effects, which is not an issue, however you might duplicated types (usually, I would advise to use newtype, but you might not have the hand on the consumer code).

That's why you have the Tagged effect:

newtype Tagged k e m a where
  Tagged :: forall k e m a. e m a -> Tagged k e m a

tag :: forall k e r a. Member (Tagged k e) r => Sem (e ': r) a -> Sem r a

We can use directly:

displayJoined :: Members '[Tagged "name" (Reader String), Tagged "location" (Reader String), Embed IO] r => Sem r ()
displayJoined = do
  name <- tag @"name" @(Reader String) ask
  location <- tag @"location" @(Reader String) ask
  embed $ putStrLn $ "Your name is " <> name
  embed $ putStrLn $ "Your location is " <> location

Or to wrap existing code:

displayName :: Members '[Reader String, Embed IO] r => Sem r ()
displayName = do
  name <- ask
  embed $ putStrLn $ "Your name is " <> name

displayLocation :: Members '[Reader String, Embed IO] r => Sem r ()
displayLocation = do
  location <- ask
  embed $ putStrLn $ "Your location is " <> location

displaySplit :: Members '[Tagged "name" (Reader String), Tagged "location" (Reader String), Embed IO] r => Sem r ()
displaySplit = do
  tag @"name" @(Reader String) displayName
  tag @"location" @(Reader String) displayLocation

Then we have untag which unwraps the underlying effect:

runApp :: Sem '[Tagged "name" (Reader String), Tagged "location" (Reader String), Embed IO] a -> IO a
runApp =
  runM
  . runReader "France"
  . untag @"location"
  . runReader "Gautier"
  . untag @"name"

Finally, here's what it gives:

Your name is Gautier
Your location is France

See the full the code here.