为什么某些函数式编程语言在函数应用程序中使用空格?


34

在看过一些用于函数式编程的语言之后,我总是想知道为什么某些fp语言会在功能应用程序(和定义)中使用一个或多个空格字符,而大多数(全部?)命令式/面向对象的语言都在使用括号,这似乎在是更数学的方式。我也认为后者的样式比没有括号的样式更清晰易读。

因此,如果我们有一个函数f(x)=x²,则有两种调用方法:

  • FP: f x

    例子:

    • ML,Ocaml,F#
    • 哈斯克尔
    • LISP,方案(以某种方式)
  • 非FP: f(x)

    例子:

    • 几乎所有命令式语言(我知道,请参阅评论/答案)
    • Erlang
    • Scala(也允许单个参数使用“运算符”)

“省略”括号的原因是什么?


9
为了使它更加混乱,呃,我的意思是简洁的

11
@Den如果您学习过haskell,则语法将是最令人困惑的事情之一:p
Simon Bergot,2014年

5
您确实意识到具有讽刺意味的说法,即使用括号会更数学,然后再使用幂函数作为示例?
约尔格W¯¯米塔格

12
@Den会因为语法而拒绝某种语言,从而危害自己。当然,学习xml或sql没问题(它们不是通用语言,但是它们定义了自己的语法)。
西蒙·贝尔格

7
我想指出,shell脚本(例如bash)通常不使用参数来调用命令。PowerShell的两全其美,因为函数是用括号声明的,而没有括号则被调用。
克里斯·哈珀

Answers:


59

这似乎是更数学的方式

功能语言受lambda演算的启发。在此字段中,括号不用于函数应用。

我也认为后者的样式比没有括号的样式更清晰易读。

易读性是情人眼中的情人。您不习惯阅读它。它有点像数学运算符。如果您了解关联性,则只需几个括号即可阐明您的表达式的结构。通常,您不需要它们。

咖喱也是使用此约定的一个很好的理由。在haskell中,可以定义以下内容:

add :: Int -> Int -> Int
add x y = x + y

x = add 5 6 -- x == 11
f = add 5
y = f 6 -- y == 11
z = ((add 5) 6) -- explicit parentheses; z == 11

使用parens,可以使用两种约定:(f(5, 6)非咖喱)或f(5)(6)(咖喱)。haskell语法有助于习惯于currying概念。您仍然可以使用非咖喱版本,但与组合器一起使用会更痛苦

