haskell-effectful/effectful

Perform operations at lower levels in the effect stack?

torgeirsh opened this issue · 2 comments

Is it possible to perform an operation at a lower level in the effect stack, similar to how "hoist" from the mmorph package works for monad transformers? A similar mechanism might have a type signature like this:

(forall a. Eff es a -> Eff fs a) -> Eff (e ': es) b -> Eff (e ': fs) b

With an effect stack like [A, B, C], this would allow "running" the B effect, or transforming the stack into something like [A, D, C], or even [A, D, E, C].

I believe inject will reshuffle effects into any order you want, at which point you can just dispatch things from the front of the list. But can you explain why you want to do this?

The primary use case for me is adding context to effects. An outer function might have more information for e.g. logging or errors available than the inner function raising the error or performing the logging. Something like withReader can add context to loggers, and something like cleff's mapError (analogous to withExceptT, and trivial to implement in effectful) can add context to errors. Since they change the type of the effect by design, my understanding is that they have to be implemented by running the effect with the old type, and introduce a new effect with the new type. Both withReader and cleff's mapError are implemented this way, and only work on the top of the effect stack (the problem is similar for mtl vs. transformers). If you want to call both withReader and mapError on an effectful action, that's obviously not going to work.

It seems like multiple calls to inject, along with intermediate type signatures to guide the type checker, does the trick for the specific problem of calling both withReader and mapError, but it's not a particularily ergonomic experience. :) I'm not sure if inject can be used to implement something simialr to the more general "hoist" without some extra type-level gymnastics, since e : es would probably have to be turned into es ++ [e] and back? But maybe I'm thinking too much in terms of mtl/transformers, and there are better ways to go about it with effectful?