Start Learning Scala

还记得大三时候室友打印了一本厚厚的SICP来读,当时我还不以为意。不过读了Paul Graham《黑客与画家》后我也想亲自尝试一下,领略下Functional Programming(函数式编程)的威力。

于是选了Coursera上的函数式编程课程Functional Programming Principles in Scala,老师是欧洲名校EPFL(洛桑联邦理工学院)的教授,Scala语言的发明人Martin Odersky。目前我刚刚完成前两周的课程,这里从第一周的第一个Lecture里摘选一点内容,简单介绍一下函数式编程。


在说“函数式”之前,先要知道它是一种编程范式(Programming paradigm)

Wikipedia上指出:

编程范式是一类典型的编程风格,是指从事软件工程的一类典型的风格(可以对照方法学)。
编程范式提供了(同时决定了)程序员对程序执行的看法。

常见的编程范式有:

  • imperative programming(命令式编程)
  • functional programming(函数式编程)
  • logic programming(逻辑式编程)
  • object-oriented programming(面向对象编程)

Imperative Programming

即命令式编程,其特点是:

  • modifying mutable variables, (可修改变量)
  • using assignments(使用赋值操作)
  • and control structure such as if-then-else, loops, break, continue, return.(使用if-else、循环等控制结构)

通过与Von Neumann computer的指令序列比较可以更好地理解命令式编程:

Mutable variables     ≈    memory cells
variables dereferences   ≈    load instructions
variables assignments   ≈    store instructions
Control structures     ≈    jumps

命令式编程是很流行的编程范式,不过后来John Backus提出了纯命令式编程的"Von Neumann"瓶颈(见1977年图灵奖演讲):

One tends to conceptualize data structures word-by-word.

这样一来,我们需要其他技术来定义高级的抽象结构(如集合,多项式,几何图形等)。

因此,我们需要研究关于这些抽象结构的理论(theory)

What is a Theory?

我本科是学数学专业,对于理论这东西可不陌生,但是为了理解FP的由来,我们要注意这里Martin给出的定义

  • one or more data types(一种或更多数据类型)
  • operations on these types(定义在这些类型上的操作)
  • laws that describe the relationships between values and operations(描述这些类型的值与操作之间的法则)

写到这里想到了抽象代数(学渣表示全还给吴Sir了╮(╯▽╰)╭)。

还要注意的一点是:A theory does not describe mutations!

mutation的意思是说改变某个东西的值但又不改变其本身(表达不太清楚,我理解为改变一个变量x的值,但x这个变量的名字还是x)。

举一个栗子:

关于多项式的theory中,两个多项式的和可以定义为(a*x + b) + (c*x + d) = (a+c)*x + (b+d),但是没有定义在多项式本身不变的情况下改变多项式的系数。

这里我们应该注意到后者在命令式编程中随处可见,比如:

1
2
3
class Polynomial {double[] coefficient; }
Polynomial p = ...;
p.coefficient[0] = 42;

另一个关于string的例子:
定义字符串连接操作(concatenation operator)为++,则在理论上有(a ++ b) ++ c = a ++ (b ++ c)

但是理论没有定义改变一个序列元素时,仍保持其不变(大部分命令式语言中string使用数组表示)。这里提到了一个例外,Java’s strings are immutable.

用惯了命令式编程的人会觉得mutation理所应当,那么为什么理论中不允许其存在呢?Martin指出:

  • The theories do not admit it.(理论不允许)
  • Mutation can destroy useful laws in the theories.(mutation会破坏有用的理论)

因此,我们需要:

  • concentrate on defining theories for operators expressed as functions.
  • avoid mutations.
  • have powerful ways to abstract and compose functions.

于是我们有了函数式编程的概念。

Functional Programming

  • 严格来说,FP表示没有变量、赋值、循环或其他命令式控制结构的编程
  • 宽泛地说,FP表示关注函数本身的编程
  • 函数是一等公民(first-class citizens),可以有值(values that are produced, consumed and composed)
    • 函数可以在任何地方被定义,包括其他函数内部
    • 和其他类型的值一样,函数可以作为函数的参数和返回结果
    • 对于其他值,存在一组将它们构造成函数的操作
  • 上一条在其他语言中也可以实现,但是在函数式编程语言中更加容易和直观


Why Functional Programming?

经常有人去讨论语言的优劣,大家各执一词,你有你的好处,我有我的优点。编程语言有优劣吗?我认为是有的,但比起关注语言好坏,我更倾向于关注这种语言能帮我完成什么问题。与其争论优劣,不如讨论在不同情况下选用哪种编程语言更合适

所以,现在的问题是:我们为什么要学习FP

Martin指出:
FP is becoming increasingly popular, because it offers the following benefits.

  • simpler reasoning principles
  • better modularity
  • good for exploiting parallelism for multicore and cloud computing

Martin还给出了他在OSCON 2011上的talk(youtube):Working Hard to Keep it Simple。搜了一下发现youku上也有:Martin’s talk at OSCON 2011

视频里Martin提出了Scala在处理传统编程范式不擅长处理的并行(Parallel)并发(Concurrent)方面的优势。

他首先指出并发线程对可变状态进行操作可能会造成不确定性,进而带来问题,如:

1
2
3
4
var x = 0
async {x = x + 1}
async {x = x * 2}
// can give 0, 1, 2

为了得到确定性的处理结果,最好的方法就是禁止可变状态(avoid mutable state)

如同上面讨论过的,禁止可变状态 --> 函数式编程

目前Scala的应用领域:

  • Web platforms
  • Trading platforms
  • Financial modeling
  • Simulation

非常适合用来快速开发第一产品,之后可以进行扩展。(Fast to first product, scalable afterwards.)

接着是通过具体例子比较Scala和Java。

首先是语法上

in Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.ArrayList;
...
Person[] people;
Person[] minors;
Person[] adults;
{ ArrayList<Person> minorsList = new ArrayList<Person>();
ArrayList<Person> adultsList = new ArrayList<Person>();
for (int i = 0; i < people.length; ++i)
(people[i].age < 18 ? minorsList : adultsList)
.add(people[i]);
minors = minorsList.toArray(people);
adults = adultsList.toArray(people);
}

in Scala:

1
2
var people: Array[Person]
var (minors, adults) = people.par partition (_.age < 18)

Scala的简洁显而易见。

接着是处理并行(Going Parallel)

in Java: ???

in Scala:

1
2
var people: Array[Person]
var (minors, adults) = people.par partition (_.age < 18)

It’s so cool!

Martin后面还举了其他的几个例子,不列举了。

总之,作为一种函数式编程语言,Scala很有用也很酷。下面这些事实可能会让你更有动机来学习一门函数式编程语言:

看看哪些公司在使用Scala:

companies adopting scala

可能还是有人会说

  • 绝大部分问题OOP就能解决了啊
  • 现在招FP的公司毕竟还是很少
  • FP这么有用,为啥上大学不教?

好吧,对此我说一下我的想法。当今技术领域的改变本来就是极快的,那种“一招鲜,吃遍天”的想法在当今社会已经很难行得通了。一个程序员如果说“我是一个XXX程序员,我对XXX语言不感兴趣,也用不到”,那么他就把自己限定在了一个比较小的框架里,进步空间也就狭窄了很多。

作为程序员,我暂时没找到不对新技术产生兴趣的理由。所以,一起来学习函数式编程吧!