Monad如何符合函数组合的要求?

3

我知道函数都是关于组合的。比如说,如果我有一个从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?这样更有意义吗?


2
因此,最近讨论了bind的相反“替代签名”:为什么bind的类型不是m a -> (a -> b) -> m b - Christian Conkle
4个回答

6

试着自己实现这个函数,你会发现它是无用的:

func :: Monad m => m a -> (m a -> m b) -> m b
func x f = f x

所以您的基本建议是为函数添加一个值。我认为我们不需要一个特殊的函数来实现这个。 :-) >>= 的整个意义在于它执行第一个参数的副作用,然后将其结果值传递给函数。

2
“我认为我们不需要为此编写特殊函数。” 美元符号 $ 不这么认为。 - Cubic
@Cubic 我知道你知道这个:[$有一些编译器魔法在其中](https://dev59.com/y2Ii5IYBdhLWcg3w9gJy)。 - Sibi
当然,但这并不否定我所说的。即使没有魔法,$仍然很有用 - 实际上,魔法只是因为它很有用而被引入,你只是期望它能够正常工作。 - Cubic
@Cubic 我同意你的观点。如果你认为在这里添加有关 $ 的信息会有所帮助,可以随意编辑答案。 :) - Sibi

6

@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


5

>>=不对应于组合,它对应于(翻转的)应用。翻转版本=<<可以说明这一点:

($)   ::              (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

(.) 组合两个一元函数,得到一个一元函数;<=< 组合两个一元操作,得到一个一元操作。


1
如果您重新排列类型签名,它将更加有意义....
modify成为反转顺序的(>>=)
modify::Monad m => (a -> m b) -> m a -> m b

或者,添加暗示的括号。
modify::Monad m => (a -> m b) -> (m a -> m b)

现在清楚发生了什么......我们得到了一个“不平衡”的函数,无法放置在固定数据类型的管道中,并将其转换为可以的函数......这些函数更容易处理,因为您可以随意添加和删除它们到管道中,应用N次,甚至重新排序它们。(至少对于a=b
这种模式非常普遍......例如,a->m b可以是一个接受值的函数,可能会返回值或错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接