我学习Haskell已经有几个星期了(只是为了好玩),刚刚看了Brian Beckman的精彩的《Haskell》。单项式介绍视频. 他激励单项式的动机是需要创建一个更通用的组成运算符。 按照这个思路,如果我有两个函数。
f :: a -> b
g :: b -> c
组成算子应该满足
h = g . f :: a -> c
由此我可以推断出正确的类型。.
操作符。
(.) : (b -> c) -> (a -> b) -> (a -> c)
当涉及到单项式时,假设我有两个函数。
f :: a -> m b
g :: b -> m c
在我看来,自然而然的选择是 定义一个广义的构成运算符,其工作原理如下:
h = f >>= g :: a -> m c
这样的话 >>=
操作符的类型签名为。
(>>=) :: (a -> m b) -> (b -> m c) -> (a -> m c)
但实际上这个操作符的定义似乎是这样的:
h a = (f a) >>= g :: m c
于是
(>>=) : m b -> (b -> m c) -> m c
谁能解释一下选择bind这个定义背后的原因? 我假设这两个选择之间有一些简单的联系,其中一个可以用另一个来表达,但我现在没有看到。
谁能解释一下选择bind这个定义背后的原因?
当然可以,而且几乎和你的理由完全一样。只是... ... 我们想要一个更通用的... 申请 操作符,而不是一个更通用的组成操作符。如果你做过很多(任何)无点程序设计,你会立即认识到为什么:与有点程序相比,无点程序很难写,而且令人难以置信的难读。比如说
h x y = f (g x y)
对于函数应用来说,这是完全直接的。只用函数组成的版本是什么样子的?
h = (f .) . g
如果你第一次看到这个的时候,不用停下来盯着看一两分钟,你可能真的是一台电脑。
所以,不管出于什么原因:我们的大脑被赋予了更好的工作能力,名字和函数应用一出,就能更好的工作。所以你剩下的参数是这样的,但是用应用代替了组成。如果我有一个函数和一个参数。
f :: a -> b
x :: a
应用运算符应该满足
h = x & f :: b
由此我可以推断出正确的类型。&
操作符。
(&) :: a -> (a -> b) -> b
当涉及到单项式时, 假设我的函数和参数是单项式的:
f :: a -> m b
x :: m a
自然而然的选择是定义一个广义的应用运算符 它的工作原理如下:
h = x >>= f :: m b
在这种情况下 >>=
操作符的类型签名为
(>>=) :: m a -> (a -> m b) -> m b
(>>=)
是 不 组成运算符。是一个应用运算符。
(&) :: a -> (a -> b) -> b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
还有 (=<<)
(自 Control.Monad
),对应的是更常见的应用运算符。($)
:
($) :: (a -> b) -> a -> b
(=<<) :: Monad m => (a -> m b) -> m a -> m b
在组成方面,我们有两个 (<=<)
和 (>=>)
(又来自 Control.Monad
,第一种是完全类似于 (.)
:
(.) :: (b -> c) -> (a -> b) -> a -> c
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
((>=>)
只是 (<=<)
并翻转其参数。(>=>) = flip (<=<)
)
当我们在比较类型时,你可能想看看如何在 fmap
适合。
($) :: (a -> b) -> a -> b
fmap :: Functor f => (a -> b) -> f a -> f b
(=<<) :: Monad m => (a -> m b) -> m a -> m b
($)
和 fmap
采用相同类型的函数,但应用于不同类型的参数。
fmap
和 (=<<)
采用不同类型的函数,但都应用于同一类型的参数(虽然方式不同)。
我同意从以下方面思考 ( >=> ) :: ( a -> m b ) -> ( b -> m c ) -> ( a -> m c)
往往感觉更自然,因为它更接近于通常的功能构成,事实上,它 是 Kleisli类别中的组成。从这个角度来看,Haskell的许多单体实例其实更容易理解。
Haskell之所以选择 ( >>= ) :: m a -> ( a -> m b) -> m b
可能是这个定义在某种程度上是最普遍的定义。这两个定义都是: >=>
和 join :: m ( m x ) -> m x
可还原为 >>=
:
( >=> ) f g x = f x >>= g
join mmx = mmx >>= id
如果你加上 return :: x -> m x
也可以推导出 fmap :: ( a -> b ) -> m a -> m b
(Functor)和 ( <*> ) :: m ( a -> b ) -> m a -> m b
(适用):
fmap f ma = ma >>= ( return . f )
( <*> ) mab ma =
mab >>= \f ->
ma >>= \a ->
return ( f a )