“链接操作”是“仅限” Monad类解决的事情? [英] Is 'Chaining operations' the "only" thing that the Monad class solves?

查看:92
本文介绍了“链接操作”是“仅限” Monad类解决的事情?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了澄清这个问题:关于monad类型的优点(与没有统一类的情况相反)。



在阅读了许多参考文献(见下文)后,
得出结论,实际上, monad class 仅用于解决一个大而关键,问题:带有上下文的类型的函数'链接'。因此,着名的句子monads是可编程的分号。
实际上,monad可以被看作是带有帮助器操作的一系列函数



我坚持monad class 之间的区别,理解为其他类型的通用接口;和其他类型实例化类(因此,monadic类型)。

我明白monad类本身只解决运算符的链接,因为主要是它只强制其类型实例
具有绑定>> = 返回,并告诉我们他们必须表现出来。作为奖励,编译器很好地帮助为一元类型提供 do 标记的编码。

另一方面, ,
每个个体类型实例化monad类,它解决了每个具体问题,但不仅仅是作为Monad的一个实例 。例如 Maybe 解决了函数如何返回值或错误, State 解决了如何使用函数全局状态, IO 解决了如何与外界交互等等。所有这些类都在上下文中封装了一个值。



但是不久或将来,我们需要对这种上下文类型进行链接操作。也就是说,我们需要按照特定的顺序对这些类型的函数进行调用(有关这种问题的示例,请阅读你可以发明单子)。如果你将每种类型都作为monad类的一个实例,你就可以解决链接问题。
对于链接工作,您需要>> = ,只需使用它的确切签名即可。 (请参阅此问题)。

因此,我想下一次你定义一个上下文数据类型T来解决某些问题时,如果你需要对函数的调用进行排序(在T的值上),可以考虑使T成为 Monad (如果您需要选择链接,并且您可以从 do >符号获益)。为了确保你做得对,请检查T是否符合 monad laws



然后,我向Haskell的专家提出两个问题:
$ b


  1. 一个具体的问题:monad类是否还有其他的问题要解决(分开monadic类)?如果有的话,它是如何与链接操作相关的问题进行比较的?
  2. 可选的一般问题:我的结论是否正确,我误解了什么? >



参考资料



教程





StackOverflow问题与放大器解答




解决方案

肯定是在说明这一点 - 有许多事情, Monad 意味着你将它们分开。



也就是说,我肯定会说,链接操作并不是Monad解决的主要问题。这可以使用简单的Functors来解决(有些麻烦)或者很容易使用Applicatives。 您需要在选择链接时使用完整的monad规范。特别是 Applicative Monad之间的张力来自 Applicative 需要静态地了解副作用计算的整个结构。 Monad 可以在运行时改变结构,从而牺牲一些可分析性的功能。





$ b $为了说明这一点,你处理Monad的唯一地方,但不是任何特定的monad是,如果你定义了多态性约束为Monad 的东西, STRONG>。这在 Control.Monad 模块中反复出现,因此我们可以从中检查一些例子。

  sequence:[ma]  - > m [a] 
永远:: m a - > m b
foldM ::(a - > b - > m a) - > a - > [b] - > ma

立即,我们可以抛出序列特别是 Monad ,因为 Data.Traversable sequenceA code>,它的类型比适用f => [f a] - > f [a] 。这应该是一个明确的指标,即 Monad 不是唯一排序顺序的方法。



同样,我们可以定义 foreverA 如下

  foreverA ::适用的f => f a  - > f b 
foreverA f =翻转常量< $> f * foreverA f

所以有更多的方法来排序非 - Monad 类型。但是我们碰到了 foldM

  foldM ::(Monad m )=> (a  - > b  - > m a) - > a  - > [b]  - > m a 
foldM _ a [] =返回a
foldM f a(x:xs)= f a x>> = \ fax - >如果我们试图将这个定义转换为 Applicative code> style我们可以写出

  foldA ::(Applicative f)=> (a  - > b  - > f a) - > a  - > [b]  - > f a 
foldA _ a [] =纯a
foldA f a(x:xs)= foldA f< $> f a x * xs

但是Haskell会正确地抱怨这不会检查 - 每次递归调用 foldA 会尝试在结果中放置另一个 f 的图层。使用 Monad 我们可以加入这些图层,但 Applicative






