How to cleanly convert between lists and ListT monad transformers?

I am currently writing a project where I make a heavy use of ListT monad transformer. When using plain lists, implementing nondeterminism is very easy. However once I had to convert my code to ListT , it got much more complicated 1.

As a simple example: converting from [a] to ListT a actually requires composing two functions:

conv :: (Monad m) => [a] -> ListT m a
conv = ListT . return

Though it's simple, I am surprised it's not already there.

Questions:

  • Is there some better way to handle nondeterminism where a monad transformer is needed?
  • Are there any techniques / libraries for converting cleanly back and forth between lists and ListT ?

  • 1 The exact reasons are quite complicated, so I don't really want to elaborate too much on that.


    I don't think there are any libraries for this; conv is an incredibly simple function, after all, and the other way around is just runListT .

    conv is similar to the liftMaybe often desired when using MaybeT :

    liftMaybe :: (Monad m) => Maybe a -> MaybeT m a
    liftMaybe = MaybeT . return
    

    I would recommend naming it something along the lines of liftList .1

    As far as a better monad transformer for nondeterminism goes, I recommend taking a look at the logict package, based on Oleg's LogicT transformer, which is a continuation-based backtracking logic monad with some helpful operations. As a bonus, since [] is an instance of MonadLogic , those operations also work on lists.


    1 Interestingly, we can define a function that generalises the pattern of conv and liftMaybe :

    import Data.Foldable (Foldable)
    import qualified Data.Foldable as F
    
    choose :: (Foldable t, MonadPlus m) => t a -> m a
    choose = F.foldr (a b -> return a `mplus` b) mzero
    

    This will probably make your code quite confusing, so I don't recommend using it :)


    I just came across this question a few months later because I was wondering something similar to this. So I came up with the following:

    {-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
    
    import Control.Monad.Trans.Class
    import Control.Monad.Trans.Maybe
    import Control.Monad.Trans.List
    
    
    -- | Minimal implementation: either joinLift or joinT
    class (MonadTrans t, Monad m) => MonadTransJoin t m | m -> t, t -> m where
        joinLift :: (Monad m', Monad (t m')) => m' (m a) -> t m' a
        joinLift = joinT . lift
    
        joinT :: (Monad m', Monad (t m')) => t m' (m a) -> t m' a
        joinT = (>>= (joinLift . return))
    
    
    instance MonadTransJoin MaybeT Maybe where
        joinLift = MaybeT
        joinT = (>>= maybe mzero return)
    
    instance MonadTransJoin ListT [] where
        joinLift = ListT
        joinT = (>>= foldr mcons mzero)
            where mcons x xs = return x `mplus` xs
    

    So far so good—and my joinT method for the ListT / [] pair looks like it has something to do with ehird's choose .

    But the problem with this is that there is actually no uniform interface between a monad transformer and the monad whose behavior it endows to its base monad. We have MaybeT :: m (Maybe a) -> MaybeT ma and ListT :: m [a] -> ListT ma , but OTOH we have StateT :: (s -> m (a, s)) -> StateT sma . I don't know if there's a way to get around this—it certaindly requires

    链接地址: http://www.djcxy.com/p/77362.html

    上一篇: Monad变压器的解剖

    下一篇: 如何干净地转换列表和ListT monad变换器?