dunai-0.7.0: src/Control/Monad/Trans/MSF/Maybe.hs
{-# LANGUAGE Arrows #-}
{-# LANGUAGE Rank2Types #-}
{- |
The 'Maybe' monad is very versatile. It can stand for default arguments,
for absent values, and for (nondescript) exceptions.
The latter viewpoint is most natural in the context of 'MSF's.
-}
module Control.Monad.Trans.MSF.Maybe
( module Control.Monad.Trans.MSF.Maybe
, module Control.Monad.Trans.Maybe
, maybeToExceptS
) where
-- External
import Control.Monad.Trans.Maybe
hiding (liftCallCC, liftCatch, liftListen, liftPass) -- Avoid conflicting exports
-- Internal
import Control.Monad.Trans.MSF.Except
import Data.MonadicStreamFunction
-- * Throwing 'Nothing' as an exception ("exiting")
-- | Throw the exception immediately.
exit :: Monad m => MSF (MaybeT m) a b
exit = constM $ MaybeT $ return Nothing
-- | Throw the exception when the condition becomes true on the input.
exitWhen :: Monad m => (a -> Bool) -> MSF (MaybeT m) a a
exitWhen condition = proc a -> do
_ <- exitIf -< condition a
returnA -< a
-- | Exit when the incoming value is 'True'.
exitIf :: Monad m => MSF (MaybeT m) Bool ()
exitIf = proc condition -> if condition
then exit -< ()
else returnA -< ()
-- | @Just a@ is passed along, 'Nothing' causes the whole 'MSF' to exit.
maybeExit :: Monad m => MSF (MaybeT m) (Maybe a) a
maybeExit = inMaybeT
-- | Embed a 'Maybe' value in the 'MaybeT' layer. Identical to 'maybeExit'.
inMaybeT :: Monad m => MSF (MaybeT m) (Maybe a) a
inMaybeT = arrM $ MaybeT . return
-- * Catching Maybe exceptions
-- | Run the first @msf@ until the second one produces 'True' from the output of the first.
untilMaybe :: Monad m => MSF m a b -> MSF m b Bool -> MSF (MaybeT m) a b
untilMaybe msf cond = proc a -> do
b <- liftTransS msf -< a
c <- liftTransS cond -< b
inMaybeT -< if c then Nothing else Just b
-- | When an exception occurs in the first 'msf', the second 'msf' is executed from there.
catchMaybe
:: (Functor m, Monad m)
=> MSF (MaybeT m) a b -> MSF m a b -> MSF m a b
catchMaybe msf1 msf2 = safely $ do
_ <- try $ maybeToExceptS msf1
safe msf2
-- * Converting to and from 'MaybeT'
-- | Convert exceptions into `Nothing`, discarding the exception value.
exceptToMaybeS :: (Functor m, Monad m) => MSF (ExceptT e m) a b -> MSF (MaybeT m) a b
exceptToMaybeS = morphS $ MaybeT . fmap (either (const Nothing) Just) . runExceptT
-- | Converts a list to an 'MSF' in 'MaybeT',
-- which outputs an element of the list at each step,
-- throwing 'Nothing' when the list ends.
listToMaybeS :: (Functor m, Monad m) => [b] -> MSF (MaybeT m) a b
listToMaybeS = exceptToMaybeS . runMSFExcept . listToMSFExcept
-- * Running 'MaybeT'
-- | Remove the 'MaybeT' layer by outputting 'Nothing' when the exception occurs.
-- The continuation in which the exception occurred is then tested on the next input.
runMaybeS :: (Functor m, Monad m) => MSF (MaybeT m) a b -> MSF m a (Maybe b)
runMaybeS msf = exceptS (maybeToExceptS msf) >>> arr eitherToMaybe
where
eitherToMaybe (Left ()) = Nothing
eitherToMaybe (Right b) = Just b
-- | Reactimates an 'MSF' in the 'MaybeT' monad until it throws 'Nothing'.
reactimateMaybe
:: (Functor m, Monad m)
=> MSF (MaybeT m) () () -> m ()
reactimateMaybe msf = reactimateExcept $ try $ maybeToExceptS msf
-- | Run an 'MSF' fed from a list, discarding results. Useful when one needs to
-- combine effects and streams (i.e., for testing purposes).
embed_ :: (Functor m, Monad m) => MSF m a () -> [a] -> m ()
embed_ msf as = reactimateMaybe $ listToMaybeS as >>> liftTransS msf