我知道函数都是关于组合的。比如说,如果我有一个从A到B的箭头和一个从B到C的箭头,组合意味着我也有一个从A到C的箭头。
但是对于(>>=)
,它的类型是Monad m => m a -> (a -> m b) -> m b
。为什么这里的m a
等于a
呢?
我想知道为什么不是Monad m => m a -> (m a -> m b) -> m b
?这样更有意义吗?
我知道函数都是关于组合的。比如说,如果我有一个从A到B的箭头和一个从B到C的箭头,组合意味着我也有一个从A到C的箭头。
但是对于(>>=)
,它的类型是Monad m => m a -> (a -> m b) -> m b
。为什么这里的m a
等于a
呢?
我想知道为什么不是Monad m => m a -> (m a -> m b) -> m b
?这样更有意义吗?
试着自己实现这个函数,你会发现它是无用的:
func :: Monad m => m a -> (m a -> m b) -> m b
func x f = f x
>>=
的整个意义在于它执行第一个参数的副作用,然后将其结果值传递给函数。$
不这么认为。 - Cubic$
仍然很有用 - 实际上,魔法只是因为它很有用而被引入,你只是期望它能够正常工作。 - Cubic$
的信息会有所帮助,可以随意编辑答案。 :) - Sibi@Sibi的答案是正确的,将单子定义为第二个签名是没有意义的,也不会有用。但是关于您关于函数组合和单子组合之间关系的问题,有一种替代方法看待运算符。
单子具有一堆等价于绑定/返回公式的替代构造方式。其中一种是使用名为Kleisli组合运算符(<=<)
,该运算符以类似于函数组合的结构方式组合单子操作。
箭头:
Functions : a -> b
Monadic operations : a -> m b
构成:
-- Function composition
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> g (f x)
-- Monad composition
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
f <=< g ≡ \x -> g x >>= f
Gabriel Gonzalez写了一篇关于这个模式的不错博客文章:http://www.haskellforall.com/2012/08/the-category-design-pattern.html
>>=
不对应于组合,它对应于(翻转的)应用。翻转版本=<<
可以说明这一点:
($) :: (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
(.)
组合两个一元函数,得到一个一元函数;<=<
组合两个一元操作,得到一个一元操作。
modify
成为反转顺序的(>>=)modify::Monad m => (a -> m b) -> m a -> m b
modify::Monad m => (a -> m b) -> (m a -> m b)
a=b
)a->m b
可以是一个接受值的函数,可能会返回值或错误。
bind
的相反“替代签名”:为什么bind
的类型不是m a -> (a -> b) -> m b
? - Christian Conkle