为什么< $>左联想?

时间:2017-12-31 14:13:40

标签: haskell design-choices

fmap也是<$>,因为它是函数类别中的函数应用程序($)。

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

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

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

因此,<$>只能解决问题,因为函数的fmap恰好是后期合成,因此(+5) <$> (*10)变为(+5) . (*10),然后应用于[1,2,3]。< / p>

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

1 个答案:

答案 0 :(得分:7)

真的,原因可能只是它允许<$><*>共享一个优先级。我们绝对希望<*>是左关联的,所以像

这样的东西
Prelude> foldr <$> [(+),(*)] <*> [0,1] <*> [[1,2,3], [4,5,6]]
[6,15,7,16,0,0,6,120]

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

但是,对于右关联性,它也不会非常有用。正如chepner评论的那样,$是正确联想的实际上有点有趣。当然,这允许编写像

这样的作品
Prelude> sum $ map (+3) $ take 19 $ cycle [4..7]
160

然而,这也可以写成可以说更优雅的

Prelude> sum . map (+3) . take 19 . cycle $ [4..7]
160

(我说更优雅,因为这里的计算链被解析为一个单一的功能管道,而不是命令式的“做到这一点,那么,然后......”)。多亏了仿函数法律,这与<$>.以及$.的方式相同。

您可能更喜欢多个$样式的唯一原因是它允许在管道中使用中缀表达式,也许最常见的例子是镜头更新(通常使用翻转的&编写,但原则是一样的):

Prelude Control.Lens> [4..7] & ix 1+~9 & ix 2*~8
[4,14,48,7]

这是有效的,因为$&的优先级非常低,几乎比任何中缀运算符都低。 <$>的情况不是这样,所以你不能这样做

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

在这种情况下,您需要无论如何都要使用一些括号,然后您也可以使用来自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]]

或每个更新程序周围的parens:

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