Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 为什么绑定运算符(>;=)按原样定义?_Haskell_Bind_Monads - Fatal编程技术网

Haskell 为什么绑定运算符(>;=)按原样定义?

Haskell 为什么绑定运算符(>;=)按原样定义?,haskell,bind,monads,Haskell,Bind,Monads,我已经学习哈斯凯尔好几个星期了(只是为了好玩),我刚刚看了布莱恩·贝克曼的《伟大》。他激励单子需要创建一个更通用的构图操作符。按照这个思路,如果我有两个功能: f :: a -> b g :: b -> c f :: a -> m b g :: b -> m c 复合运算符应满足 h = g . f :: a -> c h = x & f :: b 由此我可以推断出运算符的正确类型: (.) : (b -> c) -> (a ->

我已经学习哈斯凯尔好几个星期了(只是为了好玩),我刚刚看了布莱恩·贝克曼的《伟大》。他激励单子需要创建一个更通用的构图操作符。按照这个思路,如果我有两个功能:

f :: a -> b
g :: b -> c
f :: a -> m b
g :: b -> m c
复合运算符应满足

h = g . f :: a -> c
h = x & f :: b
由此我可以推断出
运算符的正确类型:

(.) : (b -> c) -> (a -> b) -> (a -> c)
(&) :: a -> (a -> b) -> b
对于单子,假设我有两个函数:

f :: a -> b
g :: b -> c
f :: a -> m b
g :: b -> m c
在我看来,自然选择是定义一个广义复合算子,其工作原理如下:

h = f >>= g :: a -> m c
h = x >>= f :: m b
在这种情况下,
>=
运算符的类型签名为:

(>>=) :: (a -> m b) -> (b -> m c) -> (a -> m c)
(>>=) :: m a -> (a -> m b) -> m b
但实际上,操作符似乎是这样定义的

h a = (f a) >>= g :: m c
因此

(>>=) : m b -> (b -> m c) -> m c

有人能解释一下bind定义选择背后的原因吗?我假设这两个选择之间存在某种简单的联系,其中一个可以用另一个来表示,但我目前没有看到它。

您可以搜索运算符,并看到它被称为
(>=>)
。它在
(>>=)
方面的定义是:

从某种意义上说,
(>=>)
更好地反映了概括合成的思想,但我认为
(>>=)
作为一个基本运算符更有效,因为它在更多情况下更实用,并且更容易与do表示法关联

有人能解释一下bind定义选择背后的原因吗

当然,这和你的推理几乎完全一样。只是。。。我们想要一个更通用的应用运算符,而不是更通用的复合运算符。如果你做过很多(任何)无点编程,你会马上意识到原因:与有点编程相比,无点编程很难编写,而且读起来也非常困难。例如:

h x y = f (g x y)
对于函数应用程序,这是完全简单的。只使用函数组合的版本是什么样子的

h = (f .) . g
如果你不必在第一次看到它时停下来盯着它看一两分钟,那么你实际上可能就是一台电脑

所以,不管出于什么原因:我们的大脑天生就可以更好地处理开箱即用的名称和函数应用程序。下面是你剩下的论点,但是用应用程序代替合成。如果我有一个函数和一个参数:

f :: a -> b
x :: a
应用程序操作符应该满足

h = g . f :: a -> c
h = x & f :: b
由此我可以推断出
&
运算符的正确类型:

(.) : (b -> c) -> (a -> b) -> (a -> c)
(&) :: a -> (a -> b) -> b
关于单子,假设我的函数和参数是单子的:

f :: a -> m b
x :: m a
自然选择是定义一个通用应用运算符,其工作方式如下:

h = f >>= g :: a -> m c
h = x >>= f :: m b
在这种情况下,
>=
运算符的类型签名为:

(>>=) :: (a -> m b) -> (b -> m c) -> (a -> m c)
(>>=) :: m a -> (a -> m b) -> m b

我同意用
(>=>):(a->mb)->(b->mc)->(a->mc)
来思考通常感觉更自然,因为它更接近于通常的函数组合,事实上它是Kleisli类别中的组合。从这个角度来看,Haskell的许多monad实例实际上更容易理解

Haskell选择
(>>=):MA->(a->MB)->MB
的一个原因可能是这个定义在某种程度上是最普遍的。
=>
join::m(mx)->mx
都可以减少为
>=

( >=> ) f g x = f x >>= g

join mmx = mmx >>= id
如果将
return::x->mx
添加到组合中,还可以派生
fmap::(a->b)->ma->mb
(Functor)和
()::m(a->b)->ma->mb
(应用程序):

fmap f ma=ma>>=(return.f)
()单抗=
mab>>=\f->
ma>>=\a->
返回(f a)
(>>=)
不是合成运算符。这是一个应用程序操作员

(&)   ::              a -> (a ->   b) ->   b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
还有
(=)
就是
()=翻转(a->b
函子f=>(a->b)->f a->f b
(=MB)->MA->MB
($)
fmap
采用相同类型的函数,但将其应用于不同类型的参数


fmap
(=您的操作员存在。正如您所怀疑的,它可以用
(>>=)
来定义,反之亦然。
(>>=)
(>=)更常用,因为在实践中,它在大多数情况下更方便。有关讨论,请参阅以及本部分的第一部分(尽管从另一个角度来看这个问题)。简单地说,组合和应用程序是相似的。两者都很有用,但应用程序通常在编程中更常见。太棒了……应用程序语法在大多数情况下应该更方便,这是有道理的。我想说你的建议(称为“kleisli composition”
(=)
,从数学上讲,
(>>=)
主要是程序员偏好的便利。感谢您在两个运算符中明确了这一区别。“如果您第一次看到这一点时不必停下来盯着看一两分钟,您实际上可能是一台计算机。”我也很欣慰地注意到,我仍然是人类(尽管我研究过哈斯克尔):“我并不真的相信本质论者的论点:“我们的大脑天生就可以更好地使用开箱即用的名称和函数应用程序。”,因为对初学者程序员的研究通常会发现变量、函数应用和值级编程有很大的困难,因为自然语言往往更倾向于函数级、连续性和面向事件。我确实认为,出于熟悉的原因,我们的大脑是由我们喜欢这种风格的语言连接起来的。呃,a i是唯一一个看不到点和自由点的类型是如何排列的?第一个f应用于一个值(GxY的结果;因此假设g::a->b->c,f必须是c->d)。第二个,f应用于一个恰好是函数的值,对吗?所以两个f不能具有相同的类型,除非GxY具有与().这似乎有误导性