我知道这些功能都与构图有关。例如,如果我有一个从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
?这更有意义吗?
答案 0 :(得分:6)
尝试自己实现该功能,你会发现它没用:
func :: Monad m => m a -> (m a -> m b) -> m b
func x f = f x
所以你基本上建议的是将值应用于函数。我认为我们不需要特殊的功能。 :-) >>=
的重点是它执行第一个参数的副作用,然后将结果值传递给函数。
答案 1 :(得分:6)
@Sibi的回答是正确的,将monad定义为第二个签名是没有意义或有用的。但是关于你关于函数组成和monadic组合之间关系的问题,还有另一种看待运算符的方法。
Monads有一堆替代结构,相当于绑定/返回公式。其中一个是运算符(<=<)
,称为Kleisli组合运算符,它以与结构相似的方式组成monadic运算。
箭头:
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
答案 2 :(得分: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}}组成两个一元行动,给予一元行动。
答案 3 :(得分:1)
如果您重新排序类型签名,那么对您来说会更有意义....
让modify
成为(&gt;&gt; =)翻转顺序
modify::Monad m => (a -> m b) -> m a -> m b
或添加隐含的括号
modify::Monad m => (a -> m b) -> (m a -> m b)
现在很明显发生了什么......我们得到了一个“不平衡”的功能,不能放在固定数据类型的管道中,并将其转换为可以实现的功能......这些功能更好处理,因为你可以随意添加和删除它们,应用N次,甚至重新排序。 (好吧,对于a=b, at least
)
模式很常见....例如,a->m b
可能是一个带val的函数,可能会也可能不会返回值或错误。