Polysemy: Design heuristics: Hiding interpretersGautier DI FOLCO May 03, 2023 [Haskell] #haskell #polysemy #design #effects systems
In the codebases I have worked on all have multiple global interpreters, most noticeably for production and tests.
From time to time, you want to have the same effect lists, for example to dynamically change the interpretation.
To do so you can inject effects:
Let's take back our
data Pause (m :: Type -> Type) a where Pause :: Int -> Pause m () makeSem ''Pause
We can have two interpreters:
= interpret $ \case Pause x -> embed $ threadDelay x runPause = interpret $ \case Pause x -> embed $ threadDelay x runPause
Then we can have two global interpreters:
= runM . runPauseEmbed . raiseUnder @(Embed IO) interpretFinal = runFinal . runPauseFinal . raiseUnder @(Final IO) interpretEmbed
raiseUnder injects one effect under the current head.
There are plenty of other functions such as
raiseUnder3, which adds 2/3 effects under the head,
raise3Under which adds an effect in 2nd/3rd position under the effects' head.
insertAt can helps you to acheive that with more flexibility.
subsume_ will give you the power to rewrite your effects list (effects order, duplicate or merge effects).
See the full the code here.