Is `MonadBaseControl` dead?

Greetings! This is also posted at https://www.reddit.com/r/haskell/comments/o4sggi/is_monadbasecontrol_dead/ https://www.reddit.com/r/haskell/comments/o4sggi/is_monadbasecontrol_dead/ but I'd like to also seek wider help here. (Background issue I'm facing: https://github.com/snapframework/snap-core/issues/309) I think the functionality of [MonadUnliftIO](https://hackage.haskell.org/package/unliftio-core-0.2.0.1/docs/Control-Monad...) is exactly what I want, but unfortunately [Snap](https://hackage.haskell.org/package/snap-core-1.0.4.2/docs/Snap-Core.html#t:...) appears not eligible for:
the intuition is that a monad must have no monadic state, but may have monadic context. This essentially limits MonadUnliftIO to ReaderT and IdentityT transformers on top of IO.
Then I found `Snap` [has a MonadBaseControl instance](https://hackage.haskell.org/package/snap-core-1.0.4.2/docs/src/Snap.Internal...), and thought it should work for me. But sadly to find it [losing states](https://github.com/snapframework/snap-core/issues/309). Also I failed to find a user side example of `MonadBaseControl` on Github ([as there're only instance impl. hits](https://github.com/search?l=Haskell&q=org%3Asnapframework+liftBaseWith&type=Code)), so I have not way to verify whether that's due to my incorrect usage. Any way quoting u/snoyberg : https://github.com/fpco/unliftio/issues/17#issuecomment-363655106
Continuation-based monads cannot have instances of MonadUnliftIO, the same as MonadBaseControl and MonadMask.
Then I'm not sure `Snap`'s `MonadBaseControl` instance shall really work. Also seems there had been debates I'm not aware of with clue at: https://github.com/fpco/unliftio/tree/master/unliftio#monad-control
The main contention until now is that unlifting in a transformer like `StateT` is unsafe. This is not universally true: if only one action is being unlifted, no ambiguity exists. So, for example, `try :: IO a -> IO (Either e a)` can safely be unlifted in `StateT`, while `finally :: IO a -> IO b -> IO a` cannot.
`monad-control` allows us to unlift both styles. In theory, we could write a variant of `lifted-base` that never does state discards, and let `try` be more general than `finally`. In other words, this is an advantage of `monad-control` over `MonadUnliftIO`. We've avoided providing any such extra typeclass in this package though, for two reasons:
* `MonadUnliftIO` is a simple typeclass, easy to explain. We don't want to complicated matters (`MonadBaseControl` is a notoriously difficult to understand typeclass). This simplicity is captured by the laws for `MonadUnliftIO`, which make the behavior of the run functions close to that of the already familiar `lift` and `liftIO`. * Having this kind of split would be confusing in user code, when suddenly `finally` is not available to us. We would rather encourage [good practices](https://www.fpcomplete.com/blog/2017/06/readert-design-pattern) from the beginning.
Another distinction is that `monad-control` uses the `MonadBase` style, allowing unlifting to arbitrary base monads. In this package, we've elected to go with `MonadIO` style. This limits what we can do (e.g., no unlifting to `STM`), but we went this way because:
* In practice, we've found that the vast majority of cases are dealing with `IO` * The split in the ecosystem between constraints like `MonadBase IO` and `MonadIO` leads to significant confusion, and `MonadIO` is by far the more common constraints (with the typeclass existing in `base`)
So is `MonadBaseControl` dead or alive? Any user side example for it?
participants (1)
-
YueCompl