一直以来,我有一个观点:随着软件行业的逐渐成熟,算法将慢慢不再成为软件行业工作者必备的知识(但一定是信息学相关专业必学的知识)。因为算法将被越来越多地封装和直接使用。这就好比数据库,近乎是每一个软件项目都必备的需求,但近乎每一个软件项目都不会选择重新实现一个数据库。有太多现成的数据库工具唾手可得,我们只需要学会怎么使用它们就好了。而怎么使用它们,比重新实现一个数据库,简单不止 100 倍。
事实上,计算机行业的任何一个细分领域,都在上演着这样的 “进化过程”:很多在以前看来是必学的知识,从现代开发的角度看,不一定是必备的。所以,大家会觉得面试造核弹,上班拧螺丝。
如果你曾在十五年前,就尝试做一个网页;做一个桌面 app(那时还根本没有移动 app);做一个游戏;你就会明白我在说什么。一个非常显然的事实是:在十五年前,做一个网页,做一个桌面 app,做一个游戏,相应的成本都是现在的 100 倍以上,而最终得到的成果,搞不好不足现在的百分之一。这一万倍的差距,不是因为今天的我们更聪明了,而是因为行业在发展。今天的我们,不仅仅是 “站在巨人的肩膀上”,更是 “站在了几百万个巨人的肩膀上”。
来源:brainyquote
所以,我经常提醒自己,我能实现这么多复杂的功能,不是因为我有多聪明,而是因为编程现如今竟是如此简单。任何一个技术,如果能被大规模地应用,除了它足够有用之外,还必须具备一个必要的因素 —— 它足够简单。计算机技术在我们这个时代被越来越多、越来越广泛地使用,是因为计算机技术越来越简单。
在这篇文章中,我想从计算机语言的角度,聊聊编程语言的 “演化”。这本身是一个很大的话题,甚至有很多学者做专门的相关研究。因此,这篇文章的讨论角度也是极其有限的。大家通过文章标题就可以看出来,这篇文章将聊到 DSL 为止。但除了 DSL,我们确实还有很多维度去探讨这个话题。以后有机会,我会再逐渐补充我的观点:)
1. 从没有分号,到给人看的编程语言
学习计算机的同学一定了解:分号在计算机编程语言中,有着举足轻重的地位。如果你学习的第一门语言是 C,或是 C++,或是 Java,那么你一定曾经犯过忘记写分号的错误。
图片来源:亚马逊
但是,如果多接触几门编程语言,就会发现:大多数 “现代” 编程语言,是完全不需要写分号的。比如 Python,比如 Swift,比如 Go。为什么?原因很简单,在大多数情况下,分号是给机器看的,而不是给人看的。写分号,本质是我们人类在迁就编译器。毕竟,机器是很傻的。
有意思的是,即使是同样一定需要分号的语言,Java 也比更底层的 C++ 需要的分号少。比如,下面的例子中,我们同样声明一个学生类。
C++ 语言:
class Student{
public:
string name;
}; // 注意,在 C++ 语言中,这里必须有一个分号。
Java 语言:
public class Student{
public String name;
} // 注意,对于 Java 语言,这里不需要分号!
可以看到,在类声明结束的时候,C++ 语言这个老古董,还需要加上一个分号,但是 Java 语言不需要。显然,Java 语言的语法规则更加合理。在这里,大括号的结束已经完全可以表示类声明的结束了,多一个分号,对人类来说,是一个额外的 “语法负担”。
随着编程语言的逐渐演化,这样的语法负担都将被剔除。如果你学习过 Python 语言,就会知道,何止是分号消失了,连大括号都消失了。为什么? 就是因为大括号也是给机器看的。告诉机器,类声明结束了。从 Python 的角度看,“旧式语言” 有太多的语法规则,使用了过多的字符,来告诉机器如何解析程序,比如分号,比如大括号。这使得这些语言猛地看上去,包含一大堆和程序本身所表达的逻辑完全无关的字符。所以,Python 在努力减少这样的语法。
但与此同时,Python 在另一些语法中,却在添加更多的字符。最典型的就是三目运算符 “?:”,在 Python 中没有了。为什么?三目运算符虽然形式更简单,但这个 “简单的形式” 也是给机器看的,不是给人看的。
比如我们要根据 score 这样一个变量的值,返回 “Perfect” 或者 “Not Perfect” 这样的字符串,在 C++ 或者 Java 这样的语言中,我们写出的逻辑大概是这样的:
return score == 100 ? "Perfect" : "Not Perfect";
但是,在 Python 语言中,是这样的:
return "Perfect" if score == 100 else "Not Perfect"
显然,三目运算符比 if-else 的形式更简洁,但在逻辑表意上,更加复杂,不够直观。而 Python 的写法,更接近自然语言,任何人,即使没有计算机背景,也能一眼看懂这句话的意思。所以,三目运算符被 Python 直接摒弃了。类似的,++, – 这类所谓更 “简洁” 的语法,也被 Python 摒弃了,也是这个原因。
这类语法的弃用,绝不仅仅是 Python 一门语言的选择,比如苹果几年前刚发布的新语言 Swift,也是如此。编程语言的发展趋势之一,就是越来越向着给人看的方向发展,而不是给机器看。
2. 去除和逻辑无关的语法
编程语言的另一个发展趋势,就是摒弃和业务逻辑无关的语法规则。 最典型的例子,就是现代大多数语言都不再有显示声明指针的语法。
相信对于大多数同学,如果学习过 C 或者 C++ 语言,在学习 int*, int**, int&, 等等这些语法规则的时候,都是一团浆糊。甚至可能很多同学毕了业,都没有特别搞清楚 C/C++ 语言中的指针和引用到底是怎么回事。
相信对于绝大多数同学(至少对于我是如此),在刚接触 C 语言的时候,连使用 scanf,都是一场噩梦。
int a;
scanf("%d", &a);
// 我们先不提 %d 是怎么回事,a 前面为什么一定要有个&????
// 但是,如果a是一个char[]的话...
char s[80];
scanf("%s", s);
// 咦?怎么s前又不需要&了?
这段代码中,这个 & (C/C++ 中是地址符),就是和业务逻辑无关的语法规则。在这里,我不详细的介绍 C/C++ 中的这个语法规则了。其实,如果你不使用 C/C++,对于现代编程语言,你完全不需要理解这其中有什么区别,在什么时候应该使用哪个语法。
相较而言,比如 Java 语言的控制台输入,是使用 Scanner 类,写出来大概是这样的。
Scanner myScanner = new Scanner(System.in);
int a = myScanner.nextInt();
String s = myScanner.nextLine();
同样,对于 C++ 语言,写出来大概是这样的:
int a;
cin >> a;
string s;
cin >> s;
虽然,对于 Java 和 C++ 的 IO 部分的语法设计(或者说类设计),还是有很多吐槽和更好的建议,但很明显,高级语言在努力摒弃掉诸如 % d, % s, &, 等等语法规则。因为,这些语法规则和具体逻辑无关。
对于指针这个概念同理。对于大多数高级语言,和指针相关的语法都被隐藏起来了。当然,现阶段,对于程序员,还是必须要理解清楚指针这个概念的(在 Java,Python 等语言中,其实就是引用)。但是,从语法的角度,这个概念 “消失” 了。这显然对初学者更友好。初学者不需要纠结,什么时候用 *,什么时候用 &。语言的使用者,将更多地精力,集中在逻辑表达上,而非语法细节上。
3. 自动垃圾回收
说到剔除逻辑无关的规则,最为典型的,就是现代语言在大多数情况下,不再需要程序编写者处理垃圾回收相关的逻辑了。很多语言,即使本来需要手动处理垃圾回收逻辑,在版本升级的过程中,也改为了自动垃圾回收机制。这也是因为,垃圾回收是和我们要表达的逻辑无关,是关于机器怎么执行的逻辑。这里,最典型的例子,就是 OC 语言。
我是 iOS 4 时代开始接触 iOS 开发的。那个时候,OC 语言是需要进行手动垃圾回收的(和 C/C++ 语言一样)。但是从 iOS 5 开始,OC 语言引入了自动垃圾回收机制,被称为 ARC。虽然严格意义上,ARC 和 Java 的 GC 还不一样(这是因为这两种语言本身的内存管理模型有所不同),但是他们的目的都是相同的:让程序员更多地专注于业务逻辑代码的编写,而不是专注于垃圾回收这样只有机器才关注的问题。 如果你尝试使用过需要手动处理垃圾回收机制的语言编写较大的项目,如 C/C++/OC,就会明白:你很有可能会花费 50% 的时间,来保证整个系统的垃圾回收是没有问题的。但是,使用 “现代” 编程语言,这些时间都可以用来精进你所要实际表达的代码逻辑。
对于现代编程语言,自动垃圾回收机制,近乎是标配。这也使得越来越多的程序员根本不了解内存管理,也不需要了解内存管理,就可以胜任大多数工作。在这方面,我一直喜欢举发生在我身边的一个 iOS 程序员的例子。我的这位朋友,文科生出身,在 iOS 5 的时代开始接触 iOS 开发。他接触 iOS 开发,完全是因为在那个时代对苹果产品的疯狂着迷,爱屋及乌,也想要开发属于自己的 iOS App。结果,竟然在那个大多数程序员都还不太接触 iOS 开发的年代,他无意中转行进入了 IT 行业,并且抓住了风口,在短短一年的时间里,成为了国内某著名品牌的 iOS 事业部负责人。在那个年代,大多数硕士毕业的研究生,在大厂的工资也就是 10 万每年。他一个文科生,本科学历转行计算机,借助 iOS 开发的契机,竟在那时就达到了 30 万每年的薪水。
对于这个案例,除了佩服他的兴趣,感叹时代和机遇的力量,以及佩服他的执着和努力之外,我也常常想:或许,这和 OC 语言本身在 iOS 5 开始,开发者不再需要处理内存管理,也是分不开的。否则,对于文科生来说,理解内存管理,学习曲线真的太陡峭了。
苹果每年都会在自己的发布会上声称,iOS 开发生态中,有多么多么小的开发者,或者多么多么老的开发者。是因为这个年代的人们突然都是天才了吗?不是。因为开发真的越来越简单。
九岁的苹果开发者 Anvitha Vijay
4. DSL
上面举的例子,不论是分号、大括号、三目运算符、指针、地址符、垃圾回收,等等等等,这些功能在编程语言中的演化,本质其实都是:编程语言在逐渐剔除和业务逻辑无关的语法,从而让开发者更多地关注在业务逻辑自身上,而不是机器怎么执行这些逻辑上。简单的总结,可以理解成:现代语言的发展趋势是:编程的主要任务越来越多的是告诉机器要做什么(what),而不是怎么做(how)。
正是因为这个方向的指引,越来越多的 DSL 语言被发展出来。
DSL,是 Domain Specific Language 的缩写,翻译成中文,就是 “特定领域语言”。 其实,对于 DSL 语言,我们都不陌生,最典型的 DSL,就是 SQL。大家体会一下下面的这段 SQL 代码:
SELECT name FROM Student WHERE score = 100
大家想想,这段代码所表示的逻辑,用其他语言怎么写?在大多数语言中,大概是这样的:(伪码)
names = []
for student in students:
if student.score == 100:
names.append(student.name)
return names
看了这两段代码,不知道大家是不是能够理解,什么叫告诉机器 what,而不是 how。对于 SQL 语言来说,我们的代码近乎就是用自然语言说:我们要把分数是 100 分的学生姓名拿出来。而对于其他大多数语言(C++, Java 等等),我们还需要循环 (for),需要条件判断 ( if ),需要考虑拿回来的学生姓名怎么存储(数组?集合?),还要手动把一个一个符合条件的学生姓名添加进去(append)。显然,这段代码更多的涉及 how,而不仅仅是 what。
当然了,SQL 语言这种 “简洁性” 是有代价的,这个代价就是 DSL 中所谓的 Domain Specific, 即领域相关。使用 SQL,我们只能处理和数据存储相关的内容(通常所说的增删改查)。但我们不能用 SQL 做移动 app;不能用 SQL 做后端业务逻辑;不能用 SQL 做前端;也不能用 SQL 做动画、做游戏、做人工智能。但是,只要是和存储相关的东西,掌握 SQL 这么一个简单的工具就够了。
DSL 还有很多,在现代的环境下,近乎每一个程序员都一定会接触那么几个 DSL。再比如说,正则表达式也是一种 DSL,正则表达式只可以进行模式匹配。但是,对于模式匹配,再复杂,使用正则表达式解决,也比自己写模式匹配算法或者模式匹配工具要强。
HTML 和 CSS 也属于 DSL,虽然,这两种语言本质上处理的不是逻辑(CSS 越来越强大,也已经有了 “逻辑引擎”),但是,如果学习过 HTML 和 CSS 的同学一定了解,这两种语言都是上手极快的(当然,上手和精通是两回事儿)。我们可以很快地就使用这两种语言来做出页面,而且看上去还不错。这就是 DSL 的意义 —— 可以更快地,更简洁地,去让哪怕是 “计算机科学的外行”,用编程的方式,去做特定领域的事情,而不用去纠结过多 “计算机科学” 相关的内容。
在这个年代,做科研工作,近乎一定要编程,并且大多数科研相关的编程工作是和数据相关的。因为各个领域的前沿研究,都需要在领域相关的大量数据中,寻找新的突破和发现。可能有很多同学知道,Python 和 R 是现今最主流的两种用于数据处理的语言。如果你身边有非计算机领域的博士同学,或者研究工作者,去问问他们,你会惊讶的发现,大多数非计算机专业的研究工作者,更常用 R 语言,而非 Python 语言。为什么?因为,相较 Python 语言,R 语言更像一个 DSL。当然,R 语言本身也很强大,但是远没有 Python 强大。R 语言本身近乎就是为数据科学设计的语言,大多数数据科学所需要的功能,R 语言或者本身从语法层面支持,或者在标准库中内置好了。所以,对于非计算机专业的人士而言,R 语言是更加友好,上手更快的。使用 R 语言,可以尽量少地去接触和 “计算机科学” 相关的知识,就能上手复杂的数据处理任务。
相较于 SQL,正则表达式,CSS,HTML,R 等等这些 DSL,计算机专业的同学通常需要努力学习的,诸如 C++, Java, Python 等语言,被称为 GPPL,是 General Purpose Programming Language 的简称。也就是所谓的 “通用目标语言”。 顾名思义,通用目标语言没有把功能限制在特定领域中,相较 DSL 更灵活,可以完成更多的事情,甚至可以说是任意逻辑(嗯,这句话细究起来,又可以写一篇新文章了),但代价就是:语言本身操作起来更灵活,也更复杂。
整体而言,越来越多的 DSL 的出现,也是编程语言发展的一大趋势。 比如,现在大火的人工智能界,很多人就认为,使用现有的语言做人工智能算法,太麻烦了。人工智能专家要花很多时间,来处理和人工智能算法无关的逻辑。发明一个人工智能领域专有的 DSL,近乎是必然。很有可能,我们在未来,还将看到医学领域特定的 DSL、生物学领域特定的 DSL、化工领域特定的 DSL、物理学领域特定的 DSL,等等等等。
另一方面,在未来,使用 GPPL 的工程师们 —— 也就是真正计算机专业的同学们,一个很重要的任务,就是开发 DSL。开发出的这些 DSL,是给其他领域的专家,或者业务专员使用的。“分工” 本身就是经济学的基础概念之一,是我们这个世界能够良性运转逐渐发展的诸多核心规则之一。 这种 GPPL 和 DSL 越来越清晰的分层,也是 “分工” 这一经济学概念在编程语言界的体现:)
当然,对于 GPPL,也有很多发展趋势。比如动态性,比如对函数式编程的支持,比如对并发的支持,等等等等。有机会,我再向大家总结:)
对于计算机专业的同学来说,语言只是一种工具而已。学习语言的目的,不是对细微的语言特性和语法糖如数家珍,关键还是要应用语言,解决实际的场景问题。
很多同学会问,十年后,什么语言最火?很有可能,十年后最火的语言,现在还没出现呢:)
大家加油!:)