为什么<$>左关联?

hgi*_*sel 9 haskell design-choices

fmap也是<$>因为它是函数$类中的函数application().

(+5)  $  (*10)  $  10      -- 105
(+5) <$> (*10) <$> [1,2,3] -- [15,25,35]
Run Code Online (Sandbox Code Playgroud)

然后我想,在这种情况下<*>,应用程序仿函数类别中的函数应用程序,这应该工作:

[(+5), (+10)] <*>  [(*10), (*100)] <*> [1,2,3]  -- ERROR
-- however:
[(+5), (+10)] <*> ([(*10), (*100)] <*> [1,2,3]) -- [15,25,35,...]
Run Code Online (Sandbox Code Playgroud)

因此,<$>只有因为fmap函数恰好是后期合成才会成功,因此(+5) <$> (*10)(+5) . (*10)其应用于[1,2,3].

然而,对于所有应用程序运算符(<<=包括)而言,左边的关联性在我看来似乎是一个糟糕的设计选择,特别是在他们认识到$已经是正确关联的相似性之后.还有其他原因吗?

lef*_*out 8

真的,原因可能只是它允许<$><*>共享一个优先级.我们绝对希望<*>成为左联想的东西

Prelude> foldr <$> [(+),(*)] <*> [0,1] <*> [[1,2,3], [4,5,6]]
[6,15,7,16,0,0,6,120]
Run Code Online (Sandbox Code Playgroud)

<$>虽然它没有更高的优先级,但这也使得行为正确.实际上,链接多个<$>运算符对于左关联性确实不是很有用.

但是,对于右关联性,它也没有用.正如chepner评论的那样,它实际上有点滑稽,$是正确联想的.当然,这允许写作像

Prelude> sum $ map (+3) $ take 19 $ cycle [4..7]
160
Run Code Online (Sandbox Code Playgroud)

但是,这也可以写成可以说更优雅

Prelude> sum . map (+3) . take 19 . cycle $ [4..7]
160
Run Code Online (Sandbox Code Playgroud)

(我说更优雅,因为这里计算链被解析为一个单一的功能管道,而不是命令式的"做到这一点,那么,那么......").由于仿函数法则,这可以用<$>.使用$和使用相同的方式来完成..

为什么你可能更喜欢多重的唯一原因$的风格是,它允许在管道中缀表达式,也许是最常见的例子是镜头更新(通常写有被翻动的&,但原理是一样的):

Prelude Control.Lens> [4..7] & ix 1+~9 & ix 2*~8
[4,14,48,7]
Run Code Online (Sandbox Code Playgroud)

这是因为$并且&具有非常低的优先级,几乎比任何中缀运算符低.事实并非如此,<$>所以你做不到

Prelude Control.Lens> ix 1+~9 <$> [[4..8], [5..9]]

<interactive>:23:1: error:
    Precedence parsing error
        cannot mix ‘+~’ [infixr 4] and ‘<$>’ [infixl 4] in the same infix expression
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您无论如何都需要使用一些括号,然后您也可以使用以下的低优先级合成运算符来执行此操作Control.Category:

Prelude Control.Lens Control.Category> (ix 1+~9 >>> ix 2*~8) <$> [[4..8], [5..9]]
[[4,14,48,7,8],[5,15,56,8,9]]
Run Code Online (Sandbox Code Playgroud)

或者在每个更新程序周围使用parens:

Prelude Control.Lens> (ix 1+~9) . (ix 2*~8) <$> [[4..8], [5..9]]
[[4,14,48,7,8],[5,15,56,8,9]]
Run Code Online (Sandbox Code Playgroud)