Bluefin compared to effectful [video]

I am interested in how the so-called interpose operation can be used in bluefin.
The interpose operation plays a central role in dynamic effects.

It allows for locally modifying the behavior of an effect handler within the scope enclosed by interpose, and can be considered a kind of generalization of the Reader’s local operation.

When users hear that “dynamic effects are possible,” they will expect the functionality provided by interpose.

interpose is usually used as follows:

modifyPlus1 :: Reader Int :> es => Eff es a -> Eff es a
modifyPlus1 = interpose \case
    Ask       -> (+1) <$> ask
    Local f m -> local f m

main = runReader 0 $ modifyPlus1 $ print =<< ask
-- > main
-- 1

In practical terms, for example, it can be used to add logging locally after the fact to an effect:

logWriteDB = interpose \case
    WriteDB ... -> do
        writeDB ...
        log "Wrote to the DB"

Now, in effectful, there is an interpose function at Effectful.Dispatch.Dynamic. On the other hand, it seems that bluefin currently does not have this functionality for general effects, but if you have any ideas, please let me know.

My guess is that (considering that bluefin explicitly propagates evidence via arguments* instead of implicitly holding the environment in a ReaderT IO), in bluefin it would theoretically take the following form:

modifyPlus1
    :: Reader Int :> es
    => Reader Int e
    -> (Reader Int e -> Eff (e :& es) a)
    -> Eff (e :& es) a
modifyPlus1 r f = f $ r{ask = (+1) <$> ask r}

newtype Reader r e = MkReader { ask :: ? r } -- Not quite sure what to do here...

* It seems I had a slight misunderstanding… What bluefin carries around isn’t the handler functions, but just an IORef, right?

5 Likes