Haskell Monad 绑定运算符混淆
好吧,我不是 Haskell 程序员,但我对 Haskell 背后的很多想法非常感兴趣,并且正在考虑学习它。但我陷入了第一个困境:我似乎无法理解 Monad,这似乎是相当基本的。我知道有上百万个问题要求解释 Monad,所以我将更具体地说明困扰我的问题:
我读了这篇优秀的文章 (介绍Javascript),并认为我完全理解 Monads。然后我读了维基百科关于 Monads 的条目,看到了这个:
多态类型的绑定操作 (M t)→(t→M u)→(M u),Haskell 用中缀运算符 >>= 表示。 它的第一个参数是一个单子类型的值,它的第二个参数是一个从第一个参数的基础类型映射到另一个单子类型的函数,并且它的结果是另一个单子类型。< /p>
,在我引用的文章中,bind 是一个只接受一个参数的函数。维基百科说有两个。我对 Monad 的理解如下:
- Monad 的目的是采用具有不同输入和输出类型的函数并使其可组合。它通过使用单个单子类型包装输入和输出类型来实现此目的。
- Monad 由两个相互关联的函数组成:bind 和 unit。 Bind 接受一个不可组合的函数 f 并返回一个新函数 g,该函数接受单子类型作为输入并返回单子类型。 g 是可组合的。 unit 函数接受 f 期望的类型的参数,并将其包装在 monadic 类型中。然后可以将其传递给 g 或 g 等函数的任何组合。
但肯定有什么问题,因为我的绑定概念只有一个参数:一个函数。但是(根据维基百科)Haskell 的绑定实际上需要两个参数!我的错误在哪里?
Okay, so I am not a Haskell programmer, but I am absolutely intrigued by a lot of the ideas behind Haskell and am looking into learning it. But I'm stuck at square one: I can't seem to wrap my head around Monads, which seem to be fairly fundamental. I know there are a million questions on SO asking to explain Monads, so I'm going to be a little more specific about what's bugging me:
I read this excellent article (an introduction in Javascript), and thought that I understood Monads completely. Then I read the Wikipedia entry on Monads, and saw this:
A binding operation of polymorphic type (M t)→(t→M u)→(M u), which Haskell represents by the infix operator >>=. Its first argument is a value in a monadic type, its second argument is a function that maps from the underlying type of the first argument to another monadic type, and its result is in that other monadic type.
Okay, in the article that I cited, bind was a function which took only one argument. Wikipedia says two. What I thought I understood about Monads was the following:
- A Monad's purpose is to take a function with different input and output types and to make it composable. It does this by wrapping the input and output types with a single monadic type.
- A Monad consists of two interrelated functions: bind and unit. Bind takes a non-composable function f and returns a new function g that accepts the monadic type as input and returns the monadic type. g is composable. The unit function takes an argument of the type that f expected, and wraps it in the monadic type. This can then be passed to g, or to any composition of functions like g.
But there must be something wrong, because my concept of bind takes one argument: a function. But (according to Wikipedia) Haskell's bind actually takes two arguments! Where is my mistake?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您链接的文章基于 sigfpe 的文章,该文章使用了绑定的翻转定义:
因此,Haskell
bind
接受一个包含在 monad 中的值,并返回一个函数,该函数接受一个函数,然后用提取的值调用它。我可能使用不精确的术语,所以也许用代码更好。现在,
翻译你的 JS 绑定(Haskell 会自动柯里化,因此调用
bind f
返回一个接受元组的函数,然后模式匹配负责将其解包到x 和
s
,我希望这是可以理解的):你可以看到它正在工作:
现在,让我们反转
bind
的参数:你可以清楚地看到它仍然在做同样的事情,但有一个语法略有不同:
现在,Haskell 有一个语法技巧,允许您使用任何函数作为中缀运算符。所以你可以这样写:
现在将
bind'
重命名为>>=
((3, "") >>=cube >>=正弦
),你就得到了你想要的东西。正如您所看到的,通过这个定义,您可以有效地摆脱单独的组合运算符。将新事物翻译回 JavaScript 会产生类似这样的结果(再次注意,我只是颠倒了参数顺序):
希望这会有所帮助,并且不会引入更多混乱 - 重点是这两个绑定定义是等效的,只是调用不同句法。
The article you link is based on sigfpe's article, which uses a flipped definition of bind:
So, the Haskell
bind
takes a value enclosed in a monad, and returns a function, which takes a function and then calls it with the extracted value. I might be using non-precise terminology, so maybe better with code.You have:
Now, translating your JS bind (Haskell does automatic currying, so calling
bind f
returns a function that takes a tuple, and then pattern matching takes care of unpacking it intox
ands
, I hope that's understandable):You can see it working:
Now, let's reverse arguments of
bind
:You can clearly see it's still doing the same thing, but with a bit different syntax:
Now, Haskell has a syntax trick that allows you to use any function as an infix operator. So you can write:
Now rename
bind'
to>>=
((3, "") >>= cube >>= sine
) and you've got what you were looking for. As you can see, with this definition, you can effectively get rid of the separate composition operator.Translating the new thing back into JavaScript would yield something like this (notice that again, I only reverse the argument order):
Hope this helps, and not introduces more confusion — the point is that those two bind definitions are equivalent, only differing in call syntax.
你没有犯错误。这里要理解的关键思想是柯里化——两个参数的 Haskell 函数可以用两种方式来看待。第一个只是两个参数的函数。例如,如果您有
(+)
,这通常被视为采用两个参数并将它们相加。另一种看待它的方式是作为加法机生产商。(+)
是一个函数,它接受一个数字,例如x
,并创建一个将添加x
的函数。在处理 monad 时,有时最好考虑一下
=<<
,即>>=
的翻转版本。有两种方法可以看待它:它是两个参数的函数,并将
输入函数转换为本文提到的易于组合的版本。正如我之前所解释的,它们与
(+)
是等效的。You are not making a mistake. The key idea to understand here is currying - that a Haskell function of two arguments can be seen in two ways. The first is as simply a function of two arguments. If you have, for example,
(+)
, this is usually seen as taking two arguments and adding them. The other way to see it is as a addition machine producer.(+)
is a function that takes a number, sayx
, and makes a function that will addx
.When dealing with monads, sometimes it is probably better, as ephemient mentioned above, to think of
=<<
, the flipped version of>>=
. There are two ways to look at this:which is a function of two arguments, and
which transforms the input function to an easily composed version as the article mentioned. These are equivalent just like
(+)
as I explained before.请允许我推翻你对 Monad 的信念。我真诚地希望你意识到我并不是想表现得无礼;我只是想表现得无礼。我只是想避免拐弯抹角。
不完全是。当你以“一个 Monad 的目的”开始一个句子时,你就已经走错了路。 Monad 不一定有“目的”。
Monad
只是一个抽象,一个适用于某些类型而不适用于其他类型的分类。Monad
抽象的目的就是抽象。是的,也不是。
bind
和unit
的组合足以定义 Monad,但是join
、fmap
和unit
同样足够。事实上,后者是范畴论中典型描述单子的方式。再说一遍,不完全是。一元函数
f :: a -> m b
对于某些类型来说是完全可组合的。我可以使用函数g :: mb -> 对其进行后合成c
得到g 。 f::a-> c
,或者我可以用函数h :: c -> 预先组合它。 a
得到f 。 h::c-> m b
。但你的第二部分绝对正确:
(>>= f) :: ma -> m b
。正如其他人所指出的,Haskell 的bind
函数以相反的顺序获取参数。嗯,是的。如果
g :: ma -> m b
,那么你可以用函数f :: c -> 来预组合它。 m a
得到g 。 f::c-> m b
,或者您可以使用函数h :: mb -> 进行后合成。 c
得到h 。 g::ma-> c
.请注意,c
可以采用m v
形式,其中m
是一个Monad。我想当你说“可组合”时,你的意思是说“你可以组合这种形式的任意长的函数链”,这是正确的。这是一种迂回的说法,但是,是的,这是正确的。
再说一次,是的。尽管调用
unit
(或者在 Haskell 中,return
)然后将其传递给(>>= f)
。Allow me to tear down your beliefs about Monads. I sincerely hope you realize that I am not trying to be rude; I'm simply trying to avoid mincing words.
Not exactly. When you start a sentence with "A Monad's purpose", you're already on the wrong foot. Monads don't necessarily have a "purpose".
Monad
is simply an abstraction, a classification which applies to certain types and not to others. The purpose of theMonad
abstraction is simply that, abstraction.Yes and no. The combination of
bind
andunit
are sufficient to define a Monad, but the combination ofjoin
,fmap
, andunit
is equally sufficient. The latter is, in fact, the way that Monads are typically described in Category Theory.Again, not exactly. A monadic function
f :: a -> m b
is perfectly composable, with certain types. I can post-compose it with a functiong :: m b -> c
to getg . f :: a -> c
, or I can pre-compose it with a functionh :: c -> a
to getf . h :: c -> m b
.But you got the second part absolutely right:
(>>= f) :: m a -> m b
. As others have noted, Haskell'sbind
function takes the arguments in the opposite order.Well, yes. If
g :: m a -> m b
, then you can pre-compose it with a functionf :: c -> m a
to getg . f :: c -> m b
, or you can post-compose it with a functionh :: m b -> c
to geth . g :: m a -> c
. Note thatc
could be of the formm v
wherem
is a Monad. I suppose when you say "composable" you mean to say "you can compose arbitrarily long chains of functions of this form", which is sort of true.A roundabout way of saying it, but yes, that's about right.
Again, yes. Although it is generally not idiomatic Haskell to call
unit
(or in Haskell,return
) and then pass that to(>>= f)
.