为什么 <$> 是左关联的?
Why is <$> left associative?
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]
.
然而,所有应用程序运算符(包括 <<=
)的左结合性在我看来是一个糟糕的设计选择,尤其是在他们认识到与已经是右结合的 $
的相似性之后。还有其他原因吗?
真的,原因可能只是它允许 <$>
和 <*>
共享一个优先级。我们绝对希望 <*>
是左结合的,所以像
这样的东西
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
(我说的更优雅,因为这里的计算链被解析为一个单一的功能管道,而不是命令式的“做这个,然后那个,然后......”)。多亏了函子定律,这可以用 <$>
和 .
与 $
和 .
.
相同的方式完成
您可能更喜欢 multiple-$
风格的唯一原因是它允许管道中的中缀表达式,也许最常见的例子是镜头更新(通常用翻转 &
, 但原理是一样的):
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]]
或在每个更新程序周围加上括号:
Prelude Control.Lens> (ix 1+~9) . (ix 2*~8) <$> [[4..8], [5..9]]
[[4,14,48,7,8],[5,15,56,8,9]]
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]
.
然而,所有应用程序运算符(包括 <<=
)的左结合性在我看来是一个糟糕的设计选择,尤其是在他们认识到与已经是右结合的 $
的相似性之后。还有其他原因吗?
真的,原因可能只是它允许 <$>
和 <*>
共享一个优先级。我们绝对希望 <*>
是左结合的,所以像
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
(我说的更优雅,因为这里的计算链被解析为一个单一的功能管道,而不是命令式的“做这个,然后那个,然后......”)。多亏了函子定律,这可以用 <$>
和 .
与 $
和 .
.
您可能更喜欢 multiple-$
风格的唯一原因是它允许管道中的中缀表达式,也许最常见的例子是镜头更新(通常用翻转 &
, 但原理是一样的):
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]]
或在每个更新程序周围加上括号:
Prelude Control.Lens> (ix 1+~9) . (ix 2*~8) <$> [[4..8], [5..9]]
[[4,14,48,7,8],[5,15,56,8,9]]