add' :: (Int, Int) -> Int
add' (x, y) = x + y
u = add'(5, 6) -- just like other languages
l = [1, 2, 3]
l1 = map (add 5) l -- [6, 7, 8]
l2 = map (\x -> add'(5, x)) l -- like other languages

请注意,第二个版本如何迫使您将x注册为变量,并且子表达式是一个接受整数并将其加5的函数?咖喱版本更轻巧,但也被许多人认为更易读。

Haskell程序大量使用部分应用程序和组合器作为定义和组合抽象的手段,因此,这不是一个玩具示例。一个好的功能界面将是一个参数顺序提供友好的咖喱用法的界面。

还有一点:没有参数的函数应该用调用f()。在haskell中,由于仅操作不可变的惰性求值,因此您只需编写f,并将其视为需要在必要时执行一些计算的值。由于其评估不会有任何副作用,因此对无参数函数及其返回值使用不同的表示法是没有意义的。

功能应用程序还有其他约定:

  • Lisp:(fx)-带外部括号的前缀
  • Forth:XF-后缀

7
次要小问题:术语是“咖喱”和“咖喱”,而不是“咖喱”和“咖喱”。
Doval 2014年

“非咖喱”在haskell中什么是非咖喱函数?
2014年

3
@ user1737909一个以元组为参数的函数。
2014年

1
@Doval实际上应该是“schönfinkeling”和“schönfinkeled”。但是,哦,历史总是回顾性地确定赢家。
Profpatsch 2014年

考虑带括号的循环语法,默认情况下,诸如f(a, ,b)add(a, )add(, b)看起来更灵活。
菲涅尔

25

基本思想是使最重要的操作(功能应用程序)最容易阅读和最容易编写。空格不易读取且易于键入。

请注意,这并非特定于功能语言,例如,在Smalltalk中,这是最早的OO语言和受其启发的语言之一(SelfNewspeak和Objective-C),在Io和受其启发的语言中也是如此(Ioke,Seph),空格用于方法调用。

Smalltalk风格:

anArray sort
anArray add: 2
aString replace: "a" with: "b"

(在后一种情况下,方法的名称为replace:with:。)

IO风格:

anArray sort
anArray add(2)
aString replace("a", "b")

Scala还允许空白用于方法调用:

foo bar(baz)

如果只有一个参数,则省略括号:

foo bar baz

Ruby还允许您省略括号:

an_array.sort
an_array.add 2
a_string.replace "a", "b"

使用括号,这似乎是更数学的方法

并不是的:

f(x)
sin x
x²
|x|
x!
x + y
xy
½

长期以来,数学符号的发展一直以极其不一致的方式发展。

如果有的话,函数语言从λ演算中获得灵感,其中使用空格来编写函数应用程序。


3
还有一种论证编辑的经济性。特别是,功能语言通常直接支持组合。在函数周围加上括号而不是参数是有意义的。您只需要“一次”就可以做到:(e.f.g)x而不是e(f(g(x)))。而且,至少,Haskell不会阻止一个人执行f(x)而不是f x。这不是习惯用法。
nomen 2014年

18

函数应用程序的括号只是Euler给我们留下的众多障碍之一。像其他任何事物一样,当有几种方法可以做某事时,数学也需要惯例。如果您的数学教育只延伸到大学的非数学学科,那么您可能对许多没有这些胡说八道的函数应用(包括微分几何,抽象代数)感到高兴的领域不太熟悉。而且,您甚至都不会提到应用了infix(几乎所有语言),“ outfix”(例如采用规范)或以图表形式(Befunge,代数拓扑)的函数。

因此,在回答这个问题时,我想说这是由于更多的函数式程序员和语言设计师接受了广泛的数学教育。冯·诺依曼(Von Neumann)说:“年轻人,在数学中您不了解事物。您只是习惯它们。”,当然这是正确的表示法。


7
甚至不要让我开始f(x)sin x|x|x!x + yxy½对的总和与不可分割的比较...
约尔格W¯¯米塔格

2
+1冯·诺伊曼(von Neuman)报价。剩下的答案是+1,但我不能给出+2。:(
pvorb 2014年

2
我在这里闻到一些精英主义的气息。我对“功能编程语言设计师的教育程度较高”持中立态度。
CaptainCodeman 2014年

7
@CaptainCodeman他说受过数学教育,我同意这一说法。至少在谈到Haskell时,您会注意到,这两个概念本质上都是更数学的,社区也更趋于数学。他们不是更聪明,只是在数学方面受过更好的教育。
保罗

5
@CaptainCodeman不,因为他从不暗示他们一般受过高等教育,只是数学方面受过教育。他就是这么说的:“广泛的数学教育”。:)
保罗

8

尽管Simon的回答有很多道理,但我认为还有更多实际原因。由于函数链和组成,函数式编程的性质往往比命令式编程产生更多的括号。那些链接和组成的模式通常也恰好能够被清楚地表示而没有括号。

最重要的是,所有这些括号都会让人讨厌阅读和跟踪。这可能是LISP不受欢迎的第一原因。因此,如果您可以在没有多余括号的情况下获得函数式编程的强大功能,那么我认为语言设计人员将倾向于这样做。毕竟,语言设计师也是语言用户。

甚至Scala允许程序员在某些情况下省略括号,而在使用功能样式进行编程时,括号经常会出现,因此您可以同时兼顾两者。例如:

val message = line split "," map (_.toByte)

末尾的parens对于关联性是必需的,其余的(以及圆点)则保留。用这种方式编写的代码强调您正在执行的操作的连锁性质。命令程序员可能不熟悉它,但是对于函数式程序员来说,用这种方式编写非常自然且流畅,而不必停止并插入对程序没有任何意义的语法,而只是为了使编译器满意而已。 。


2
“最重要的是,所有这些括号都会让人讨厌阅读和跟踪。这可能是LISP不受欢迎的第一原因。”:我不认为括号是Lisp不受欢迎的原因:我不认为它们特别难于阅读,我发现其他语言的语法更加尴尬。
Giorgio

2
LISP语法具有超高的正交度,在很大程度上弥补了令人讨厌的括号。那只是意味着它还有其他可赎回的功能,使其仍然可以使用,而不是parren并不令人讨厌。我知道不是每个人都有这种感觉,但是我遇到的绝大多数程序员都这么认为。
Karl Bielefeldt

我最喜欢的LISP反义词是“迷糊括号”。计算机还可以使用人性化的方式来指定代码块并删除冗余,例如Wisp之类的语言。
Cees Timmerman

4

两个前提都是错误的。

  • 这些功能语言为功能应用程序占用空间。他们所做的只是简单地解析函数后的任何表达式作为参数。

    GHCi> f 3
    4
    GHCi> f(3)
    4
    GHCi>(f)3
    4

    当然,如果您既不使用空格也不使用parens,则通常无法正常工作,这仅仅是因为表达式未正确标记

    GHCi> f3

    <‌interactive>:7:1:
        不在范围内:“ f3”
        也许您是指“ f”(第2行)

    但是,如果将这些语言限制为1个字符的变量名,那么它也将起作用。

  • 数学惯例通常也不需要在函数应用中使用括号。数学家不仅会经常写sin x-对于线性函数(通常称为线性运算符),写参数时不带括号也很常见,这可能是因为(与函数编程中的任何函数一样)线性函数通常在不直接提供的情况下进行处理一个争论。

因此,实际上,您的问题可以归结为:为什么某些语言在函数应用中需要括号?好吧,显然,像您一样,有些人认为这更具可读性。我对此表示强烈反对:一对parens很不错,但是一旦您将其中两个嵌套在一起,就会开始感到困惑。Lisp代码很好地演示了这一点,如果您到处都需要parens,那么几乎所有功能语言都看起来很混乱。在命令式语言中,这种情况不会发生太多,主要是因为这些语言的表达能力不足以一开始就编写简洁明了的单行代码。


知道了,这个问题并不完美。:)因此得出的结论是:“括号无法缩放”。
pvorb 2014年

