Agda-2.4.2.3: src/full/Agda/Utils/ListT.hs
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE UndecidableInstances #-} -- Due to limitations of funct.dep.
-- | @ListT@ done right,
-- see https://www.haskell.org/haskellwiki/ListT_done_right_alternative
--
-- There is also the @list-t@ package on hackage (Nikita Volkov)
-- but it again depends on other packages we do not use yet,
-- so we rather implement the few bits we need afresh.
module Agda.Utils.ListT where
import Control.Applicative
import Control.Arrow
import Control.Monad
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Trans
import Data.Functor
import Data.Monoid
import Agda.Utils.Maybe
import Agda.Utils.Monad
-- | Lazy monadic computation of a list of results.
newtype ListT m a = ListT { runListT :: m (Maybe (a, ListT m a)) }
deriving (Functor)
-- * List operations
-- | The empty lazy list.
nilListT :: Monad m => ListT m a
nilListT = ListT $ return Nothing
-- | Consing a value to a lazy list.
consListT :: Monad m => a -> ListT m a -> ListT m a
consListT a l = ListT $ return $ Just (a, l)
-- | Singleton lazy list.
sgListT :: Monad m => a -> ListT m a
sgListT a = consListT a nilListT
-- | Case distinction over lazy list.
caseListT :: Monad m => ListT m a -> m b -> (a -> ListT m a -> m b) -> m b
caseListT l nil cons = caseMaybeM (runListT l) nil $ uncurry cons
-- | Folding a lazy list, effects left-to-right.
foldListT :: Monad m => (a -> m b -> m b) -> m b -> ListT m a -> m b
foldListT cons nil = loop where
loop l = caseListT l nil $ \ a l' -> cons a $ loop l'
-- | The join operation of the @ListT m@ monad.
concatListT :: Monad m => ListT m (ListT m a) -> ListT m a
concatListT = ListT . foldListT append (return Nothing)
where append l = runListT . mappend l . ListT
-- * Monadic list operations.
-- | We can ``run'' a computation of a 'ListT' as it is monadic itself.
runMListT :: Monad m => m (ListT m a) -> ListT m a
runMListT ml = ListT $ runListT =<< ml
-- | Monadic cons.
consMListT :: Monad m => m a -> ListT m a -> ListT m a
consMListT ma l = runMListT $ liftM (`consListT` l) ma
-- | Monadic singleton.
sgMListT :: Monad m => m a -> ListT m a
sgMListT ma = consMListT ma nilListT
-- Instances
instance Monad m => Monoid (ListT m a) where
mempty = nilListT
mappend l1 l2 = ListT $ foldListT cons (runListT l2) l1
where cons a = runListT . consListT a . ListT
instance (Functor m, Applicative m, Monad m) => Alternative (ListT m) where
empty = mempty
(<|>) = mappend
instance (Functor m, Applicative m, Monad m) => MonadPlus (ListT m) where
mzero = mempty
mplus = mappend
instance (Functor m, Applicative m, Monad m) => Applicative (ListT m) where
pure = return
(<*>) = ap
-- Another Applicative, but not the canonical one.
-- l1 <*> l2 = ListT $ loop <$> runListT l1 <*> runListT l2
-- where
-- loop (Just (f, l1')) (Just (a, l2')) = Just (f a, l1' <*> l2')
-- loop _ _ = Nothing
instance (Functor m, Applicative m, Monad m) => Monad (ListT m) where
return = sgListT
l >>= k = concatListT $ k <$> l
instance MonadTrans ListT where
lift = sgMListT
instance (Applicative m, MonadIO m) => MonadIO (ListT m) where
liftIO = lift . liftIO
instance (Applicative m, MonadReader r m) => MonadReader r (ListT m) where
ask = lift ask
local f = ListT . local f . runListT
instance (Applicative m, MonadState s m) => MonadState s (ListT m) where
get = lift get
put = lift . put