为什么<$>和<*>的输入顺序是>> =?
我了解<$>
的签名背后的原因,因为它只是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
?
答案是“为什么它以这个顺序参数”基本上是“因为”。定义这些功能的人认为这是最好的方法。 (在每种情况下可能都不是同一个人)。但是,我会提供一些示例:
假设我们有某种解析单子。再假设我们已经定义
data Foobar = Foobar X Y Z
parseFoo :: Parser X
parseBar :: Parser Y
parseBaz :: Parser Z
然后,我们可以写
parseFoobar :: Parser Foobar
parseFoobar = do
foo <- parseFoo
bar <- parseBar
baz <- parseBaz
return (Foobar foo bar baz)
或者明确,
parseFoobar =
parseFoo >>= \ foo ->
parseBar >>= \ bar ->
parseBaz >>= \ baz ->
return (Foobar foo bar baz)
现在让我们来编写应用性风格:
parseFoobar = return Foobar <*> parseFoo <*> parseBar <*> parseBaz
或者可选地,
parseFoobar = Foobar <$> parseFoo <*> parseBar <*> parseBaz
如果我们假设<**> = flip <*>
存在(并具有正确的关联性),那么我们有
parseFoobar = parseBaz <**> parseBar <**> parseFoo <**> return Foobar
这只是看起来很奇怪。在最后的函数中,参数的顺序是相反的?你为什么要这样写呢? (请注意,任何效果都是也是,相反顺序)。
在monadic版本中,效果发生在顶部到底部。在应用版本中,效果从左到右发生。这似乎是“自然的”。
你可以在示例函数中添加一些示例类型吗?只是让我有一个更容易的时间围绕发生的事情 – 2014-09-24 11:45:12
编辑了一点东西。 – MathematicalOrchid 2014-09-24 11:55:12
这是如何工作的功能链方面呢?你在这里做的是将一个函数应用到3个包装的值,但是“仅仅4 >> = foo >> = bar >> = baz',与说'+(+)只有3 只有4' – 2014-09-24 12:21:57
不要让monad阻碍我们前进。想想应用程序。
比较:
(<$>) :: Functor f => (a -> b) -> f a -> f b
和
($) :: (a -> b) -> a -> b
你可以看到有一个仿函数在普通的应用程序和应用程序之间的连接。
花絮:有过proposals使用括号超载空白(应用程序),以便我们可以这样写:
(| f a1 .. an |)
,而不是
pure f <*> a1 <*> .. <*> an
我从来没有见过这种情况,我只用'fmap'来看'',现在你已经把它作为'$'的一个变体呈现出来了,它更有意义 – 2014-09-24 12:35:41
我认为这应该是可接受的回答! – 2014-10-03 21:45:09
但你经常需要的正是这种与'Applicatives' :例如'(+)只是5 只是5 ' Carsten 2014-09-24 11:27:58
'(>> =)'构造链条“势在必行的风格”(否则我们会留下奇怪的原因它不是与'$'相同的顺序)。 '()'对于“应用”多参数函数很有用 - 按照声明的顺序提供参数。 – 2014-09-24 11:31:59
真正的问题是为什么'(>> =)'使用与Haskell中几乎所有对象相反的顺序。 (= (a - > mb) - >(ma - > mb)'的类型看起来更像Haskell中的所有其他部分,而非'(>> =)= flip = 2014-09-25 09:51:40