我目前正在编写一个项目,我大量使用ListT
monad变换器。使用普通列表时,实现非确定性很容易。但是,一旦我必须将代码转换为ListT
,它就会变得更加复杂 1 。
举个简单的例子:从[a]
转换为ListT a
实际上需要组合两个函数:
conv :: (Monad m) => [a] -> ListT m a
conv = ListT . return
虽然很简单,但我很惊讶它还没有出现。
问题:
ListT
之间来回干净地进行转换?1 确切的原因非常复杂,所以我真的不想详细说明。
答案 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 有趣的是,我们可以定义一个概括conv
和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
这可能会使您的代码非常混乱,因此我不建议使用它:)
答案 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 a
和ListT :: m [a] -> ListT m a
,但OTOH我们有StateT :: (s -> m (a, s)) -> StateT s m a
。我不知道是否有办法解决这个问题 - 它肯定需要