那么这怎么转化为 Applicative 是否限制了我们的运行时选择?那么,这正是我们用 foldM 表示的一元计算(a - > b - > ma)这取决于它的 a 参数,这是先前单子计算的结果。这种情况在 Applicative 这个更纯粹的顺序世界中根本没有任何意义。


To clarify the question: it is about the merits of the monad type class (as opposed to just its instances without the unifying class).

After having read many references (see below), I came to the conclusion that, actually, the monad class is there to solve only one, but big and crucial, problem: the 'chaining' of functions on types with context. Hence, the famous sentence "monads are programmable semicolons". In fact, a monad can be viewed as an array of functions with helper operations.

I insist on the difference between the monad class, understood as a general interface for other types; and these other types instantiating the class (thus, "monadic types").

I understand that the monad class by itself, only solves the chaining of operators because mainly, it only mandates its type instances to have bind >>= and return, and tell us how they must behave. And as a bonus, the compiler greatyly helps the coding providing do notation for monadic types.

On the other hand, it is each individual type instantiating the monad class which solves each concrete problem, but not merely for being a instance of Monad. For instance Maybe solves "how a function returns a value or an error", State solves "how to have functions with global state", IO solves "how to interact with the outside world", and so on. All theses classes encapsulate a value within a context.

But soon or later, we will need to chain operations on such context-types. I.e., we will need to organize calls to functions on these types in a particular sequence (for an example of such a problem, please read the example about multivalued functions in You could have invented monads).

And you get solved the problem of chaining, if you have each type be an instance of the monad class. For the chaining to work you need >>= just with the exact signature it has, no other. (See this question).

Therefore, I guess that the next time you define a context data type T for solving something, if you need to sequence calls of functions (on values of T) consider making T an instance of Monad (if you need "chaining with choice" and if you can benefit from the do notation). And to make sure you are doing it right, check that T satisfies the monad laws

Then, I ask two questions to the Haskell experts:

  1. 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?
  2. An optional general question: are my conclusions right, am I misunderstanding something?

References

Tutorials

StackOverflow Questions & Answers

解决方案

You're definitely on to something in the way that you're stating this—there are many things that Monad means and you've separated them out well.

That said, I would definitely say that chaining operations is not the primary thing solved by Monads. That can be solved using plain Functors (with some trouble) or easily with Applicatives. You need to use the full monad spec when "chaining with choice". In particular, the tension between Applicative and Monad comes from Applicative needing to know the entire structure of the side-effecting computation statically. Monad can change that structure at runtime and thus sacrifices some analyzability for power.


To make the point more clear, 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. This shows up repeatedly in the Control.Monad module, so we can examine some examples from there.

sequence     :: [m a] -> m [a]
forever      :: m a   -> m b
foldM        :: (a -> b -> m a) -> a -> [b] -> m a

Immediately, we can throw out sequence as being particular to Monad since there's a corresponding function in Data.Traversable, sequenceA which has a type slightly more general than Applicative f => [f a] -> f [a]. This ought to be a clear indicator that Monad isn't the only way to sequence things.

Similarly, we can define foreverA as follows

foreverA :: Applicative f => f a -> f b
foreverA f = flip const <$> f <*> foreverA f

So more ways to sequence non-Monad types. But we run into trouble with foldM

foldM             :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
foldM _ a []      =  return a
foldM f a (x:xs)  =  f a x >>= \fax -> foldM f fax xs

If we try to translate this definition to Applicative style we might write

foldA             :: (Applicative f) => (a -> b -> f a) -> a -> [b] -> f a
foldA _ a []      =  pure a
foldA f a (x:xs)  = foldA f <$> f a x <*> xs

But Haskell will rightfully complain that this doesn't typecheck--each recursive call to foldA tries to put another "layer" of f on the result. With Monad we could join those layers down, but Applicative is too weak.


So how does this translate to Applicatives restricting us from runtime choices? Well, that's exactly what we express with foldM, a monadic computation (a -> b -> m a) which depends upon its a argument, a result from a prior monadic computation. That kind of thing simply doesn't have any meaning in the more purely sequential world of Applicative.

这篇关于“链接操作”是“仅限” Monad类解决的事情?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