澄清问题:它是关于monad类型类的优点(而不是它没有统一类的实例) .
在阅读了很多参考文献(见下文)之后,我得出的结论是,实际上, monad class 在那里解决了 only ,但是 big and crucial ,问题: the 'chaining' of functions on types with context . 因此,着名的句子"monads are programmable semicolons" . 事实上,monad可以被视为an array of functions with helper operations .
我坚持monad class 之间的区别,被理解为其他类型的通用接口;以及这些实例化类的其他类型(因此,"monadic types") .
我理解monad类本身只解决了运算符的链接,因为主要是它只要求它的类型实例有 bind >>=
和 return
,并告诉我们它们必须如何表现 . 作为奖励,编译器非常有助于编码为monadic类型提供 do
表示法 .
另一方面,它是 each 个体类型实例化monad类,它解决了 each concrete problem , but not merely for being a instance of Monad . 例如 Maybe
求解"how a function returns a value or an error", State
求解"how to have functions with global state", IO
求解"how to interact with the outside world",依此类推 . 所有这些类都在上下文中封装了一个值 .
但不久之后,我们需要对这些上下文类型进行连锁操作 . 即,我们需要以特定的顺序组织对这些类型的函数的调用(对于这样的问题的示例,请阅读You could have invented monads中关于多值函数的示例) .
如果每个类型都是monad类的实例,那么你就解决了链接的问题 . 要使链接起作用,你需要 >>=
只需要具有它的确切签名,而不是其他 . (见this question) .
因此,我猜你下次定义一个上下文数据类型T来解决问题时,如果你需要对函数的调用进行排序(在T的值上),可以考虑将T作为 Monad
的实例(如果你需要"chaining with choice",如果你可以受益来自 do
符号) . 为了确保你做得对,检查T是否满足monad laws
Then, I ask two questions to the Haskell experts:
-
A concrete question: is there any other problem that the monad class solves by ifself (leaving apart monadic classes)? If any, then, how it compares in relevance to the problem of chaining operations?
-
一个可选的一般问题:我的结论是对的,我是否误解了什么?
参考文献
教程
-
Monads in pictures绝对物有所值;先读一下 .
-
Monads are trees(pdf)
2 回答
你're definitely on to something in the way that you'重申这一点 - 有很多事情是
Monad
的意思,而且你把它们分开了 .也就是说,我肯定会说链接操作不是Monads解决的主要问题 . 这可以使用简单的Functors(有一些麻烦)或使用Applicatives轻松解决 . You need to use the full monad spec when "chaining with choice". 特别是,
Applicative
和Monad
之间的张力来自Applicative
需要静态地知道副作用计算的整个结构 .Monad
可以在运行时更改该结构,从而牺牲了一些功率的可分析性 .为了更清楚地说明这一点, the only place you deal with a Monad but not any specific monad is if you're defining something with polymorphism constrained to be a Monad . 这在
Control.Monad
模块中反复显示,因此我们可以从中检查一些示例 .我们可以立即抛弃
sequence
作为特定的Monad
,因为Data.Traversable
中有相应的函数,sequenceA
,其类型略大于Applicative f => [f a] -> f [a]
. 这应该是一个明确的指标 Monad isn't the only way to sequence things .同样,我们可以如下定义
foreverA
因此有更多的方法来排序非
Monad
类型 . 但我们遇到麻烦foldM
如果我们尝试将此定义翻译为
Applicative
样式,我们可能会写但是Haskell肯定会抱怨这不是类型检查 - 每次递归调用
foldA
都试图在结果上放置另一个"layer"的f
. 使用Monad
,我们可以join
这些图层向下,但Applicative
太弱了 .那么这又如何转化为限制我们运行时选择的
Applicative
?嗯,这正是我们用foldM
表达的,一个monadic计算(a -> b -> m a)
,它依赖于它的a
参数,这是先前monadic计算的结果 . 这种事情在Applicative
的更纯粹的连续世界中根本没有任何意义 .为了解决在单个monadic类型上链接操作的问题,根本不需要使它成为
Monad
的实例 . 并确保monad法律得到满足 . 您可以直接在您的类型上实现链接操作 .它可能与monadic绑定非常相似,但不一定完全相同(回想一下,列表绑定是
concatMap
,无论如何都存在的函数,但参数的顺序不同) . 你不会有任何共同的要求 .要询问
Monad
类类本身解决了什么问题,请查看处理任何monadic类型的值的所有函数(在Control.Monad
和其他位置) . 解决的问题是代码重用!Monad
正是所有monadic类型的一部分,每个monadic类型对于它们中的每一个都是通用的 . 该部分足以编写有用的计算 . 所有这些函数都可以针对任何单个monadic类型实现(通常更直接),但它们已经针对所有monadic类型实现,即使是那些尚不存在的类型 .您没有编写
Monad
实例,因此您可以对类型进行链接操作(实际上您通常已经有了链接方式) . 您为Monad
实例自动附带的所有代码编写Monad
实例 .Monad
不是为任何单一类型解决任何问题,它是一种将许多不同类型视为单一统一概念的实例 .