【发布时间】:2014-11-18 20:42:00
【问题描述】:
我理解 <$> 的类型签名背后的原因,因为它只是 fmap 的中缀版本,但将其与 >>= 的类型签名相比,对我来说意义不大。
让我们首先确定我的意思。
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(<$>) :: Functor f => (a -> b) -> f a -> f b
查看类型签名,我们可以看到 >>= 在左侧接受一个值,在右侧接受一个函数,如果考虑它的链接属性,这很有意义:foo >>= bar >>= baz
这让我想知道,为什么 <*> 和 <$> 不这样做呢?你不能写foo <*> bar <*> baz,因为它要求foo <*> bar的输出是一个函数,而不是一个值。
我知道<**> 和=<< 存在,它们都颠倒了参数的顺序,允许我做类似的事情:
Just 4 <**> pure (+3) <**> pure (*2) >>= (\x -> Just (x-3))
这本可以完美地简化为:
Just 4 <$$> (+3) <$$> (*2) >>= (\x -> Just (x-3))
如果<$$> 已经存在,或者<$> 和<*> 的参数顺序颠倒了。
让我想知道为什么存在差异的另一件事是,新手更难习惯和/或记住它是函数还是首先出现的值,而无需查找它。
那么为什么<*> 和<$> 是fn op val 而>>= 是val op fn?
【问题讨论】:
-
但你经常想要
Applicatives完全一样:例如(+) <$> Just 5 <*> Just 5Just 漂亮吗;) -
(>>=)构造链“命令式”(否则我们会想知道为什么它与$的顺序不同)。(<=<)还允许链接 - “(.)样式”。(<*>)对于“应用”多参数函数很有用——按照声明的顺序提供参数。 -
真正的问题是为什么
(>>=)使用与 Haskell 中几乎所有内容相反的顺序。(=<<) :: Monad m => (a -> m b) -> (m a -> m b)的类型看起来比(>>=) = flip (=<<)的类型更像 Haskell 中的其他所有内容。 -
@Rhymoid 我想从左到右阅读更自然,所以通过箭头方向从左到右将值传递给函数感觉更自然?
val >>= fun -
@ElectricCoffee 从左到右,从上到下是not natural per se。在文件中,“从早到晚”是一个更合适的术语。在这种情况下,像
(>>>)那样进行组合工作似乎是有意义的。甚至类型更好(仅限于函数,(>>>) :: (a -> b) -> (b -> c) -> (a -> c))。为了使这更自然,您还需要更改函数应用程序的工作方式:(foo >>> bar >>> baz) x == baz (bar (foo x))。你确定要改写x (foo >>> bar >>> baz) == ((x foo) bar) baz吗?
标签: haskell parameters monads functor applicative