在许多情况下,我不清楚将两个monad与变压器组合而不是使用两个单独的monad可以获得什么 . 显然,使用两个独立的monad是一件麻烦事,并且可能涉及到符号内部的符号,但是有些情况下它只是表达不够吗?
一个案例似乎是列表上的StateT:组合monads不能得到正确的类型,如果你通过像Bar这样的monad栈获得正确的类型(其中Bar a =(Reader r(List(Writer w(Identity) a))),它没有做正确的事情 .
但是我想更准确地理解monad变压器带来什么,当它们是否必要时,以及为什么 .
为了使这个问题更加集中:
-
什么是没有相应变换器的monad的实际示例(这将有助于说明变换器可以做什么,只是堆叠monad不能) .
-
StateT和ContT是唯一的变换器,它们给出的类型与m的组成不等同于m,对于底层monad m(无论它们是由哪个顺序组成的 . )
(关于库的不同选择,我对特定的实现细节不感兴趣,而是对monad变换器/态射正在添加的一般问题(可能是Haskell独立)的问题,作为通过堆叠一堆monadic类型构造函数来组合效果的替代方法 . )
(为了给出一点背景,我是一个语言学家,正在做一个丰富蒙塔古语法的项目 - 简单地输入lambda演算,用于将单词意义组成句子 - 用monad变换器堆栈 . 理解变换器是否真的在做真的很有帮助对我有用的任何东西 . )
谢谢,
鲁本
2 回答
要回答关于
Writer w (Maybe a)
与MaybeT (Writer w) a
之间区别的问题,让我们首先看一下定义:使用
~~
表示"structurally similar to"我们有:所以在某种意义上你是正确的 - 结构上
Writer w (Maybe a)
和MaybeT (Writer w) a
都是相同的 - 两者基本上只是一对Maybe值和w
.不同之处在于我们如何将它们视为monadic值 .
return
和>>=
类函数的功能完全不同,具体取决于它们所属的monad .让我们考虑一对
(Just 3, []::[String])
. 使用我们在上面得到的关联是如何在两个monad中表示该对:以下是我们如何构建一对
(Nothing, [])
:现在考虑对这个函数:
让我们看看我们将如何在两个不同的monad中实现它:
通常,您会看到MaybeT monad中的代码更简洁 .
而且,在语义上两个单子是非常不同的......
MaybeT (Writer w) a
是一个可能失败的Writer操作,并且会自动为您处理失败 .Writer w (Maybe a)
只是一个返回Maybe的Writer动作 . 如果那个Maybe值变成Nothing,那么什么都不会发生 . 这在add1_W
函数中有所例证,我们必须在x
上执行案例分析 .更喜欢
MaybeT
方法的另一个原因是我们可以编写在任何monad堆栈上都是通用的代码 . 例如,功能:可以在任何具有Writer字符串的monad堆栈中保持不变,例如:
但是
square
的返回值不会对Writer String (Maybe Int)
进行类型检查,因为square
不会返回Maybe
.当您在
Writer String (Maybe Int)
中编码时,您的代码显式地显示了monad的结构,使其不那么通用 .add1_W
的定义:仅适用于双层monad堆栈,而像
square
这样的函数在更常规的设置中工作 .IO
和ST
是这里的规范示例 .不,ListT m a不是(同构)
[m a]
: