Scala是一种函数式编程语言吗?

21

我曾经学习Java编程语言,然后每年尝试学习一种新的编程语言,第二个是C++,然后是Python。当我打算学下一个编程语言时,我想找点新东西,于是选择了Scala,因为它与Java兼容,并且可以过渡到面向对象编程到函数式编程。

学习新的范例、新的风格和新的思考方式非常酷。只是阅读关于优雅的Scala概念的文章已经是很棒的体验了,而在Scala上编码则更好。

在阅读了很多文章之后,我遇到了这篇批评Scala的文章:

Scala不是函数式编程语言。它是一种具有闭包的静态类型的面向对象编程语言。

阅读完这篇文章后,我有些疑虑。我非常喜欢Scala,并开始更多地使用Scala编写代码,但Scala是否符合函数式编程的定义呢?那篇文章说的是真话还是在误导读者?我必须学习Haskell或其他函数式编程语言才能真正体验FP吗?

更新:期望得到合理的回答,有良好的例子,不引起争议。


6
请移除与问题无关的个人观点。这会浪费我们宝贵的时间,本应该用来解决您的问题。请注意不要改变原文的意思。 - the_drow
1
相关链接:https://dev59.com/F2865IYBdhLWcg3wIrJg#3962690 和 https://dev59.com/IXA75IYBdhLWcg3wAD_x#3528329。 - missingfaktor
4
“面向对象语言鼓励过度使用状态和继承。”“函数式语言鼓励过度使用高阶编程。” - oluies
1
这个问题很难回答,因为对于什么是函数式语言并没有明确的定义。在Scala中,你可以以函数式风格进行编程。但这是否意味着它是函数式的呢? - augustss
3个回答

39

Scala并不强制要求您使用函数式风格。以下代码是完全有效的Scala示例:

var i = 1
while (i < 10) {
  println("I like side effects, this is number "+i)
  i += 1
}
case class C(var i: Int, var set: Boolean = false)
def setMe(c: C) = { if (!c.set) { c.set = true; c.i += 1 }; c }
setMe(C(5))

因此,Scala在这个意义上可不是函数式的!它充满了副作用和可变状态——你在Java中能做的所有事情都可以在Scala中做到。

尽管如此,Scala允许你以函数式风格编码,并在许多方面(比Java更)简化了你的生活:

  • 有一流函数
  • 有不可变集合库
  • 支持尾递归(只要JVM能够管理得了)
  • 支持模式匹配
  • (等等)

这看起来更像是函数式的:

for (i <- 1 to 10) println("Sometimes side effects are a necessary evil; this is number"+i)
case class C(i: Int, set: Boolean = false)
def setIt(c: C, f: Int=>Int) = C(f(c.i), true)
setIt(C(5), _+1)

值得注意的是,那篇特定文章的作者似乎对Scala了解甚少;几乎每个在他手中看起来丑陋的例子都是不必要的。例如,他写道:

def x(a: Int, b: Int) = a + b
def y = Function.curried(x _)(1)

但这不是很糟糕,只要你注意自己在做什么:

def x(a: Int)(b: Int) = a + b
val y = x(1) _

总之,Scala不是一种纯函数式编程语言,因此它的语法并不总是理想的函数式编程,因为还有其他考虑因素。然而,它几乎拥有所有标准的函数式编程语言特性。



19

我对功能语言的个人检验是查奇数。

Scheme示例:

(define (thrice f)
    (lambda (x)
        (f (f (f x))))))

((thrice 1+) 0)
  => 3

(1+是一个Scheme函数,它将其参数加1。 thrice接受一个函数f,并返回一个将f与自身组合三次的函数。因此,(thrice 1+)会将其参数加三。)

((thrice (thrice 1+)) 0)
  => 9

(由于(thrice 1+)是一个将三个数相加的函数,对其进行三次操作得到一个将九个数相加的函数。)

还有我最喜欢的:

(((thrice thrice) 1+) 0)
  => 27

这个最后的例子留给读者自己推理(思考)。它是最重要的例子。

如果你无法用你的编程语言轻松写出这个例子,只能通过扭曲代码实现,那么我认为这不是一种函数式语言(例如:C/C++)。

如果你可以用你的编程语言写出这个例子,但是它看起来非常不自然,那么我认为你的语言“支持函数式编程”,但并不是真正的函数式语言(例如:Perl)。

如果这个例子能够在你的编程语言中顺利地移植,并且实际上看起来与你日常使用它的方式并没有太大区别,那么它就是一种函数式语言。

我不了解Scala。有人想告诉我它属于哪一类吗? :-)


9
def thrice[A](f: A => A) = f andThen f andThen f:定义了一个函数 thrice,该函数接收一个类型为 A 的参数 f,并将 f 连续应用三次,返回一个经过三次应用 f 后的结果。thrice[Int](1+)(0) // gives 3:对 1+ 函数进行三次应用,并将初始值设为 0,结果为 3thrice(thrice[Int](1+))(0) // gives 9:先对 1+ 函数进行三次应用得到一个新的函数,然后对该函数再进行三次应用,并将初始值设为 0,结果为 9thrice(thrice[Int])(1+)(0) // gives 27:先将 thrice 函数应用两次得到一个新的函数,然后将 1+ 函数连续应用三次,并将初始值设为 0,结果为 27 - Rex Kerr
2
(顺便说一句,这看起来很不自然,因为这是一种可怕的低效方法来创建数字27,并且应用后继操作“1+”并不是我们大多数人进行算术运算的方式。 (而且,就此而言,三倍函数也不是特别有用,因为有Iterator.iterate可以任意深度地应用函数。) - Rex Kerr
1
@Rex:好吧,你也不会像这样在Scheme中添加27,但这不是重点。这实际上是Alonzo Church在原始的lambda演算中表示自然数的方式。(当你只有“lambda”时,你的选择是有限的。)我所谓的“三倍”,他称之为“3”;就像实际的整数3一样。能够编写一个可以应用于自身的“三倍”函数是我认为测试语言“功能性”的很好的方法。 - Nemo
1
@Hyenk:嗯,我想对大多数Perl程序员来说,线路噪声看起来很“自然” :-) - Nemo
1
@Nemo:美在观者的眼中。我可以同样地说Scheme也是如此。对我而言,Thrice(\&Thrice)->(\&Inc)->(0)(((thrice thrice) 1+) 0)更易于理解,更明确地表达了正在发生的事情。 - Hynek -Pichi- Vychodil
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接