如何干净地转换列表和ListT monad变换器?
我目前正在编写一个项目,我大量使用ListT
monad变压器。 使用普通列表时,实现非确定性非常容易。 但是,一旦我不得不将我的代码转换为ListT
,它就变得更加复杂1。
作为一个简单的例子:从[a]
转换到ListT a
实际上需要编写两个函数:
conv :: (Monad m) => [a] -> ListT m a
conv = ListT . return
虽然很简单,但我感到惊讶,它并不在那里。
问题:
ListT
之间来回转换? 1确切的原因相当复杂,所以我不想在此详细阐述。
我不认为有这样的图书馆; conv
是一个令人难以置信的简单函数,毕竟,其他方法只是runListT
。
conv
类似于liftMaybe
使用时通常需要MaybeT
:
liftMaybe :: (Monad m) => Maybe a -> MaybeT m a
liftMaybe = MaybeT . return
我建议将其命名为liftList
.1
就非确定性而言,一个更好的monad变换器,我建议看看基于Oleg的LogicT
变换器的逻辑程序包,它是一种基于延续的回溯逻辑单元,带有一些有用的操作。 作为奖励,由于[]
是MonadLogic
一个实例, MonadLogic
这些操作也可以在列表中使用。
1有趣的是,我们可以定义一个函数来概括conv
和liftMaybe
的模式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
这可能会让你的代码非常混乱,所以我不建议使用它:)
几个月后我刚刚发现了这个问题,因为我想知道类似这样的事情。 所以我想出了以下几点:
{-# 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
到目前为止这么好 - 我的ListT
/ []
对的joinT
方法看起来好像与ehird的choose
。
但是问题在于,monad变换器和monad的基本monad行为之间实际上没有统一的接口。 我们有MaybeT :: m (Maybe a) -> MaybeT ma
和ListT :: m [a] -> ListT ma
,但是OTOH我们有StateT :: (s -> m (a, s)) -> StateT sma
。 我不知道是否有办法解决这个问题 - 它肯定需要
上一篇: How to cleanly convert between lists and ListT monad transformers?