我对函数式编程感兴趣,因此决定与Haskell面对面。我的头很痛...但是我最终会得到...虽然我有一个好奇心,为什么语法如此神秘(缺少另一个词)?
是否有一个原因为什么它是不是更有表现力,更接近人类语言?
我知道FP擅长对数学概念进行建模,并且借用了一些简洁的表达方式,但它仍然不是数学……这是一种语言。
我对函数式编程感兴趣,因此决定与Haskell面对面。我的头很痛...但是我最终会得到...虽然我有一个好奇心,为什么语法如此神秘(缺少另一个词)?
是否有一个原因为什么它是不是更有表现力,更接近人类语言?
我知道FP擅长对数学概念进行建模,并且借用了一些简洁的表达方式,但它仍然不是数学……这是一种语言。
Answers:
诸如Haskell及其前身Miranda之类的功能语言源于lambda微积分的数学概念。在Wikipedia页面上:
Lambda演算(也称为λ演算或称为“ lambda演算”)是一种数学逻辑形式系统,用于通过变量绑定和替换来表示计算。
...
Lambda演算在编程语言理论的发展中发挥了重要作用。在计算机科学中,与lambda微积分最杰出的对等是函数编程语言,它实质上实现了微积分(增加了一些常量和数据类型)。除了编程语言外,lambda演算在证明论中也有许多应用。一个主要的例子是Curry-Howard对应关系,它给出了不同类型的Lambda微积分系统和形式逻辑系统之间的对应关系。
由于这个历史,这些功能语言(以及更命令式语言的功能元素)的语法受到lambda微积分所使用的数学符号的强烈影响。
数学符号和计算机语言都可以描述需求的精度与计算机要求编写其指令的精度相互关联良好。但是,自然语言的不精确性为将其用于计算机编程创造了巨大的障碍。即使是(可以说)最成功的自然语言编程环境,例如Wolfram Alpha,也需要大量的领域经验才能有效地使用。
Haskell语法是接近人类的语言。它经过专门设计,类似于数学符号,这是人类设计的一种语言(因此为人类语言),用于精确表达Haskell所基于的那些概念。
您可以向任何数学家或逻辑学家展示一段Haskell代码,即使他从未听说过Haskell并且对编程,计算机科学甚至计算机一窍不通,他也将能够理解。
另一方面,您可以向任何数学家或逻辑学家展示一段这样的代码:
x = x + 1
他会心乱如麻,他说:“这没有任何意义,也没有x
这样x
等于x
加1
。”
一些特定的符号是否为“神秘的”,这是意见和熟悉的问题。
where
允许凝结的要点一行中的一项功能。
x = infinity
确实可以解决方程式。
我认为您在学习函数式编程时可能也会遇到的一件事,那就是,与使用命令式编程相比,使用函数式编程,您可以(并且几乎必须)在更高的层次上进行思考/工作。
您发现的表现力较低,但我认为实际上更具表现力:您不必拼出每个小细节,并且可以在函数式编程中用更少的代码来做更多的事情-编写的内容具有更大的力量。
例如,我可以命令性地写:
for each (Person person in people)
print(person.name)
用英语完全可以读懂。
Haskell版本可能是(并且这不是有效的Haskell,但仅用于语法比较):
map (print . name) people
这需要更少的代码和更少的细节处理-我不必将事情分解成一个循环及其变量(for each (...)
),该map
函数将为我解决这个问题。
在该级别上工作可能需要一些时间来适应。如果有帮助的话,Haskell可能是自开始编程以来学习新语言最困难的时期,而且我知道> 10种语言(包括Lisp)。不过,这完全值得学习。
目前,我目前正与Scala保持联系,因此我可以表示同情。至少在Scala中,当事情变得太艰难时,我可以退回到命令式风格。
我认为这里有多种作用:
函数式语言的设计师必须具有强大的数学背景,因此他们借用了自然的数学符号。
任何一种语言的更高的表现力(即陈述的能力,而不是接近英语的能力)都会给学习曲线的陡峭度带来相应的代价(IMO)。Terseness是Scala中备受推崇的品质,很多东西都是可选的。
函数式语言在做事上有很大不同,因此基本操作不会映射到命令式构造上。
如果我正确理解了您的最后一条评论,那么您的问题是,为什么Haskell经常在可能同时使用字母数字关键字(或函数名称)的地方使用符号运算符?
关于关键字,一个答案就是关键字阻止您将某些单词用作变量名。例如,当我想调用变量from
并to
使用其中一种是关键字的语言时,总是很烦恼,例如Python。
通常,符号运算符的另一个原因是简洁。这实际上不适用于您的列表理解的具体示例(<-
不比短in
),但是在许多情况下确实如此。
另一个原因是可读性。这似乎是违反直觉的,因为符号运算符通常较难学习,因为它们的“名称”并不能真正告诉您它们的功能(而函数名称通常会告诉您),但是一旦您知道给定运算符的功能,与具有大量字母数字前缀函数调用的表达式相比,具有infix符号运算符的表达式对于人类而言通常更易于解析,因为这样可以从视觉上将运算符与操作数区分开来。例如,大多数人会同意,2 * 3 + 4
它比add (mult 2 3) 4
或更容易阅读add(mult(2, 3), 4)
。
我认为Haskell在不引入疯狂语法的情况下变得与人类语言相似。这确实非常有用,例如,使用了where
推迟定义的子句和列表理解语法,这正是我要在黑板上写的内容。它还避免使用大括号等多余的字符,并且可以自由使用缩进。典型示例是quicksort:
qsort [] = []
qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
where lesser = [x | x <- xs, x <= p]
greater = [x | x <- xs, x > p]
诚然,这是一个简单的示例,但是我不知道任何其他语言可以使它像现在这样可读。
在Haskell中阅读更复杂的内容可能变得很难理解,但这与类型系统,受约束的IO有关,而与语法无关。
如果有的话,我想说Haskell牺牲了句法简单性以适应与人类语言更相似的语法。类似方案这样的语言的语法与人类编写的语法相去甚远,但是解释其规则确实非常简单。
++ [p] ++
吗?
我认为差异与函数/声明式编程对问题的陈述方式有关,就像数学问题一样,其中命令式编程描述了一个过程。在商业世界中,计算机程序包装或替换了物理过程,因此您可能会发现一种反映这种情况的语言,使其更加直观。
事实证明,该功能样式适合安全使用多个处理器(因为它最大程度地减少了可变性和副作用)-我们将来会看到更多。同样,大多数现代函数式语言都有带有迭代器的集合,您可以将函数传递给迭代器。这些方法可以非常有效地将其工作负载分散到多个处理器上,而您甚至根本不知道。