【发布时间】:2019-12-28 02:25:27
【问题描述】:
我很难确定这是否是一个重复的问题,但找不到任何专门解决此问题的内容。如果真的有什么,我很抱歉。
所以,我了解了lift 的工作原理,它将一个单子动作(完全定义)从最外层的转换器提升到转换后的单子。很酷。
但是,如果我想将变压器下一层的(>>=) 应用到变压器中怎么办?我会用一个例子来解释。
说MyTrans是一个MonadTrans,还有一个实例Monad m => Monad (MyTrans m)。现在,来自该实例的 (>>=) 将具有以下签名:
instance Monad m => Monad (MyTrans m) where
(>>=) :: MyTrans m a -> (a -> MyTrans m b) -> MyTrans m b
但我需要的是这样的:
(>>=!) :: Monad m => MyTrans m a -> (m a -> MyTrans m b) -> MyTrans m b
一般:
(>>=!) :: (MonadTrans t, Monad m) => t m a -> (m a -> t m b) -> t m b
它看起来像是原始 (>>=) 和 lift 的组合,但实际上并非如此。 lift 只能用于m a 类型的协变参数,以将它们转换为t m a,而不是相反。换句话说,下面的类型是错误的:
(>>=!?) :: Monad m => MyTrans m a -> (a -> m b) -> MyTrans m b
x >>=!? f = x >>= (lift . f)
当然,一般的colift :: (MonadTrans t, Monad m) => t m a -> m a 绝对是零意义,因为肯定变压器正在做一些我们不能在所有情况下都像那样扔掉的东西。
但是就像(>>=) 通过确保它们总是“返回”来将逆变参数引入 monad 一样,我认为类似于 (>>=!) 函数的内容是有道理的:是的,它在某种程度上使m a 来自 t m a,但这仅仅是因为它在 t 内完成了所有这些,就像 (>>=) 在某种程度上从 m a 生成 a。
我已经考虑过了,我认为(>>=!) 通常不能从可用工具中定义。从某种意义上说,它比MonadTrans 提供的更多。我也没有找到任何提供此功能的相关类型类。 MFunctor 是相关的,但它是另一回事,用于更改内部 monad,但不适用于仅链接与转换器相关的操作。
顺便说一下,这里有一个示例说明您为什么要这样做:
编辑:我试图提出一个简单的例子,但我意识到可以使用变压器中的常规 (>>=) 来解决这个问题。我的真实例子(我认为)不能用这个来解决。如果您认为每个案例都可以使用通常的(>>=) 解决,请解释一下。
我应该为此定义自己的类型类并提供一些基本实现吗? (我对StateT 很感兴趣,而且我几乎可以肯定它可以实现)我在做一些扭曲的事情吗?有什么我忽略的吗?
谢谢。
编辑:Fyodor 提供的答案与类型匹配,但不符合我的要求,因为使用pure,它忽略了m monad 的单子效应。这是一个给出错误答案的例子:
采取t = StateT Int 和m = []。
x1 :: StateT Int [] Int
x1 = StateT (\s -> [(1,s),(2,s),(3,s)])
x2 :: StateT Int [] Int
x2 = StateT (\s -> [(1,s),(2,s),(3,s),(4,s))])
f :: [Int] -> StateT Int [] Int
f l = StateT (\s -> if (even s) then [] else (if (even (length l)) then (fmap (\z -> (z,z+s)) l) else [(123,123)]))
runStateT (x1 >>= (\a -> f (pure a))) 1 按预期返回[(123,123),(123,123),(123,123)],因为1 都是奇数,而x1 中的列表长度是奇数。
但是runStateT (x2 >>= (\a -> f (pure a))) 1 返回[(123,123),(123,123),(123,123),(123,123)],而我预计它会返回[(1,2),(2,3),(3,4),(4,5)],因为1 是奇数并且列表的长度是偶数。相反,由于 pure 调用,f 的评估正在列表 [(1,1)]、[(2,1)]、[(3,1)] 和 [(4,1)] 上独立进行。
【问题讨论】:
标签: haskell monads monad-transformers state-monad