{-|
Module      : Control.Monad.MaybeContT
Description : The 'MaybeCont' type and API
Copyright   : (c) Eitan Chatav, 2015
License     : PublicDomain
Maintainer  : eitan.chatav@gmail.com
Stability   : experimental

The 'MaybeCont' type and API provide an idiomatic way to handle possibly
failing computations in continuation passing style.
-}

module Control.Monad.MaybeCont
    ( MaybeCont
    , MaybeContT()
    , maybeCont
    , runMaybeCont
    , liftMaybe
    , nothingC
    , mapMaybeCont
    , withMaybeContJust
    , withMaybeContNothing
    ) where

import Control.Monad.Trans.MaybeCont
import Data.Functor.Identity

-- |'MaybeCont' 'a' 'r' is a CPS computation that produces an intermediate
-- result of type 'a' within a CPS computation which produces either a just
-- or nothing.
type MaybeCont a r = MaybeContT a Identity r

-- |Construct a continuation-passing computation from a function.
maybeCont :: (a -> (r -> a) -> a) -> MaybeCont a r
maybeCont mc = MaybeContT $ \ma k -> Identity $
    mc (runIdentity ma) (runIdentity . k)

-- |The result of running a CPS computation with given nothing and just
-- continuations.
--
-- prop> runMaybeCont . maybeCont = id
-- prop> maybeCont . runMaybeCont = id
runMaybeCont :: MaybeCont a r -> (a -> (r -> a) -> a)
runMaybeCont mc a k = runIdentity $
    runMaybeContT mc (Identity a) (Identity . k)

-- |'liftMaybe' embeds 'Maybe' in 'MaybeCont' 'a'.
liftMaybe :: Maybe r -> MaybeCont a r
liftMaybe may = maybeCont $ \a k -> maybe a k may

-- |Apply a function to transform the result of a continuation-passing
-- computation.
mapMaybeCont :: (a -> a) -> MaybeCont a r -> MaybeCont a r
mapMaybeCont = mapMaybeContT . fmap

-- |Apply a function to transform the just continuation passed to a
-- continuation-passing computation.
withMaybeContJust :: ((r' -> a) -> r -> a)
               -> MaybeCont a r
               -> MaybeCont a r'
withMaybeContJust f = withMaybeContTJust ((Identity .) . f . (runIdentity .))

-- |Apply a function to transform the nothing continuation passed to an
-- continuation-passing computation.
withMaybeContNothing :: (a -> a) -> MaybeCont a r -> MaybeCont a r
withMaybeContNothing = withMaybeContTNothing . fmap