如何在列表和ListT monad变换器之间进行干净的转换?

时间:2012-02-09 16:33:02

标签: list haskell monads monad-transformers non-deterministic

我目前正在编写一个项目,我大量使用ListT monad变换器。使用普通列表时,实现非确定性很容易。但是,一旦我必须将代码转换为ListT,它就会变得更加复杂 1

举个简单的例子:从[a]转换为ListT a实际上需要组合两个函数:

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

虽然很简单,但我很惊讶它还没有出现。

问题:

  • 是否有更好的方法来处理需要monad变换器的非确定性?
  • 是否有任何技术/库可以在列表与ListT之间来回干净地进行转换?

1 确切的原因非常复杂,所以我真的不想详细说明。

2 个答案:

答案 0 :(得分:6)

我认为没有任何图书馆;毕竟,conv是一个非常简单的功能,而另一种方式只是runListT

conv与使用liftMaybe时经常需要的MaybeT类似:

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

我建议将其命名为liftList 1

至于一个更好的monad变换器用于非确定性,我建议看看logict包,基于Oleg的LogicT变换器,这是一个基于延续的回溯逻辑单元{{ 3}}。作为奖励,由于[]MonadLogic的实例,因此这些操作也适用于列表。


1 有趣的是,我们可以定义一个概括convliftMaybe模式的函数:

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

这可能会使您的代码非常混乱,因此我不建议使用它:)

答案 1 :(得分:0)

几个月后我才刚刚遇到这个问题,因为我想知道类似的事情。所以我想出了以下内容:

{-# 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

到目前为止一直很好 - joinT / ListT对的[]方法看起来与ehird的choose有关。

但问题是,monad变换器和monad之间实际上没有统一的接口,monad的行为赋予它的基本monad。我们有MaybeT :: m (Maybe a) -> MaybeT m aListT :: m [a] -> ListT m a,但OTOH我们有StateT :: (s -> m (a, s)) -> StateT s m a。我不知道是否有办法解决这个问题 - 它肯定需要