{-# OPTIONS_GHC -Wno-dodgy-imports #-} module ListT ( ListT (..), -- * Execution utilities uncons, head, tail, null, alternate, alternateHoisting, fold, foldMaybe, applyFoldM, toList, toReverseList, traverse_, splitAt, -- * Construction utilities cons, fromFoldable, fromMVar, unfold, unfoldM, repeat, -- * Transformation utilities -- | -- These utilities only accumulate the transformations -- without actually traversing the stream. -- They only get applied in a single traversal, -- which only happens at the execution. traverse, take, drop, slice, ) where import ListT.Prelude hiding (drop, fold, head, null, repeat, splitAt, tail, take, toList, traverse, traverse_, uncons, yield) -- | -- A proper implementation of the list monad-transformer. -- Useful for streaming of monadic data structures. -- -- Since it has instances of 'MonadPlus' and 'Alternative', -- you can use general utilities packages like -- -- with it. newtype ListT m a = ListT (m (Maybe (a, ListT m a))) deriving (Foldable, Traversable, Generic) deriving instance (Show (m (Maybe (a, ListT m a)))) => Show (ListT m a) deriving instance (Read (m (Maybe (a, ListT m a)))) => Read (ListT m a) deriving instance (Eq (m (Maybe (a, ListT m a)))) => Eq (ListT m a) deriving instance (Ord (m (Maybe (a, ListT m a)))) => Ord (ListT m a) deriving instance (Typeable m, Typeable a, Data (m (Maybe (a, ListT m a)))) => Data (ListT m a) instance (Eq1 m) => Eq1 (ListT m) where liftEq eq = go where go (ListT m) (ListT n) = liftEq (liftEq (\(a, as) (b, bs) -> eq a b && go as bs)) m n instance (Ord1 m) => Ord1 (ListT m) where liftCompare cmp = go where go (ListT m) (ListT n) = liftCompare (liftCompare (\(a, as) (b, bs) -> cmp a b <> go as bs)) m n instance (Show1 m) => Show1 (ListT m) where -- I wish I were joking. liftShowsPrec sp (sl :: [a] -> ShowS) = mark where bob :: Int -> m (Maybe (a, ListT m a)) -> ShowS bob = liftShowsPrec jill edith edith :: [Maybe (a, ListT m a)] -> ShowS edith = liftShowList jack martha jill :: Int -> Maybe (a, ListT m a) -> ShowS jill = liftShowsPrec jack martha martha :: [(a, ListT m a)] -> ShowS martha = liftShowList2 sp sl mark juan mark :: Int -> ListT m a -> ShowS mark d (ListT m) = showsUnaryWith bob "ListT" d m juan :: [ListT m a] -> ShowS juan = liftShowList sp sl jack :: Int -> (a, ListT m a) -> ShowS jack = liftShowsPrec2 sp sl mark juan instance (Monad m) => Semigroup (ListT m a) where (<>) (ListT m1) (ListT m2) = ListT $ m1 >>= \case Nothing -> m2 Just (h1, s1') -> return (Just (h1, ((<>) s1' (ListT m2)))) instance (Monad m) => Monoid (ListT m a) where mempty = ListT $ return Nothing mappend = (<>) instance (Functor m) => Functor (ListT m) where fmap f = go where go = ListT . (fmap . fmap) (bimapPair' f go) . uncons instance (Monad m, Functor m) => Applicative (ListT m) where pure a = ListT $ return (Just (a, (ListT (return Nothing)))) (<*>) = ap -- This is just like liftM2, but it uses fmap over the second -- action. liftM2 can't do that, because it has to deal with -- the possibility that someone defines liftA2 = liftM2 and -- fmap f = (pure f <*>) (leaving (<*>) to the default). liftA2 f m1 m2 = do x1 <- m1 fmap (f x1) m2 instance (Monad m, Functor m) => Alternative (ListT m) where empty = inline mempty (<|>) = inline mappend instance (Monad m) => Monad (ListT m) where return = pure -- We use a go function so GHC can inline k2 -- if it likes. (>>=) s10 k2 = go s10 where go s1 = ListT $ uncons s1 >>= \case Nothing -> return Nothing Just (h1, t1) -> uncons $ k2 h1 <> go t1 instance (Monad m) => MonadFail (ListT m) where fail _ = inline mempty instance (Monad m) => MonadPlus (ListT m) where mzero = inline mempty mplus = inline mappend instance MonadTrans ListT where lift = ListT . fmap (\a -> Just (a, mempty)) instance (MonadIO m) => MonadIO (ListT m) where liftIO = lift . liftIO instance MFunctor ListT where hoist f = go where go (ListT run) = ListT . f $ run <&> \case Just (elem, next) -> Just (elem, go next) Nothing -> Nothing instance MMonad ListT where embed f (ListT m) = f m >>= \case Nothing -> mzero Just (h, t) -> ListT $ return $ Just $ (h, embed f t) instance (MonadBase b m) => MonadBase b (ListT m) where liftBase = lift . liftBase instance (MonadBaseControl b m) => MonadBaseControl b (ListT m) where type StM (ListT m) a = StM m (Maybe (a, ListT m a)) liftBaseWith runToBase = lift $ liftBaseWith $ \runInner -> runToBase $ runInner . uncons restoreM inner = lift (restoreM inner) >>= \case Nothing -> mzero Just (h, t) -> cons h t instance (MonadError e m) => MonadError e (ListT m) where throwError = ListT . throwError catchError m handler = ListT $ catchError (uncons m) $ uncons . handler instance (MonadReader e m) => MonadReader e (ListT m) where ask = lift ask reader = lift . reader local r = go where go (ListT m) = ListT $ local r (fmap (fmap (secondPair' go)) m) instance (MonadState e m) => MonadState e (ListT m) where get = lift get put = lift . put state = lift . state instance (Monad m) => MonadLogic (ListT m) where msplit (ListT m) = lift m interleave m1 m2 = ListT $ uncons m1 >>= \case Nothing -> uncons m2 Just (a, m1') -> uncons $ cons a (interleave m2 m1') m >>- f = ListT $ uncons m >>= \case Nothing -> uncons empty Just (a, m') -> uncons $ interleave (f a) (m' >>- f) ifte t th el = ListT $ uncons t >>= \case Nothing -> uncons el Just (a, m) -> uncons $ th a <|> (m >>= th) once (ListT m) = ListT $ m >>= \case Nothing -> uncons empty Just (a, _) -> uncons (return a) lnot (ListT m) = ListT $ m >>= \case Nothing -> uncons (return ()) Just _ -> uncons empty instance (MonadZip m) => MonadZip (ListT m) where mzipWith f = go where go (ListT m1) (ListT m2) = ListT $ mzipWith ( mzipWith $ \(a, as) (b, bs) -> (f a b, go as bs) ) m1 m2 munzip (ListT m) | (l, r) <- munzip (fmap go m) = (ListT l, ListT r) where go Nothing = (Nothing, Nothing) go (Just ((a, b), listab)) = (Just (a, la), Just (b, lb)) where -- If the underlying munzip is careful not to leak memory, then we -- don't want to defeat it. We need to be sure that la and lb are -- realized as selector thunks. {-# NOINLINE remains #-} {-# NOINLINE la #-} {-# NOINLINE lb #-} remains = munzip listab (la, lb) = remains -- * Execution in the inner monad ------------------------- -- | -- Execute in the inner monad, -- getting the head and the tail. -- Returns nothing if it's empty. uncons :: ListT m a -> m (Maybe (a, ListT m a)) uncons (ListT m) = m -- | -- Execute, getting the head. Returns nothing if it's empty. {-# INLINEABLE head #-} head :: (Monad m) => ListT m a -> m (Maybe a) head = fmap (fmap fst) . uncons -- | -- Execute, getting the tail. Returns nothing if it's empty. {-# INLINEABLE tail #-} tail :: (Monad m) => ListT m a -> m (Maybe (ListT m a)) tail = fmap (fmap snd) . uncons -- | -- Execute, checking whether it's empty. {-# INLINEABLE null #-} null :: (Monad m) => ListT m a -> m Bool null = fmap (maybe True (const False)) . uncons -- | -- Execute in the inner monad, -- using its '(<|>)' function on each entry. {-# INLINEABLE alternate #-} alternate :: (Alternative m, Monad m) => ListT m a -> m a alternate (ListT m) = m >>= \case Nothing -> empty Just (a, as) -> pure a <|> alternate as -- | -- Use a monad morphism to convert a 'ListT' to a similar -- monad, such as '[]'. -- -- A more efficient alternative to @'alternate' . 'hoist' f@. {-# INLINEABLE alternateHoisting #-} alternateHoisting :: (Monad n, Alternative n) => (forall a. m a -> n a) -> ListT m a -> n a alternateHoisting f = go where go (ListT m) = f m >>= \case Nothing -> empty Just (a, as) -> pure a <|> go as -- | -- Execute, applying a strict left fold. {-# INLINEABLE fold #-} fold :: (Monad m) => (b -> a -> m b) -> b -> ListT m a -> m b fold step = go where go !acc (ListT run) = run >>= \case Just (element, next) -> do acc' <- step acc element go acc' next Nothing -> return acc -- | -- A version of 'fold', which allows early termination. {-# INLINEABLE foldMaybe #-} foldMaybe :: (Monad m) => (b -> a -> m (Maybe b)) -> b -> ListT m a -> m b foldMaybe s r l = fmap (maybe r id) $ runMaybeT $ do (h, t) <- MaybeT $ uncons l r' <- MaybeT $ s r h lift $ foldMaybe s r' t -- | -- Apply the left fold abstraction from the \"foldl\" package. applyFoldM :: (Monad m) => FoldM m i o -> ListT m i -> m o applyFoldM (FoldM step init extract) lt = do a <- init b <- fold step a lt extract b -- | -- Execute, folding to a list. {-# INLINEABLE toList #-} toList :: (Monad m) => ListT m a -> m [a] toList = fmap reverse . toReverseList -- | -- Execute, folding to a list in the reverse order. -- Performs more efficiently than 'toList'. {-# INLINEABLE toReverseList #-} toReverseList :: (Monad m) => ListT m a -> m [a] toReverseList = fold (\list element -> return (element : list)) [] -- | -- Execute, traversing the stream with a side effect in the inner monad. {-# INLINEABLE traverse_ #-} traverse_ :: (Monad m) => (a -> m ()) -> ListT m a -> m () traverse_ f = fold (const f) () -- | -- Execute, consuming a list of the specified length and returning the remainder stream. {-# INLINEABLE splitAt #-} splitAt :: (Monad m) => Int -> ListT m a -> m ([a], ListT m a) splitAt = \case n | n > 0 -> \l -> uncons l >>= \case Nothing -> return ([], mzero) Just (h, t) -> do (r1, r2) <- splitAt (pred n) t return (h : r1, r2) _ -> \l -> return ([], l) -- * Construction ------------------------- -- | -- Prepend an element. cons :: (Monad m) => a -> ListT m a -> ListT m a cons h t = ListT $ return (Just (h, t)) -- | -- Construct from any foldable. {-# INLINEABLE fromFoldable #-} fromFoldable :: (Monad m, Foldable f) => f a -> ListT m a fromFoldable = foldr cons mzero -- | -- Construct from an MVar, interpreting the value of Nothing as the end. fromMVar :: (MonadIO m) => MVar (Maybe a) -> ListT m a fromMVar v = fix $ \loop -> liftIO (takeMVar v) >>= maybe mzero (flip cons loop) -- | -- Construct by unfolding a pure data structure. {-# INLINEABLE unfold #-} unfold :: (Monad m) => (b -> Maybe (a, b)) -> b -> ListT m a unfold f s = maybe mzero (\(h, t) -> cons h (unfold f t)) (f s) -- | -- Construct by unfolding a monadic data structure -- -- This is the most memory-efficient way to construct ListT where -- the length depends on the inner monad. {-# INLINEABLE unfoldM #-} unfoldM :: (Monad m) => (b -> m (Maybe (a, b))) -> b -> ListT m a unfoldM f = go where go s = ListT $ f s >>= \case Nothing -> return Nothing Just (a, r) -> return (Just (a, go r)) -- | -- Produce an infinite stream. {-# INLINEABLE repeat #-} repeat :: (Monad m) => a -> ListT m a repeat = fix . cons -- * Transformation ------------------------- -- | -- A transformation, -- which traverses the stream with an action in the inner monad. {-# INLINEABLE traverse #-} traverse :: (Monad m) => (a -> m b) -> ListT m a -> ListT m b traverse f = go where go (ListT run) = ListT $ run >>= \case Nothing -> return Nothing Just (a, next) -> f a <&> \b -> Just (b, go next) -- | -- A transformation, -- reproducing the behaviour of @Data.List.'Data.List.take'@. {-# INLINEABLE take #-} take :: (Monad m) => Int -> ListT m a -> ListT m a take = \case n | n > 0 -> \t -> lift (uncons t) >>= \case Nothing -> t Just (h, t) -> cons h (take (pred n) t) _ -> const $ mzero -- | -- A transformation, -- reproducing the behaviour of @Data.List.'Data.List.drop'@. {-# INLINEABLE drop #-} drop :: (Monad m) => Int -> ListT m a -> ListT m a drop = \case n | n > 0 -> lift . uncons >=> maybe mzero (drop (pred n) . snd) _ -> id -- | -- A transformation, -- which slices a list into chunks of the specified length. {-# INLINEABLE slice #-} slice :: (Monad m) => Int -> ListT m a -> ListT m [a] slice n l = do (h, t) <- lift $ splitAt n l case h of [] -> mzero _ -> cons h (slice n t)