Skip to content

Commit

Permalink
Merge pull request #131 from haskell/koz/monad-cont
Browse files Browse the repository at this point in the history
Make MonadCont for ContT polykinded, add liftCallCC
  • Loading branch information
kozross authored Aug 10, 2022
2 parents 0a71324 + c3dfa94 commit 5a4c24d
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 4 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.markdown
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
2.3.1 -- 2022-07-05
-----
* Add `modifyError` to `Control.Monad.Error.Class`, and re-export from
`Control.Monad.Except`.
* Make the `MonadCont` instance for `ContT` more polykinded; now, `r` is allowed
to be of an arbitrary kind `k`, rather than only `Type`.
* Add a generic `liftCallCC` for use with any `MonadTrans`.
* Add `modifyError` to `Control.Monad.Error.Class`
* Return re-export of `ExceptT` and related functions to `Control.Monad.Except`.
* Add `label` function to `MonadCont`
Expand Down
43 changes: 39 additions & 4 deletions Control/Monad/Cont/Class.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QuantifiedConstraints #-}
-- Needed because the CPSed versions of Writer and State are secretly State
-- wrappers, which don't force such constraints, even though they should legally
-- be there.
Expand Down Expand Up @@ -58,8 +61,10 @@ module Control.Monad.Cont.Class (
MonadCont(..),
label,
label_,
liftCallCC,
) where

import Data.Kind (Type)
import Control.Monad.Fix (fix)
import Control.Monad.Trans.Cont (ContT)
import qualified Control.Monad.Trans.Cont as ContT
Expand All @@ -81,8 +86,11 @@ import Control.Monad.Trans.Accum (AccumT)
import qualified Control.Monad.Trans.Accum as Accum
import qualified Control.Monad.Trans.RWS.CPS as CPSRWS
import qualified Control.Monad.Trans.Writer.CPS as CPSWriter
import Control.Monad.Trans.Class (MonadTrans (lift))
import Control.Monad.Signatures (CallCC)
import Control.Monad (join)

class Monad m => MonadCont m where
class Monad m => MonadCont (m :: Type -> Type) where
{- | @callCC@ (call-with-current-continuation)
calls a function with the current continuation as its argument.
Provides an escape continuation mechanism for use with Continuation monads.
Expand All @@ -104,13 +112,14 @@ class Monad m => MonadCont m where
callCC :: ((a -> m b) -> m a) -> m a
{-# MINIMAL callCC #-}

instance MonadCont (ContT r m) where
-- | @since 2.3.1
instance forall k (r :: k) (m :: (k -> Type)) . MonadCont (ContT r m) where
callCC = ContT.callCC

-- ---------------------------------------------------------------------------
-- Instances for other mtl transformers

{- | @since 2.2 -}
-- | @since 2.2
instance MonadCont m => MonadCont (ExceptT e m) where
callCC = Except.liftCallCC callCC

Expand Down Expand Up @@ -165,9 +174,35 @@ instance
label :: MonadCont m => a -> m (a -> m b, a)
label a = callCC $ \k -> let go b = k (go, b) in return (go, a)

-- | Simplified version of `label` without arguments
-- | Simplified version of `label` without arguments.
--
-- @since 2.3.1
--
label_ :: MonadCont m => m (m a)
label_ = callCC $ return . fix

-- | Lift a 'ContT.callCC'-style function through any 'MonadTrans'.
--
-- = Note
--
-- For any function @f@, @'liftCallCC f'@ satisfies the [uniformity
-- condition](https://hackage.haskell.org/package/transformers-0.5.6.2/docs/Control-Monad-Signatures.html#t:CallCC)
-- provided that @f@ is quasi-algebraic. More specifically, for any @g@, we must have:
--
-- > 'join' '$' f (\exit -> 'pure' '$' g (exit '.' 'pure') = f g
--
-- 'ContT.callCC' is quasi-algebraic; furthermore, for any quasi-algebraic @f@,
-- @'liftCallCC' f@ is also quasi-algebraic.
--
-- = See also
--
-- * [Proof of quasi-algebraic
-- properties](https://gist.github.com/KingoftheHomeless/5927257cc7f6f8a2da685a2045dac204)
-- * [Original issue](https://github.com/haskell/mtl/issues/77)
--
-- @since 2.3.1
liftCallCC ::
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (a :: Type) (b :: Type) .
(MonadTrans t, Monad m, forall (m' :: Type -> Type) . Monad m' => Monad (t m')) =>
CallCC m (t m a) b -> CallCC (t m) a b
liftCallCC f g = join . lift . f $ \exit -> pure $ g (lift . exit . pure)

0 comments on commit 5a4c24d

Please sign in to comment.