freer-simple
freer-simple copied to clipboard
Handling the same effect multiple times with interpret and interpose
Here's self-containing example:
data MyEffect r where
Execute :: MyEffect ()
makeEffect ''MyEffect
handleFirstEffect :: (LastMember IO effs) => MyEffect ~> Eff effs
handleFirstEffect = \case
Execute -> liftIO $ print "handleFirstEffect"
handleSecondEffect :: (LastMember IO effs) => MyEffect ~> Eff effs
handleSecondEffect = \case
Execute -> liftIO $ print "handleSecondEffect"
main :: IO ()
main = do
runM
$ interpret handleSecondEffect
$ interpose handleFirstEffect execute
The programs prints only handleFirstSecond
, but does not print handleSecondEffect
. I would have expected to also print the latter, since interpose
should allow someone to respond to the effect while leaving it unhandled.
Bug or is there's something I'm not understanding?
You’re right that the documentation for interpose
is somewhat confusing/ambiguous. Really, interpose
does intercept and “retire” each action executed by send
, so enclosing handlers do not see the intercepted actions. If you want that behavior, you need to explicitly re-send
the action in the interposing handler:
handleFirstEffect :: (LastMember IO effs, Member MyEffect effs) => MyEffect ~> Eff effs
handleFirstEffect = \case
Execute -> do
liftIO $ print "handleFirstEffect"
send Execute
This is generally a Good Thing, because it gives you more control. For example, if you wanted "handleSecondEffect"
to be printed first, you could swap the order of the statements:
handleFirstEffect :: (LastMember IO effs, Member MyEffect effs) => MyEffect ~> Eff effs
handleFirstEffect = \case
Execute -> do
send Execute
liftIO $ print "handleFirstEffect"
The benefit of using interpose
over interpret
is simply that interpose
does not eliminate the effect from the effs
type-level list and therefore does not require it to be the first element of that list. This is convenient when you do not actually want to grow the list of effects, you just want to add a more-nested handler for an effect you know is already present. Otherwise, interpose
doesn’t do anything special, it works just like interpret
.
Does that answer your question? (I’ll leave this issue open regardless, since the documentation should be clarified.)
Thank you very much. It does answer my question :)