- 面向对象用来组织程序是好,但我用C
- 我用C ,函数式编程的好,跟我有什么关系
- 动态语言那些特性很好,可惜我用Java
- ……
如果你这么想,说明你被自己的看家本事给局限住了,这种思维方式会让你即便学到了更多好东西,也无可奈何。
程序设计语言之间没有那么泾渭分明界限,多学几门,才能打破语言局限,让设计更好落地。可根据项目特点选择合适语言,也可以将其它语言一些优秀的地方借鉴过来。Andrew Hunt和David Thomas在《程序员修炼之道》(The Pragmatic Programmer)中给程序员们提了一项重要的建议:每年至少学习一门新语言。
语言那么多,我要一个一个都学过去吗?学语言到底在学什么呢?
程序设计语言本身也是一个软件,它也包含模型、接口和实现。 学习程序设计语言主要是为了学习程序设计语言提供的编程模型,比如:不同的程序组织方式,不同的控制结构等等。因为不同的编程模型会带给你不同的思考方式。
1 程序设计语言发展简史
程序设计语言都是图灵完备:语言指定的数据操作规则能够实现图灵机的全部功能(图灵机的概念是由阿兰·图灵提出的,图灵机为计算机能够解决的问题划定了一个边界)。 所以,图灵机是所有程序设计语言最底层的模型,程序设计语言都是在这个基础上生长出来的,包括计算机用0和1编码。
计算机能够识别的都是0和1,但用0和1直接写代码实在太麻烦。所以,早在计算机诞生之初,就产生汇编语言,将那些0101的操作符变成更容易记住的ADD、MOV之类指令。
但人们很快就发现,用汇编写程序也痛苦,因为只有对计算机了如指掌,才能写好汇编。更可怕的是,即便你熟练掌握了一种计算机的汇编语言,换成另外一种计算机,你也必须从头学。
高级程序设计语言登场。
第一门高级程序设计语言Fortran,为程序设计语言的发展奠定基础。数据开始拥有了类型(类型就是一种对内存数据的解释方式)。人们逐渐认识到高级程序设计语言对于开发效率的提高。
早期程序设计语言探索的集大成者就是C语言,它提供了对于计算机而言最为恰当的抽象,屏蔽了计算机硬件的诸多细节。
随着高级程序设计语言的发展,门槛逐步降低,可开发的程序规模也逐渐膨胀。搭着C语言的便车将面向对象的程序设计风格带入了主流视野,这就是C 。
各种高级程序设计语言已经屏蔽了很多细节,但有一个问题始终没有得到很好的解决,也由此引发了更多的问题,这就是内存管理。其实,人们早就在尝试各种屏蔽内存管理的方式,但因为早期计算机硬件性能有限,所以没有任何一种方式能够成为行业主流。
后来,计算机硬件的能力得到了大幅度提升,这让那些在故纸堆里的技术又焕发了新的活力。这个阶段的胜利者是Java,一方面,它支持面向对象编程;另一方面,它还有垃圾回收机制——一种内存管理的方式。
Java的路其实也很坎坷,因为它早期在个人电脑上的尝试并不算成功。后来选择了企业级开发的赛道,才有机会展现自己的优势。因为企业级服务器本身性能优于个人电脑,对Java有更高的容忍度,它才得到了机会,不断进行自身的优化。
当硬件不再是程序设计语言的发展障碍之后,程序设计语言又该如何发展呢?
从前面的历程不难看出,程序设计语言的发展就是一个“逐步远离计算机硬件,向着待解决的问题靠近”的过程。所以,程序设计语言接下来的发展方向就是探索怎么更好地解决问题了。
前面说的这些只是程序设计语言发展的主流路径。
还有一条不那么主流的路径也一直在发展,就是函数式编程的程序设计语言,这方面的代表就是LISP。 在这条路上,刚开始,很多人都是偏学术风格的,关心解决方案是否优雅,也就是说,如何解决问题,如何一层层构建抽象。也探索更多可能,垃圾回收机制就是从这里来的。但同样受限于当时硬件的性能,这条路上的探索在很长一段时间之内都只是一个小众游戏。
当硬件的性能不再成为阻碍,如何解决问题开始变得越来越重要时,函数式编程终于和程序设计语言发展的主流汇合了。促进函数式编程引起广泛重视也还有一个硬件因素:多核。
多核的出现,本身是IT行业应对CPU发展进入瓶颈期的一个解决方案,但它却打破了很多程序员只习惯于利用一个CPU写程序的传统方式。
为了利用多核的优势,人们探索了各种方案,今天看到的各种并发模型、异步模型等解决方案都从那时开始得到了蓬勃的发展。函数式编程在这个方面的探索就是利用自己声明式的表达方式屏蔽了硬件差异。让人们注意到函数式编程的价值的就是著名的MapReduce。
函数式编程兴起,让那些在函数式编程社区的探索随之兴起,比如,声明式编程、DSL、元编程等等。一些后出现的程序设计语言开始将面向对象和函数式编程二者融合起来,比如Scala。而像Java和C 这些“老战士”则逐渐地将函数式编程的支持加入到语言之中。
相比于这些“正规军”,还有一股力量也逐渐从边缘走上了舞台,这就是动态语言,代表语言有 Perl、Python、Ruby、PHP等等。 以前,人们更喜欢用“脚本语言”称呼这类程序设计语言,这个名字表明,它就是为了简单地解决一些特定的问题而出现的。所以,在人们心目中,它们显得并不那么正式。但它们简单、轻巧的特性有效地降低了入门的门槛,也赢得了一大批拥趸。
语言的发展就是一个互相学习和借鉴的过程。以前,动态语言的弱项在于不适用于规模比较大的工程,而近些年来,随着动态语言用户的增多,配套的工具也逐渐多了起来,动态语言项目的规模也逐渐增大。而在主航道的程序设计语言,也纷纷向动态语言学习,努力地简化代码编写的难度,比如,Java和C 都开始支持类型推演(Type Inference),目的就是让程序员少敲几个字符。
把程序设计语言当作一个软件,它的发展历程就是一个逐渐添加新模型的过程,而其发展的结果就是如今的开发门槛越来越低,能够开发的程序规模越来越大。
2 语法都是语法糖
C语言提供了对汇编指令直接的封装。 C 先是提供了面向对象,后来又提供了泛型编程。 Java把内存管理从开发者面前去掉了,后来引入的Annotation可以进行声明式编程。 Ruby提供了动态类型,以及由Ruby on Rails引导出的DSL风格。 Scala和Clojure提供了函数式编程。 Rust提供了新的内存管理方式,而Libra提供的Move语言则把它进一步抽象成了资源的概念。 既然学习新的程序设计语言是为了学习新的编程模型,反过来也可以说,不提供新编程模型的语言是不值得刻意学习的。 如果你已经学会了一两门程序设计语言,学习一门新的语言其实并不困难,因为每种语言提供的新模型是有限的,基本的元素类似,无非用不同关键字。
所以,学习新语言,只是在做增量的学习,思维负担并没那么沉重。一旦对于程序设计语言的模型有了新的认识,你就能理解一件事:一切语法都是语法糖。
语法糖(Syntactic sugar)是英国计算机科学家彼得·兰丁发明的一个术语,指的是那些为了方便程序员使用的语法,它对语言的功能没有影响。
懂得了语法糖的道理,要想更好地理解程序设计语言,一种好的做法就是打开语法糖,了解一下语法是怎么实现的:
类型是一种对内存的解释方式。 class/struct是把有相关性的数据存放到一起的一种数据组织方式。 Groovy、Scala、Kotlin、Clojure等JVM上的新语言,提供了一种不同于Java的封装JVM的方式。 ……
3 总结
语言的发展并非一蹴而就,而是一个渐进式的发展历程。一些新的尝试总会在一些不起眼的地方冒出来,而且语言之间也在相互借鉴。
如果你能每年学习一门新语言,起初,你可以了解不同的编程模型。当你的积累足够多了,学习语言就是在跟踪程序设计语言的最新发展了。
当你手里有了足够多的“武器”时,你就可以打开思路,运用不同的方式解决问题了,甚至把其它语言的好东西,借鉴到自己使用的语言中。
学习不同的程序设计语言可以帮助我们更好地落地设计,也可以让我们向不同的语言借鉴优秀的方面。
我们简要地了解了程序设计语言的发展历史,从最开始的对机器模型的封装,到今天不断降低的开发门槛,程序设计语言的演化从未停止。我们也看到各种不同的编程风格在经历了最初各自独立的发展之后,开始慢慢融合。
对程序设计语言发展的了解,可以帮助我们理解一件事:一切语法都是语法糖。新的语法通常是在既有的结构上不断添加出来的,为的是简化代码的编写。
《程序员修炼之道》鼓励程序员们每年至少学习一门新语言,主要是为了让我们去学习新的编程模型,而不提供新编程模型的语言不值得刻意去学习。
不过,这就需要你对程序设计语言有着更深的理解。