in-other-words-0.2.0.0: src/Control/Monad/Trans/List/Church.hs
module Control.Monad.Trans.List.Church where
import Control.Applicative
import Control.Monad
import Control.Monad.Base
import Control.Monad.Trans
import qualified Control.Monad.Catch as C
import qualified Control.Monad.Fail as Fail
import Control.Effect.Carrier
import Control.Effect.Type.ListenPrim
import Control.Effect.Type.WriterPrim
import Control.Effect.Type.Regional
import Control.Effect.Type.Optional
import Control.Effect.Type.Unravel
import Control.Effect.Type.ReaderPrim
newtype ListT (m :: * -> *) a = ListT {
unListT :: forall r
. (a -> m r -> m r)
-> m r -- lose
-> m r -- cutfail
-> m r
}
cons :: a -> ListT m a -> ListT m a
cons a m = ListT $ \c b t -> c a (unListT m c b t)
instance ThreadsEff ListT (Regional s) where
threadEff = threadRegionalViaOptional
instance Functor s => ThreadsEff ListT (Optional s) where
threadEff alg (Optionally s main) = ListT $ \c b t ->
let
n = \m -> alg (Optionally (fmap (`c` b) s) m)
in
join $ n
$ unListT main
(\a r -> return $ c a (join (n r)))
(return b)
(return t)
{-# INLINE threadEff #-}
instance ThreadsEff ListT (Unravel p) where
threadEff alg (Unravel p cataM main) =
let
n = \m' -> alg (Unravel p (cataM . lift) m')
in
lift $ n
$ unListT main
(\a r -> return $ cataM $ cons a (lift (n r)))
(return $ cataM lose)
(return $ cataM cutfail)
{-# INLINE threadEff #-}
instance Monoid s => ThreadsEff ListT (ListenPrim s) where
threadEff = threadListenPrim $ \alg main ->
let
listenStack m = LayeredListT $ do
(s, e) <- alg (ListenPrimListen (unLayeredListT m))
case e of
Right (a, r) -> return $ Right ((s, a), listenStack r)
Left failure -> return $ Left failure
in
(fromLayeredListT . listenStack . toLayeredListT) $ main
{-# INLINE threadEff #-}
instance Monoid s => ThreadsEff ListT (WriterPrim s) where
threadEff = threadWriterPrim $ \alg main ->
let
passStack m = LayeredListT $ alg $ WriterPrimPass $
unLayeredListT m >>= \case
Right ((f, a), r) -> return $ (f, Right (a, passStack r))
Left e -> return $ (id, Left e)
in
(fromLayeredListT . passStack . toLayeredListT) $ main
{-# INLINE threadEff #-}
instance ThreadsEff ListT (ReaderPrim i) where
threadEff = threadReaderPrimViaRegional
{-# INLINE threadEff #-}
instance MonadBase b m => MonadBase b (ListT m) where
liftBase = lift . liftBase
{-# INLINE liftBase #-}
instance Fail.MonadFail m => Fail.MonadFail (ListT m) where
fail = lift . Fail.fail
{-# INLINE fail #-}
instance MonadThrow m => MonadThrow (ListT m) where
throwM = lift . C.throwM
{-# INLINE throwM #-}
instance MonadCatch m => MonadCatch (ListT m) where
catch main h = ListT $ \c b t ->
let
n = (`C.catch` \e -> return $ unListT (h e) c b t)
in
join $ n
$ unListT main
(\a r -> return $ c a (join (n r)))
(return b)
(return t)
{-# INLINE catch #-}
cull :: ListT m a -> ListT m a
cull m = ListT $ \c b t -> unListT m (\a _ -> c a b) b t
choose :: ListT m a -> ListT m a -> ListT m a
choose ma mb = ListT $ \c b t -> unListT ma c (unListT mb c b t) t
lose :: ListT m a
lose = ListT $ \_ b _ -> b
cutfail :: ListT m a
cutfail = ListT $ \_ _ t -> t
call :: ListT m a -> ListT m a
call m = ListT $ \c b _ -> unListT m c b b
data LoseOrCutfail
= Lost
| Cutfailed
newtype LayeredListT m a =
LayeredListT {
unLayeredListT :: m (Either LoseOrCutfail (a, LayeredListT m a))
}
toLayeredListT :: Monad m => ListT m a -> LayeredListT m a
toLayeredListT main =
LayeredListT
$ unListT main
(\a mr -> return $ Right $ (a, LayeredListT mr))
(return $ Left Lost)
(return $ Left Cutfailed)
split' :: Monad m => LayeredListT m a -> LayeredListT m (Maybe (a, LayeredListT m a))
split' m = LayeredListT $ unLayeredListT m >>= \case
Right (a, r) -> return $ Right (Just (a, r), LayeredListT $ return $ Left Lost)
Left Lost -> return $ Right (Nothing, LayeredListT $ return $ Left Lost)
Left Cutfailed -> return $ Left Cutfailed
fromLayeredListT :: Monad m => LayeredListT m a -> ListT m a
fromLayeredListT main = ListT $ \c b t ->
let
go m = unLayeredListT m >>= \case
Left Lost -> b
Left Cutfailed -> t
Right (a, r) -> c a (go r)
in
go main
-- split cutfail === cutfail
-- If you don't want that behaviour, instead of @split m@, do @split (call m)@
split :: Monad m => ListT m a -> ListT m (Maybe (a, ListT m a))
split =
(fmap . fmap . fmap) fromLayeredListT
. fromLayeredListT
. split'
. toLayeredListT
{-# INLINE split #-}
instance Functor (ListT m) where
fmap f m = ListT $ \c b t ->
unListT m (c . f) b t
{-# INLINE fmap #-}
instance Applicative (ListT m) where
pure a = ListT $ \c b _ -> c a b
liftA2 f fa fb = ListT $ \c b t ->
unListT fa (\a r -> unListT fb (c . f a) r t) b t
{-# INLINE liftA2 #-}
ma *> mb = ma >>= \_ -> mb
{-# INLINE (*>) #-}
instance Monad (ListT m) where
m >>= f = ListT $ \c b t ->
unListT m (\a r -> unListT (f a) c r t) b t
{-# INLINE (>>=) #-}
instance MonadTrans ListT where
lift m = ListT $ \c b _ -> m >>= (`c` b)
{-# INLINE lift #-}
instance MonadIO m => MonadIO (ListT m) where
liftIO = lift . liftIO
{-# INLINE liftIO #-}
runListT :: (Alternative f, Monad m)
=> ListT m a
-> m (f a)
runListT m =
unListT m (fmap . (<|>) . pure) (pure empty) (pure empty)
{-# INLINE runListT #-}