2
也不是非功能性的语言的是一致在需要括号; 例如,Smalltalk具有object method: args,Objective C具有[object method: args],Ruby和Perl都允许省略函数和方法调用中的括号,仅要求它们解决歧义。
hobbs 2014年

1

一个小的历史澄清:数学的原始符号 没有括号

符号FX的任意函数X是由约翰·伯努利引入的1700年左右后不久,莱布尼茨开始的聊天功能(实际上伯努利写道φX )。但在此之前已经在那里使用像罪恶的符号,很多人XLX(对数X)为特定的功能X,没有括号。我怀疑这就是今天许多人仍然写sin x而不是sin (x)的原因

伯纳利(Bernoulli)的学生欧拉(Euler)收养了伯努利(Bernoulli)的x,并将希腊字母改为拉丁字母,写为fx。后来他注意到将f混淆为“数量” 可能很容易,并且认为fx是一个乘积,因此他开始写f:x。因此,符号f(x)并非归因于欧拉。当然,出于与在函数式编程语言中仍需要括号的相同原因,Euler需要使用括号:例如将(fx)+ yf(x + y)区分开。我怀疑在Euler之后采用括号作为默认值的人,因为他们主要看到Euler编写了诸如f(ax + b)的复合表达式。他很少写fx

有关此内容的更多信息,请参见:欧拉(Euler)是否曾经写过带括号的f(x)?

我不知道函数式编程语言的设计者或lambda演算的发明者是否意识到其表示法的历史根源。我个人觉得没有括号的表示法更自然。

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.