
Hello Mikhail, (Apologies for reviving a two month old thread). Have you put some thought into whether or not these extra classes generalize in a way that is not /quite/ as general as MonadBaseControl (so as to give you the power you need) but still allow you to implement the functionality you are looking for? I'm not sure but it seems something along the lines of unwind-protect ala Scheme might be sufficient. Edward Excerpts from Mikhail Vorozhtsov's message of Mon Nov 14 01:25:34 -0500 2011:
Hi Mikhail,
your type class:
class MonadAbort e μ ⇒ MonadRecover e μ | μ → e where recover ∷ μ α → (e → μ α) → μ α
looks a lot like the MonadCatchIO type class from MonadCatchIO-transformers:
class MonadIO m => MonadCatchIO m where catch :: E.Exception e => m a -> (e -> m a) -> m a
I haven't looked at your code in detail but are you sure your continuation based AIO monad doesn't suffer from the same unexpected behavior as the ContT monad transformer with regard to catching and handling exceptions? Yes, I'm sure. The reason why it works is because finally/bracket/etc are not implemented on top of 'recover' (i.e. they don't assume that
On 11/14/2011 06:55 AM, Bas van Dijk wrote: throwing an exception is the only reason control can escape). The following class takes care of it:
class (Applicative μ, Monad μ) ⇒ MonadFinally μ where finally' ∷ μ α → (Maybe α → μ β) → μ (α, β) finally ∷ μ α → μ β → μ α finally m = fmap fst . finally' m . const
Finalizers have type 'Maybe α → μ β' so we can
(a) Thread transformer side effects properly:
instance MonadFinally μ ⇒ MonadFinally (L.StateT s μ) where finally' m f = L.StateT $ \s → do ~(~(mr, _), ~(fr, s'')) ← finally' (L.runStateT m s) $ \mbr → do let ~(a, s') = case mbr of Just ~(x, t) → (Just x, t) Nothing → (Nothing, s) L.runStateT (f a) s' return ((mr, fr), s'')
(b) Detect that control escaped computation before producing a result (finalizer will be called with 'Nothing' in that case).
instance (MonadFinally μ, Error e) ⇒ MonadFinally (ErrorT e μ) where finally' m f = ErrorT $ do ~(mr, fr) ← finally' (runErrorT m) $ \mbr → runErrorT $ f $ case mbr of Just (Right a) → Just a _ → Nothing return $ (,) <$> mr <*> fr
That of course does not mean that I can use 'finally' and friends with ContT, but I can use them with monads which are carefully /implemented/ on top of ContT but do not expose it's full power to the users.