Haskell函数组成(。)和函数应用程序($)的成语:正确使用


129

我一直在阅读Real World Haskell,并且快要结束了,但是风格问题一直困扰着我与(.)and ($)运算符。

当您编写一个由其他功能组成的函数时,可以这样写:

f = g . h

但是,当您在这些函数的末尾应用某些内容时,我会这样写:

k = a $ b $ c $ value

但是这本书会这样写:

k = a . b . c $ value

现在,在我看来,它们在功能上是等效的,它们在我眼中的作用完全相同。但是,我看的越多,人们见到的书就越多,就像书中写的那样:(.)首先编写,然后最后才使用($)附加值来评估手数(没人会用很多美元组成) 。

是否有理由比使用所有($)符号更好地使用书籍方式?还是我没有得到一些最佳实践?还是它是多余的,我根本不必担心它?


4
请注意,第二个示例可以通过以下方式完成k = a $ b $ c value
Thomas Eding 2010年

1
是的,可以做到,正如Zifre在下面提到的那样,但是我决定将其保留在这里,因为它不会损害任何东西,并且可以使他(现在是您)的评论变得有意义。谢谢你 +1 :)
罗伯特·马赛奥利

2
另一个常见的方法是a . b $ c value,我不喜欢第三个示例,但是节省了一些字符。
约翰L

Answers:


153

我想我可以从权威那里回答这个问题。

有没有理由比使用所有($)符号好得多?

没有特别的原因。我和布莱恩(Bryan)都希望减少线路噪音。.比安静$。结果,这本书使用了f . g . h $ x语法。


3
好吧,我不能希望有比这个更好的答案了。来自一位作者本人。:)这很有意义,在我阅读时,它的确在页面上显得更安静。将其标记为答案,因为它直接回答了问题。感谢您的答复; 实际上,这本书。
罗伯特·马赛利

38
不要不同意作者的观点,但我认为在心理模型中还有另一个更突出的原因是创造某种东西而不是使用它。Haskell用户倾向于将其f.g.h视为一种新的聪明的创造f(g(h()))。现在,他们正在调用他们创建的新的,尽管匿名的功能,而不是像PHP用户那样链接大型的预制函数调用字典。
埃文·卡罗尔

1
这是一个有趣的说法:“降低线路噪音”和“比噪音小”。我从没想过用这种术语来编程语言,但这是很有意义的。
Rabarberski

53

它们确实是等效的:请记住,$操作员实际上什么也没做。f $ x评估为f x。其目的$是固定性行为:右联想和最小优先级。删除$并使用括号分组而不是使用前缀优先级,代码段如下所示:

k = a (b (c (value)))

k = (a . b . c) value

.版本相比,偏爱该版本的$原因与上述两者都偏于括号的原因相同:美观。

虽然,有些人可能会怀疑是否使用infix运算符而不是括号是基于某种潜意识的冲动,以避免与Lisp可能的任何相似(开玩笑...我想?)。


38

我想补充一点,在f . g $ xf . g是一个有意义的句法单位。

同时,中的f $ g $ xf $ g不是有意义的单位。A链$可以说是更为迫切- 第一个得到的结果gx然后f它,然后foo吧,然后

同时,.可以说一连串的声明更具声明性,并且在某种意义上更接近于以数据流为中心的视图-组成了一系列功能,并最终将它们应用于某些事物。


2
我正在学习Haskell(没有编写任何有意义的代码),但我喜欢将$视为一种“管道”运算符。它很像终端| 管道(数据流从右到左除外),但是像终端进程一样,每个单元都是明确独立的。
LostSalad 2014年

1
$操作似乎类似的行为将<|在F#操作。我相信实际上在F#(<|) a b = a b和Haskell中,两者的定义是相同的,a $ b = a b它们基本上是等效的。
汤姆·加尔文

@Quackmatic可以肯定的是它(<|) b a = a b在F#中,因为[1; 2; 3; 4; 5] <| List.map ((+) 1)如果有内存,它的用法就像。
BalinKingOfMoria恢复CM,2013年

@BalinKingOfMoria <|运算符将值传递给函数,而不是将函数传递给值-您的代码示例将与|>运算符一起使用。
汤姆·加尔文

@Quackmatic哦。不知道有多个版本(我不太使用F#)。
BalinKingOfMoria恢复CM,2013年

18

对我来说,我认为答案是(a)就像唐所说的那样整洁;(b)我发现在编辑代码时,我的函数可能会以无点样式结束,然后,我要做的就是删除最后一个,$而不是返回并更改所有内容。当然,这是次要的,但很好。


是的,这样做是一个很好的理由,如果您想以函数组合结尾,那么它会更快。:)是的,节省了击键,但更重要的是节省了时间。+1
罗伯特·马赛奥利

13

haskell-cafe线程对此问题进行了有趣的讨论。显然有这么认为的右联一个少数人的观点$“完全错误的”,并选择f . g . h $ xf $ g $ h $ x是侧步进问题的一种方式。


嘿,您找到了关于此事的完整讨论。太好了,我现在正在阅读。+1
罗伯特·马赛奥利


请注意,$!还具有“普通错误”的正确关联性- 为什么是$!运算符右关联?
imz-伊万·扎哈拉里舍夫(Ivan Zakharyaschev),2015年

3

这只是样式问题。但是,这本书对我来说更有意义。它组合了所有功能,然后将其应用于值。

您的方法看起来很奇怪,最后一个 $是不必要的。

但是,这并不重要。在Haskell中,通常有很多正确的方法可以完成同一件事。


3
我没有注意到最后的$是不必要的,但我应该有。谢谢,我将其保留在那里,以便人们知道此评论的含义。
罗伯特·马赛奥利

0

我意识到这是一个非常老的问题,但是我认为还有另一个未提及的原因。

如果您声明一个新的无点功能f . g . h,则传入的值将自动应用。但是,如果您编写 f $ g $ h,它将不起作用。

我认为作者之所以喜欢使用合成方法是因为它导致了构建功能的良好实践。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.