mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-10-22 09:53:44 +08:00
mod
This commit is contained in:
146
极客时间专栏/编译原理之美/面向未来的编程语言/36 | 当前技术的发展趋势以及其对编译技术的影响.md
Normal file
146
极客时间专栏/编译原理之美/面向未来的编程语言/36 | 当前技术的发展趋势以及其对编译技术的影响.md
Normal file
@@ -0,0 +1,146 @@
|
||||
<audio id="audio" title="36 | 当前技术的发展趋势以及其对编译技术的影响" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/e6/ec/e6f9fbc1ab2d319cd19fc29ec977aaec.mp3"></audio>
|
||||
|
||||
在IT领域,技术一直在飞速的进步,而每次进步,都会带来新的业态和新的发展机遇。
|
||||
|
||||
退回到10年前,移动互联网刚兴起不久,谁也没想到它会催生现在这么多的业态。而云计算还在酝酿期,腾讯和百度的创始人都觉得它走不远,现在竟然这么普及。
|
||||
|
||||
退回到20年前,互联网刚兴起,上网都要拨号。互联网的几个巨头,像阿里巴巴、百度、腾讯、新浪,还有网易,都是在那个时代展露头角的。毫不夸张地说,如果你在那个时代搞技术,懂Web编程的话,那绝对是人人争抢的“香饽饽”,毕竟那时,Web编程是前沿技术,懂这个领域的人,凤毛麟角。
|
||||
|
||||
退回到30年前,微软等公司才刚开始展露头角,雷军、求伯君等老一代程序员也正在发力,WPS的第一个版本运行在DOS操作系统上。我还记得,95年的时候,我在大学的阶梯教室里,看了比尔盖茨曾发表的,关于未来技术方向的演讲。当时,他预测了未来的科技成果,比如移动智能设备,听上去像天方夜谭,但现在移动互联网、人工智能和5G的发展,早已超出了他当时的想象。
|
||||
|
||||
那么你有理由相信,未来10年、20年、30年,会发生同样天翻地覆的变化。这种变化所造成的的影响,你我哪怕大开“脑洞”都无法预料。而你在这种趋势下,所能做的就是,把握当下,并为未来的职业生涯做好准备。**这是一件认真且严肃的事情,值得你用心对待。**
|
||||
|
||||
当然,洞悉未来很难,但你可以根据当前了解到的信息,捕捉一些发展趋势,看看这些发展趋势,让编译技术的发展方向有了哪些不同,跟你又有什么关系。
|
||||
|
||||
本节课,我想与你分享3个方面的技术发展趋势,以及它们对编译技术的影响:
|
||||
|
||||
- 人工智能,以及如何让编程和编译技术变得更智能?
|
||||
- 云计算,以及是否需要云原生的语言?
|
||||
- 前端技术,以及能否出现统一各个平台的大前端技术?
|
||||
|
||||
期望这些内容,能让你看到一些不同的思考视角,获得一些新的信息。
|
||||
|
||||
## 趋势1:让编程更智能
|
||||
|
||||
人工智能是当前发展最迅速的技术之一了。这几年,它的发展速度超过了人们的预期。那么你知道,它对编译技术和计算机语言的影响是什么吗?
|
||||
|
||||
**首先,**它需要编译器能够支撑,机器学习对庞大计算力的需求,同时兼容越来越多新的硬件架构。
|
||||
|
||||
由于机器学习的过程需要大量的计算,仅仅采用CPU效率很低,所以GPU和TPU等新的硬件架构得到了迅速的发展。对于编译技术来说,首要的任务,是要充分发挥这些新硬件的能力;因为AI的算法要能跑在各种后端架构上,包括CPU、GPU和TPU,也包括仍然要采用SIMD等技术,所以后端技术就会变得比较复杂。同时,前端也有不同的技术框架,比如谷歌的TensorFlow、Facebooke的pyTorch等。那么编译器怎样更好地支持多种前端和多种后端呢?
|
||||
|
||||
根据在[24讲](https://time.geekbang.org/column/article/151939)学到的知识,你应该会想到要借助中间代码。所以,MLIR应运而生。**这里要注意,**ML是Multi-Level(多层次)的意思,而不是Machine Learning的缩写。**我还想告诉你,**MLIR的作者,也是LLVM的核心作者、Swift语言的发明人,Chris Lattner(他目前在谷歌TensorFlow项目中)。而当你看到MLIR的格式,也许会觉得跟LLVM的IR很像,那么你其实可以用更短的学习周期来掌握这个IR。
|
||||
|
||||
**其次,**AI还可能让现有的编译技术发生较大的改变。
|
||||
|
||||
实际上,把AI和编译技术更好地结合,是已经持续研究了20年左右的,一个研究方向。不过,没有很大的发展。因为之前,人工智能技术的进步不像这几年这么快。近几年,随着人工智能技术快速进步,在人脸识别、自动驾驶等各个领域产生了相当实用的成果,人们对人工智能可能给编译技术带来的改变,产生了更大的兴趣。这给了研究者们研究的动力,他们开始从各个角度探索变革的可能性。
|
||||
|
||||
比如说,在后端技术部分,很多算法都是NP完全的。这就是说,如果你用穷举的方法找出最优解,成本非常高。这个时候,就会用启发式(heuristic)的算法,也就是凭借直观或经验构造的算法,能够在可接受的花费下找出一个可行的解。那么采用人工智能技术,通过大数据的训练,有可能找出更好的启发式算法,供我们选择。这是人工智能和编译技术结合的一个方向。
|
||||
|
||||
Milepost GCC项目早在2009年就发布了,它是一款开源的,人工智能编译器。它能够通过自动学习来确定去优化哪些代码,以便让程序的性能更高。据IBM的测试数据,某些嵌入式软件的性能因此提升了18%。
|
||||
|
||||
另一个讨论度比较高的方向就是**人工智能编程(或者叫自动编程)。**从某种意义上看,从计算机诞生到现在,我们编写程序的方式一直没有太大的进步。最早,是通过在纸带或卡片上打孔,来写程序;后来产生了汇编语言和高级语言。但是,写程序的本质没有变化,我们只是在用更高级的方式打孔。
|
||||
|
||||
**讽刺的是,**在计算机语言的帮助下,很多领域都出现了非常好的工具,比如CAD完全改变了建筑设计行业。但计算机行业本身用的工具仍然是比较原始的,还是在一个编辑器中,用文本的方式输入代码。
|
||||
|
||||
而人工智能技术可能让我们习惯已久的编程模式发生改变。比如,现在的编译器只是检查错误并生成代码,带有AI功能的编译器呢,有可能不仅检查出比较明显的错误,甚至还会对你的编码方式提出建议。假设你用一个循环去做某个数组的计算,带有AI功能的编译器会告诉你,用函数式编程做向量计算性能更高,并提供一键式替换功能。
|
||||
|
||||
**这里延伸一下,**有可能,未来写程序的方式都会改变。微软收购GitHub以后,运用大量的代码作为训练数据,正在改进IDE,提供智能提示功能。而这还只是开始。**目前,AI其实已经能帮你做UI的设计:**你画一个草图,AI给你生成对应的Web页面。
|
||||
|
||||
而且在AI辅助设计领域,算法还能根据你的需要,帮你生成平面或三维的设计方案。我能想象,未来,你告诉AI给你生成一个电商APP,它就能给你生成出来。你再告诉它做什么样的修改,它都会立即修改。在未来,应用开发中,最令人头疼的需求变化的问题,在AI看来根本不是事。
|
||||
|
||||
那么,如果这个前景是真实的,**对于你而言,需要掌握什么技能呢?**
|
||||
|
||||
我建议你了解,编译技术和人工智能这两个领域的知识。那些计算机的基础知识会一直有用,你可以参与到编程范式迁移,这样一个伟大的进程中。现有程序开发中的那些简单枯燥,又不需要多少创造力的工作,也就是大家通常所说的“搬砖”工作,可能会被AI代替。而我猜测,未来的机会可能会留给两类人:
|
||||
|
||||
<li>
|
||||
一类是具备更加深入的计算机基础技能,能应对未来挑战的,计算机技术人才,他们为新的计算基础设施的发展演化,贡献自己的力量。
|
||||
</li>
|
||||
<li>
|
||||
另一类是应用领域的专家和人才。他们通过更富有创造力的工作,利用新的编程技术实现各种应用。编写应用程序的重点,可能不再是写代码,而是通过人工智能,训练出能够反映领域特点的模型。
|
||||
</li>
|
||||
|
||||
当然,向自动编程转移的过程肯定是逐步实现的:AI先是能帮一些小忙,解放我们一部分工作量,比如辅助做界面设计、智能提示;接着是能够自动生成某些小的、常用的模块;最后是能够生成和管理复杂的系统。
|
||||
|
||||
总而言之,AI技术给编译技术,和编程模式带来了各种可能性,而你会见证这种转变。除此之外,云计算技术的普及和深化,也可能给编译技术和编程模式带来改变。
|
||||
|
||||
## 趋势2:云原生的开发语言
|
||||
|
||||
云计算技术现在的普及度很广,所有应用的后端部分,缺省情况下都是跑在云平台上的,云就是应用的运行环境。
|
||||
|
||||
在课程里,我带你了解过程序的运行环境。那时,我们的关注点,还是在一个单机的环境上,包括CPU和内存这些硬件,以及操作系统这个软件,弄清楚程序跟它们互动的关系。比如,操作系统能够加载程序,能够帮程序管理内存,能够为程序提供一些系统功能(把数据写到磁盘上等等)。
|
||||
|
||||
然而,在云计算时代,**云就是应用的运行环境。**一个应用程序不是仅仅加载到一台物理机上,而是要能够根据需要,加载很多实例到很多机器上,实现横向扩展。当然了,云也给应用程序提供各种系统功能,比如云存储功能,它就像一台单独的服务器,会给程序提供读写磁盘的能力一样。
|
||||
|
||||
除此之外,在单机环境下,传统的应用程序,是通过函数或方法,来调用另一个模块的功能,函数调用的层次体现为栈里一个个栈桢的叠加,编译器要能够形成正确的栈桢,实现自动的内存管理。**而在云环境下,**软件模块以服务的形式存在,也就是说,一个模块通过RESTful接口或各种RPC协议,调用另外的模块的功能。程序还需要处理通讯失败的情况,甚至要在调用多个微服务时,保证分布式事务特性。而我们却没从编译技术的角度,帮助程序员减轻这个负担。
|
||||
|
||||
**导致的结果是:**现在后端的程序特别复杂。你要写很多代码,来处理RPC、消息、分布式事务、数据库分片等逻辑,还要跟各种库、框架、通讯协议等等打交道。**更糟糕的是,**这些技术性的逻辑跟应用逻辑,混杂在一起,让系统的复杂度迅速提高,开发成本迅速提升,维护难度也增加。很多试图采用微服务架构的项目因此受到挫折,甚至回到单一应用的架构。
|
||||
|
||||
**所以,一个有意义的问题是:**能否在语言设计的时候,就充分利用云的基础设施,实现云原生(Cloud Native)的应用?也就是说,我们的应用,能够透明地利用好云计算的能力,并能兼容各种不同厂商的云计算平台,就像传统应用程序,能够编译成,不同操作系统的可执行文件一样。
|
||||
|
||||
好消息是,云计算的服务商在不断地升级技术,希望能帮助应用程序,更好地利用云计算的能力。而无服务器(Serverless)架构就是最新的成果之一。采用无服务器架构,你的程序都不需要知道容器的存在,也不需要关心虚拟机和物理机器,你只需要写一个个的函数,来完成功能就可以了。至于这个函数所需要的计算能力、存储能力,想要多少就有多少。
|
||||
|
||||
**但是,**云计算厂商提供的服务和接口缺少标准化,当你把大量应用都部署到某个云平台的时候,可能会被厂商锁定。如果有一门面向云原生应用的编程语言,和相应的开发平台,能帮助人们简化云应用的开发,同时又具备跨不同云平台的能力,**那就最理想了。**
|
||||
|
||||
实际上,已经有几个创业项目在做这个方向做探索了,比如 [Ballerina](https://ballerina.io/)、[Pulumi](https://www.pulumi.com/)和[Dark](https://darklang.com/),你可以看一下。
|
||||
|
||||
当然了,云计算和编程结合起来,就是另一个有趣的话题:云编程。我会在下一讲,与你进一步讨论这个话题。
|
||||
|
||||
## 趋势3:大前端技术栈
|
||||
|
||||
上面所讲的云计算,针对的是后端编程,而与此对应的,是前端编程工作。
|
||||
|
||||
后端工作的特点,是越来越云化,让工程师们头疼的问题,是处理分布式计算环境下,软件系统的复杂性。**当然,前端的挑战也不少。**
|
||||
|
||||
我们开发一款应用,通常需要支持Web、IOS和Android三种平台,有时候,甚至需要提供Windows和macOS的桌面客户端。不同的平台会需要不同的技术栈,从而导致一款应用的开发成本很高,这也是前端工程师们不太满意的地方。
|
||||
|
||||
所以,前端工程师们一直希望能用一套技术栈,搞定多个平台。比如,尝试用Web开发的技术栈完成Android、IOS和桌面应用的开发。React Native、Electron等框架是这个方面的有益探索;Flutter项目也做了一些更大胆的尝试。
|
||||
|
||||
Flutter采用Dart开发语言,可以在Android和IOS上生成高质量的原生界面,甚至还可以支持macOS、Windows和Linux上的原生界面。另外,它还能编译成Web应用。所以,本质上,你可以用同一套代码,给移动端、桌面端和Web端开发UI。
|
||||
|
||||
**你可以把这种技术思路叫做大前端:**同一套代码,支持多个平台。
|
||||
|
||||
从Flutter框架中,你可以看出编译技术起到的作用。首先,Dart语言也是基于虚拟机的,编译方式支持AOT和JIT,能够运行在移动端和桌面端,能够调用本地操作系统的功能。对于Web应用则编译成JavaScript、CSS和HTML。这个项目的创新力度已经超过了React Native这些项目,工程师们已经不满足于,在现有的语言(JavaScript)基础上编写框架,而是用一门新的语言去整合多个技术栈。
|
||||
|
||||
当然,提到前端技术,就不能不提Web Assembly(WASM)。WASM是一种二进制的字节码,也就是一种新的IR,能够在浏览器里运行。相比JavaScript,它有以下特点:
|
||||
|
||||
- 静态类型;
|
||||
- 性能更高;
|
||||
- 支持C/C++/Rust等各种语言生成WASM,LLVM也给了WASM很好的支持;
|
||||
- 字节码尺寸比较少,减少了下载时间;
|
||||
- 因为提前编译成字节码,因此相比JavaScript减少了代码解析的时间。
|
||||
|
||||
由于这些特点,WASM可以在浏览器里,更高效地运行,比如可以支持更复杂的游戏效果。**我猜想,**未来可能出现,基于浏览器的、性能堪比本地应用的字处理软件、电子表格软件。基于云的文档软件(比如Google Doc)会得到再一次升级,使用者也将获得更好的体验。
|
||||
|
||||
此外,WASM还允许除了JavaScript之外的语言,来编写Web应用。这些语言可以像JVM上的语言一样,生成字节码,并且只要有运行WASM的虚拟机,它们就具备一样的可移植性。
|
||||
|
||||
而且,WASM不仅可以运行在前端,还可以运行在后端。就像JavaScript语言被Node.js项目,用于开发后端服务一样,现在Node.js项目也可以调用WASM模块。还有一些更激进的项目,正在开发高效运行WASM的虚拟机,比如[wasmer项目](https://wasmer.io/)。wasmer虚拟机可以使用LLVM进行编译和优化,从而能够提供更高的性能。
|
||||
|
||||
**讨论到这里,你有什么感受?**C/C++语言写的程序,以WASM的形式运行在浏览器里,或者运行在后端的虚拟机里,通过即时编译运行。完全颠覆了你对这两门语言的传统印象吧?这就是编译技术与时俱进的一个体现。
|
||||
|
||||
其实,学过《编译原理之美》这门课程以后,我也期望你有信心,做一款WASM的虚拟机,并基于它,做一个类似Node.js的后端服务平台。因为这并没有太大的技术难度,你只要做到稳定好用,花费很多心血就是了。
|
||||
|
||||
## 课程小结
|
||||
|
||||
为了拓展你的视野,我带你探讨了三个技术的发展趋势,以及它们对编译技术和编程方式所带来的影响。我希望,在学完本节课之后,你能有以下收获:
|
||||
|
||||
<li>
|
||||
人工智能有可能提升现有的编译技术框架,并带来自动编程等,编程模式的重大变化。
|
||||
</li>
|
||||
<li>
|
||||
应用程序的运行环境,不能仅仅考虑单机,还要考虑云这个更大的环境。因此,新一代的编程语言和开发平台,可能会让开发云原生的应用更加简单。
|
||||
</li>
|
||||
<li>
|
||||
在应用开发的前端技术方面,如果要想支持多种平台,可能还需要通过编译技术来获得大的突破。
|
||||
</li>
|
||||
|
||||
当然,编译技术还有很多其他的研究方向,比如更好地支持并行计算、支持物联网和低功耗场景,支持区块链,甚至支持一些同学感兴趣的,未来的量子计算机,等等。**不过,在我看来,**我在文中提到的这三个趋势,跟你的关系是最为密切的。因为你现在或多或少地都在接触AI、云和前端技术。
|
||||
|
||||
我希望今天的内容能帮你开拓思路,为迎接未来的技术趋势做好准备,并且能够更好地利用编译技术,增强自身的竞争力。
|
||||
|
||||
## 一课一思
|
||||
|
||||
在本节课中,我分享了自己对技术趋势的思考和感悟,而你或许有其他的见解,欢迎在留言区与我讨论,碰撞思维的火花。
|
||||
|
||||
感谢你的阅读,如果这篇文章让你有所收获,也欢迎你将它分享给更多的朋友。
|
||||
|
||||
|
178
极客时间专栏/编译原理之美/面向未来的编程语言/37 | 云编程:云计算会如何改变编程模式?.md
Normal file
178
极客时间专栏/编译原理之美/面向未来的编程语言/37 | 云编程:云计算会如何改变编程模式?.md
Normal file
@@ -0,0 +1,178 @@
|
||||
<audio id="audio" title="37 | 云编程:云计算会如何改变编程模式?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/36/28/36ef59dc8c60b4c95ea03de2c946f128.mp3"></audio>
|
||||
|
||||
上一讲中,我分享了当前3个技术发展趋势,以及其对编译技术的影响。今天我们把其中的云计算和编程模式、编译技术的之间的关系、前景再展开探讨一下。
|
||||
|
||||
总的来说,现在编写程序是越来越云化了,所以,我们简单地称作云编程就好了。
|
||||
|
||||
**关于云编程,有很多有趣的问题:**
|
||||
|
||||
1.编程本身是否也能上云?在云上编程会跟本地开发有什么不同?<br>
|
||||
2.如何编写云应用,来充分发挥云平台的能力?分为哪些不同的模式?<br>
|
||||
3.为什么编写云应用那么复杂?如何降低这些复杂度?云原生应用的开发平台,能否解决这些问题?
|
||||
|
||||
本节课,我就带你深入讨论这些问题,希望借此帮助你对编程和云计算技术的关系做一个梳理,促使你更好地利用云计算技术。
|
||||
|
||||
首先,来看看如何实现云上编程。
|
||||
|
||||
## 实现云上编程
|
||||
|
||||
90年代初,我在大学学习编程,宿舍几个人合买了一台386电脑。那个时候,我记得自己不太喜欢微软提供的MFC编程框架,这和386电脑没有浮点运算器,编译起来比较慢有关,编译一次使用MFC框架的,C++程序的时间,足够我看一页报纸的了。
|
||||
|
||||
喜欢编程的人,为了获得流畅的性能,电脑配置总是很高,虽然这足以满足C/C++时代的编程需要,但进入Java时代后,因为应用结构越来越复杂,工程师们有时需要在笔记本或桌面电脑上,安装各种复杂的中间件,甚至还要安装数据库软件,这时,电脑的配置即便再高,也很难安装和配置好这么复杂的环境。那么到了云计算时代,挑战就更大了,比如,你能想象在电脑上安装Hadoop等软件,来做大数据功能的开发吗?
|
||||
|
||||
其实,编写一个小的应用还好,但现在的应用越来越复杂,所需的服务端资源越来越多。**以我最近参与的一个项目为例,**这个项目是采用微服务架构的一个企业应用,要想实现可扩展的性能、更好的功能复用,就要用到数据库、消息队列、容器服务、RPC服务、分布式事务服务、API服务等等很多基础设施,在自己的电脑上配置所有这些环境,是不大可能的。
|
||||
|
||||
因此,工程师们已经习惯于,在云上搭建开发和测试环境,这样,可以随需获取各种云端资源。
|
||||
|
||||
因为编程跟云的关系越发紧密,有些开发工具已经跟云平台有了一定的整合,方便开发者按需获取云端资源。比如,微软的Visual Studio支持直接使用Azure云上的资源。
|
||||
|
||||
再进一步,IDE本身也可以云化,我们可以把它叫做“云IDE”。你的电脑只负责代码编辑的工作,代码本身放在云上,编译过程以及所需的类库也放在云上。Visual Studio Code就具备UI和服务端分离的能力。还有一些服务商提供基于浏览器的IDE,也是实现了前后端的分离。
|
||||
|
||||
我认为,未来的IDE可能会越来越云化,因为云IDE有很多优势,能给你带来很多好处。
|
||||
|
||||
**1.易于管理的编程环境**
|
||||
|
||||
编程环境完全配置在云上,不用在本地配置各种依赖项。
|
||||
|
||||
这一点,会给编程教育这个领域,提供很大的帮助。因为,学习编程的人能够根据需要,打开不同的编程环境,立即投入学习。反之,如果要先做很多复杂的配置才能开始学习,学习热情就会减退,一些人也就因此止步了。
|
||||
|
||||
其实,在软件开发团队中,你经常会看到这样一个现象:新加入项目组的成员,要花很长的时间,才能把开发环境搭建起来。因为他们需要安装各种软件,开通各种账号等等。那么,如果是基于云IDE开发的,这些麻烦都可以省掉。
|
||||
|
||||
**2.支持跨平台编程**
|
||||
|
||||
有些编程所需要的环境,在本地很难配置,在云中开发就很简单。比如,可以用Windows电脑为Linux平台开发程序,甚至你可以在云上,为你的无人机开发程序,并下载到无人机上。
|
||||
|
||||
在为手机编程时,比较复杂的一项工作是,适配各种不同型号的手机。这时,你只需要通过云IDE,整合同样基于云的移动应用测试环境,就可以在成百上千种型号的手机上测试你的应用了。
|
||||
|
||||
**3.更强的计算能力**
|
||||
|
||||
有些软件的编译非常消耗CPU,比如,完整编译LLVM可能需要一两个小时,而充分利用服务器的资源可以让编译速度更快。如果你从事AI方面的开发,体会会更深,AI需要大量的算力,并且GPU和TPU都很昂贵,我们很难自己去搭建这样的开发环境。而基于云开发,你可以按需使用云上的GPU、TPU和CPU的计算能力。
|
||||
|
||||
**4.有利于开发过程的管理**
|
||||
|
||||
开发活动集中到云上以后,会有利于各种管理工作。比如,很多软件项目是外包开发的,那么你可以想象,基于云编程的平台,甲乙双方的项目管理者,都可以获得更多关于开发过程的大数据,也更容易做好源代码的保护。
|
||||
|
||||
**5.更好的团队协作**
|
||||
|
||||
越来越多的人已经习惯在网上编写文档,平心而论,线上文档工具并没有本地的Office软件功能强大,是什么因素让我们更加偏爱线上文档工具呢?就是它的协作功能。团队中的成员可以同时编辑一个文档,还可以方便地将这个文档在团队中分享。
|
||||
|
||||
**而我比较希望见到这样的场景,**那就是,程序员们可以基于同一个代码文件,进行点评和交互式的修改,这相当于基于云的结对编程,对于加强团队的知识分享、提升软件质量都会有好处。
|
||||
|
||||
**基于上述几点,我个人猜测:**编程这项工作,会越来越与云紧密结合。这样一来,不仅仅能方便地调取云端的资源,越来越多的编程环境也会迁移到云上。
|
||||
|
||||
既然提到了在云上编程的方式,那么接下来,我们从编译技术的视角,来探讨一下,如何编写能充分运用云计算强大威力的应用,这样,你会对云计算有一个更加全面的认知。
|
||||
|
||||
## 如何编写云应用?
|
||||
|
||||
学习编译原理,你可能会有一个感受,那就是编程可以在不同的抽象层次上进行。也就是说,你可以通过抽象,把底层复杂的技术细节转换成上层简单的语义。
|
||||
|
||||
程序员最早是直接编写机器码,指令和寄存器都要直接用0101来表示。后来,冯·诺依曼的一个学生,发明了用助记符的方法(也就是汇编语言)简化机器码的编写。用汇编语言编程的时候,你仍然要使用指令和寄存器,但可以通过名称来引用,比如[34讲](https://time.geekbang.org/column/article/164017)中,用pushq %rbp这样的汇编指令来表示机器码0x55。这就增加了一个抽象层次,用名称代替了指令和寄存器的编码。
|
||||
|
||||
而高级语言出现后,我们不再直接访问寄存器,而是使用变量、过程和作用域,抽象程度进一步增加。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ea/66/ea42415d8187f70bc9dbc90a5e3ddb66.jpg" alt="">
|
||||
|
||||
总结起来,就是我们使用的语言抽象程度越来越高,每一次抽象对下一层的复杂性做了屏蔽,因此使用起来越来越友好。**而编译技术,**则帮你一层层地还原这个抽象过程,重新转换成复杂的底层实现。
|
||||
|
||||
云计算的发展过程跟编译技术也很类似。云计算服务商们希望通过层层的抽象,来屏蔽底层的复杂性,让云计算变得更易用。
|
||||
|
||||
而且,通常来说,在较低的抽象层次上,你可以有更大的掌控力,而在更高的抽象层次上,则会获得更好的方便性。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/99/48/991f00b87cea86d10f53ed2ceaa4b148.jpg" alt="">
|
||||
|
||||
**虚拟机是人们最早使用云资源的方式,一台物理服务器可以分割成多个虚拟机。**在需要的时候,可以创建同一个虚拟机镜像的多个实例,形成集群。因为虚拟机包含了一套完整的操作系统,所以占据空间比较大,启动一个实例的速度比较慢。
|
||||
|
||||
我们一般是通过编写脚本来管理软件的部署,每种软件的安装部署方式都不相同,系统管理的负担比较重。
|
||||
|
||||
**最近几年,容器技术变得流行起来。**容器技术可以用更轻量级的方式,分配和管理计算资源。一台物理服务器可以运行几十、上百个容器,启动新容器的速度也比虚拟机快了很多。
|
||||
|
||||
跟虚拟机模式相比,容器部署和管理软件模块的方式标准化了,我们通过Kubernetes这样的软件,编写配置文件来管理容器。从编译原理的角度出发,这些配置文件就是容器管理的DSL,它用标准化的方式,取代了原来对软件配置进行管理的各种脚本。
|
||||
|
||||
**无服务器(Serverless)架构,或者叫做FaaS(Function as a Service),做了进一步的抽象。**你只要把一个个功能写成函数,就能被平台调用,来完成Web服务、消息队列处理等工作。这些函数可能是运行在容器中的,通过Kubernetes管理的,并且按照一定的架构来协调各种服务功能。
|
||||
|
||||
但这些技术细节都不需要你关心,**你会因此丧失一些掌控力,**比如,你不能自己去生成很多个线程做并行计算。不过,也因为需要你关心的技术细节变少了,**编程效率会提高很多。**
|
||||
|
||||
**上面三个层次,**每一级都比上一级的抽象层次更高。就像编译技术中,高级语言比汇编语言简单一样,使用无服务架构要比直接使用虚拟机和容器更简单、更方便。
|
||||
|
||||
但即使到了FaaS这个层次,编写一个云应用仍然不是一件简单的事情,你还是要面临很多复杂性,比如,处理应用程序与大容量数据库的关系,实现跨公有云和私有云的应用等等。那么能否再进一步抽象并简化云应用的开发?是否能通过针对云原生应用的编程平台,来实现这个目标呢?
|
||||
|
||||
为了探究这个问题,我们需要进一步审视一下,现在云编程仍然有哪些,需要被新的抽象层次消除掉的复杂性。
|
||||
|
||||
## 对云原生编程平台的需求:能否解决云应用的复杂性?
|
||||
|
||||
在《人月神话》里,作者把复杂性分为两种:
|
||||
|
||||
- 一种叫做本质复杂性(Essential Complexity),指的是你要解决的问题本身的复杂性,是无法避免的。
|
||||
- 一种叫做附属复杂性(Accidental Complexity),是指我们在解决本质问题时,所采用的解决方案而引入的复杂性。在我们现在的系统中,90%的工作量都是用来解决附属复杂性的。
|
||||
|
||||
**我经常会被问到这样的问题:**做一个电商系统,成本是多少?而我给出的回答是:可能几千块,也可能很多亿。
|
||||
|
||||
如果你理解我的答案,那意味着比较理解当前软件编程的复杂性问题。因为软件系统的复杂性会随着规模急剧上升。
|
||||
|
||||
像阿里那样的电商系统,需要成千上万位工程师来维护。它在双11的时候,一天的成交量要达到几千亿,接受几亿用户的访问,在性能、可靠性、安全性、数据一致性等维度,都面临巨大的挑战。最重要的是,复杂性不是线性叠加的,可能是相乘的。
|
||||
|
||||
比如,当一个软件服务1万个用户的时候,增加一个功能可能需要100人天的话;针对服务于1百万用户的系统,增加同样的功能,可能需要几千到上万人天。同样的,如果功能不变,只是用户规模增加,你同样要花费很多人天来修改系统。那么你可以看出,整体的复杂性是多个因素相乘的结果,而不是简单相加。
|
||||
|
||||
**这跟云计算的初衷是相悖的。**云计算最早承诺,当我们需要更多计算资源的时候,简单增加一下就行了。然而,现有软件的架构,其实离这个目标还很远。那有没有可能把这些复杂性解耦,使得复杂性的增长变成线性或多项式级别(这里是借助算法复杂性的理论)的呢?
|
||||
|
||||
我再带你细化地看一下附属复杂性的一些构成,以便加深你对造成复杂性的根源的理解。
|
||||
|
||||
**1.基础设施的复杂性**
|
||||
|
||||
编写一个简单的程序,你只需要写写业务逻辑、处理少量数据,采用很简单的架构就行了。但是编写大型应用,你必须关心软件运行的基础设施,比如,你是用虚拟机还是容器?你还要关心很多技术构成部分,比如Kubernetes、队列、负载均衡器、网络、防火墙、服务发现、系统监控、安全、数据库、分片、各种优化,等等。
|
||||
|
||||
这些基础设施产生的复杂性,要花费你很多时间。像无服务器架构这样的技术,已经能够帮你屏蔽部分的复杂性,但还不够,仍然有很多复杂性因素需要找到解决方案。**举个例子。**
|
||||
|
||||
大多数商业应用都要很小心地处理跟数据库的关系,因为一旦数据出错(比如电商平台上的商品价格出错),就意味着重大的商业损失。你要根据应用需求设计数据库结构;要根据容量设计数据库分片的方案;要根据数据分析的需求设计数据仓库方案,以及对应的ETL程序。
|
||||
|
||||
**一个经常出现的情况是,**数据处理的逻辑分布在几个微服务中,要让它们对数据的修改满足事务特征,所以你要在代码里添加与分布式事务有关的逻辑。
|
||||
|
||||
那么,能否由云原生的开发平台来自动处理所有这些事情?我们只需要做业务对象(比如订单)的逻辑设计,把上述所有技术细节都隐藏起来呢?
|
||||
|
||||
**2.部署复杂性**
|
||||
|
||||
大型软件从编写代码,到部署,再到生产环境运行,是一个复杂的过程。
|
||||
|
||||
- 源代码可能有多个分支,需要进行合并;
|
||||
- 需要能够正确地编译;
|
||||
- 编译后的成果,要打包成可部署的对象,比如容器镜像;
|
||||
- 要对需要发布的模块进行测试,确保不会因为这次发布而造成很多bug;
|
||||
- 要对数据库的结构、基础数据等做必要的修改;
|
||||
- 新版本的软件上线,有时候不是全部上线,而是先让一部分用户使用,然后再针对所有用户;
|
||||
- 如果上线的特性出现问题,需要能够回滚到原来的版本。
|
||||
|
||||
是不是很复杂?那么,这样的复杂性,是否也可以由云原生的开发平台隐藏起来呢?
|
||||
|
||||
**3.API的复杂性**
|
||||
|
||||
我们在写云应用的时候,需要通过API来调用别的服务。你需要处理与之相关的各种问题,包括API访问的权限、访问次数的限制、错误处理、不同的RPC协议和调用约定,以及相同的功能在不同的云平台上使用不同的API。
|
||||
|
||||
**那么我的问题是:**能否让API调用跟普通语言的函数调用一样简单,让开发平台来处理上述复杂性呢?
|
||||
|
||||
**回答上面3个问题,并不简单。**但是,根据计算机语言的发展规律,我们总是会想办法建立更高的抽象层次,把复杂性隐藏在下层。就像高级语言隐藏了寄存器和内存管理的复杂性一样。
|
||||
|
||||
这样看来,解决云计算的复杂性,要求新的编程语言从更高的一个抽象层次上,做编译、转换和优化。我们只需要编写业务逻辑就可以了,当应用规模扩大时,真的只需要增加计算资源就行了;当应用需求变化时,也只需要修改业务逻辑,而不会引起技术细节上的很多工作量。能解决这些问题的软件,就是云原生的编程语言及其基础设施。
|
||||
|
||||
而现在的技术进步已经提供了很好的基础,容器技术、无服务器架构、处理大数据的Map/Reduce架构等,为云原生的编程语言准备好了素材。
|
||||
|
||||
**我相信,**在很多应用领域,我们其实可以降低对掌控力的要求,从而获取更大的方便性的。比如,对于大多数企业应用来说(比如ERP、CRM等),进行的都是以业务数据为核心的处理,也就是以数据库为核心的处理。
|
||||
|
||||
这些应用都具备相对一致的模式,通过更高的抽象层次,去除各种附属复杂性是有可能的。像这样的针对数据库编程的特定领域的云原生编程平台,会比较容易成功。
|
||||
|
||||
## 课程小结
|
||||
|
||||
本节课,我带你围绕“云编程”这个话题,剖析了云计算技术怎样和编程结合。我希望以下几个观点会对你有所启发:
|
||||
|
||||
1.编程环境会逐渐跟云平台结合起来,不仅仅是调用云上的资源,还可能实现编程环境本身的云化。
|
||||
|
||||
2.编译技术能够在不同的抽象层次上,处理计算问题,云计算技术也有类似的不同级别的抽象层次。一般来说,抽象层次越高,对技术细节的掌控力就越低,但是获得的便利性就越高。
|
||||
|
||||
3.附属复杂性会让成本和工作量呈指数级上升,云原生编程平台的核心任务是去除附属复杂性。而我对于在特定领域,成功应用云原生编程平台,持乐观态度。
|
||||
|
||||
## 一课一思
|
||||
|
||||
那么,如果以后的编程环境都搬到云上,你会喜欢吗?为什么?另外,你的实际项目中遇到过哪些复杂性问题属于附属复杂性?你认为该如何解决这些复杂性?
|
||||
|
||||
最后,感谢你的阅读,如果这篇文章让你有所收获,也欢迎你将它分享给更多的朋友。
|
||||
|
||||
|
201
极客时间专栏/编译原理之美/面向未来的编程语言/38 | 元编程:一边写程序,一边写语言.md
Normal file
201
极客时间专栏/编译原理之美/面向未来的编程语言/38 | 元编程:一边写程序,一边写语言.md
Normal file
@@ -0,0 +1,201 @@
|
||||
<audio id="audio" title="38 | 元编程:一边写程序,一边写语言" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d8/08/d8fc0f0a9db135e6bbdcc9d410191608.mp3"></audio>
|
||||
|
||||
今天,我再带你讨论一个很有趣的话题:元编程。把这个话题放在这一篇的压轴位置,也暗示了这个话题的重要性。
|
||||
|
||||
我估计很多同学会觉得元编程(Meta Programming)很神秘。编程,你不陌生,但什么是元编程呢?
|
||||
|
||||
**元编程是这样一种技术:**你可以让计算机程序来操纵程序,也就是说,用程序修改或生成程序。另一种说法是,具有元编程能力的语言,能够把程序当做数据来处理,从而让程序产生程序。
|
||||
|
||||
而元编程也有传统编程所不具备的好处:比如,可以用更简单的编码来实现某个功能,以及可以按需产生、完成某个功能的代码,从而让系统更有灵活性。
|
||||
|
||||
**某种意义上,**元编程让程序员拥有了语言设计者的一些权力。是不是很酷?你甚至可以说,普通程序员自己写程序,文艺程序员让程序写程序。
|
||||
|
||||
那么本节课,我会带你通过实际的例子,详细地来理解什么是元编程,然后探讨带有元编程能力的语言的特性,以及与编译技术的关系。通过这样的讨论,我希望你能理解元编程的思维,并利用编译技术和元编程的思维,提升自己的编程水平。
|
||||
|
||||
## 从Lisp语言了解元编程
|
||||
|
||||
说起元编程,追溯源头,应该追到Lisp语言。这门语言其实没有复杂的语法结构,仅有的语法结构就是一个个函数嵌套的调用,就像下面的表达式,其中“+”和“*”也是函数,并不是其他语言中的操作符:
|
||||
|
||||
```
|
||||
(+ 2 (* 3 5)) //对2和3求和,这里+是一个函数,并不是操作符
|
||||
|
||||
```
|
||||
|
||||
你会发现,如果解析Lisp语言形成AST,是特别简单的事情,基本上括号嵌套的结构,就是AST的树状结构(其实,你让Antlr打印输出AST的时候,它缺省就是按照Lisp的格式输出的,括号嵌套括号)。这也是Lisp容易支持元编程的根本原因,你实际上可以通过程序来生成,或修改AST。
|
||||
|
||||
我采用了Common Lisp的一个实现,叫做SBCL。在macOS下,你可以用“brew install sbcl”来安装它;而在Windows平台,你需要到sbcl.org去下载安装。在命令行输入sbcl,就可以进入它的REPL,你可以试着输入刚才的代码运行一下。
|
||||
|
||||
在Lisp中,你可以把(+ 2 (* 3 5))看做一段代码,也可以看做是一个列表数据。所以,你可以生成这样一组数据,然后作为代码执行。**这就是Lisp的宏功能。**
|
||||
|
||||
我们通过一个例子来看一下,宏跟普通的函数有什么不同。下面两段代码分别是用Java和Common Lisp写的,都是求一组数据的最大值。
|
||||
|
||||
Java版本:
|
||||
|
||||
```
|
||||
public static int max(int[] numbers) {
|
||||
int rtn = numbers[0];
|
||||
for (int i = 1;i < numbers.length; i++){
|
||||
if (numbers[i] > rtn)
|
||||
rtn = numbers[i];
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Common Lisp版本:
|
||||
|
||||
```
|
||||
(defun mymax1 (list)
|
||||
(let ((rtn (first list))) ;让rtn等于list的第一个元素
|
||||
(do ((i 1 (1+ i))) ;做一个循环,让i从1开始,每次加1
|
||||
((>= i (length list)) rtn) ;循环终止条件:i>=list的长度
|
||||
(when (> (nth i list) rtn) ;如果list的第i个元素 > rtn
|
||||
(setf rtn (nth i list)))))) ;让rtn等于list的第i个元素
|
||||
|
||||
```
|
||||
|
||||
那么,如果写一个函数,去求一组数据的最小值,你该怎么做呢?采用普通的编程方法,你会重写一个函数,里面大部分代码都跟求最大值的代码一样,只是把其中的一个“>”改为"<"。
|
||||
|
||||
这样的话,代码佷冗余。那么,能不能实现代码复用呢?这一点,用普通的编程方法是做不到的,你需要利用元编程技术。我们用Lisp的宏来实现一下:
|
||||
|
||||
```
|
||||
(defmacro maxmin(list pred)
|
||||
`(let ((rtn (first ,list)))
|
||||
(do ((i 1 (1+ i)))
|
||||
((>= i (length ,list)) rtn)
|
||||
(when (,pred (nth i ,list) rtn)
|
||||
(setf rtn (nth i ,list))))))
|
||||
|
||||
(defun mymax2 (list)
|
||||
(maxmin list >))
|
||||
|
||||
(defun mymin2 (list)
|
||||
(maxmin list <))
|
||||
|
||||
```
|
||||
|
||||
在宏中,到底使用“>” 还是使用“<”,是可以作为参数传入的。你可以看一下函数mymax2和mymin2的定义。这样,宏展开后,就形成了不同的代码。你可以敲入下面的命令,显示一下宏展开后的效果(跟我们前面定义的mymax1函数是完全一样的)。
|
||||
|
||||
```
|
||||
(macroexpand-1 '(maxmin list >))
|
||||
|
||||
```
|
||||
|
||||
在Lisp运行时,会先进行宏展开,然后再编译或解释执行所生成的代码。通过这个例子,你是否理解了“用程序写程序”的含义呢?这种元编程技术用好了以后,会让代码特别精简,产生很多神奇的效果。
|
||||
|
||||
初步了解了元编程的含义之后,你可能会问,我们毕竟不熟悉Lisp语言,目前那些常见的语言有没有元编程机制呢?我们又该如何加以利用呢?
|
||||
|
||||
## 不同语言的元编程机制
|
||||
|
||||
首先,我们回到元编程的定义上来。比较狭义的定义认为,一门语言要像Lisp那样,要能够把程序当做数据来操作,这样才算是具备元编程的能力。
|
||||
|
||||
但是,你学过编译原理就知道,在CPU眼里,程序本来就是数据。
|
||||
|
||||
我们在[34讲](https://time.geekbang.org/column/article/164017),曾经直接把二进制机器码放到内存,然后作为函数调用执行。有一位同学在评论区留言说,这看上去就是把程序当数据处理。在[32讲](https://time.geekbang.org/column/article/161944)中,我们也曾生成字节码,并动态加载进JVM中运行。这也是把程序当数据处理。
|
||||
|
||||
实际上,整个课程,都是在把程序当做数据来处理。你先把文本形式的代码变成Token,再变成AST,然后是IR,最后是汇编代码和机器代码。所以,有的研究者认为,编写编译器、汇编器、解释器、链接器、加载器、调试器,实际上都是在做元编程的工作,你可以参考一下[这篇文章](https://cs.lmu.edu/~ray/notes/metaprogramming/)。
|
||||
|
||||
**从这里,你应该得到一个启示:**学习汇编技术以后,你应该有更强的自信,去发掘你所采用的语言的元编程能力,从而去实现一些高级的功能。
|
||||
|
||||
当然了,通常我们说某个语言的元编程能力,要求并不高,没必要都去实现一个编译器(当然,如果必须要实现,你还是能做到的),而是利用语言本身的特性来操纵程序。**这又分为两个级别:**
|
||||
|
||||
- 如果一门语言写的程序,能够报告它自身的信息,这叫做自省(introspection)。
|
||||
- 如果能够再进一步,操纵它自身,那就更高级一些,这叫做反射(reflection)。
|
||||
|
||||
那么你常见的语言,都具备哪些元编程能力呢?
|
||||
|
||||
**1. JavaScript**
|
||||
|
||||
从代码的可操纵性来看,JavaScript是很灵活的,可以给高水平的程序员,留下充分发挥的空间。JavaScript的对象就跟一个字典差不多,你可以随时给它添加或修改某个属性,你也可以通过拼接字符串,形成一段JavaScript代码,然后再用eval()解释执行。JavaScript还提供了一个Reflect对象,帮你更方便地操纵对象。
|
||||
|
||||
实际上,JavaScript被认为是继承了Lisp衣钵的几门语言之一,因为JavaScript的对象确实就是个可以随意修改的数据结构。这也难怪有人用JavaScript,实现了很多优秀的框架,比如React和Vue。
|
||||
|
||||
**2. Java**
|
||||
|
||||
从元编程的定义来看,Java的反射机制就算是一种元编程机制。你可以查询一个对象的属性和方法,你也可以用程序按需生成对象和方法来处理某些问题。
|
||||
|
||||
我们[32讲](https://time.geekbang.org/column/article/161944)中的字节码生成技术,也是Java可以采用的元编程技术。你再配合上注解机制或者配置文件,就能实现类似Spring的功能。可以说,Spring是采用了元编程技术的典范。
|
||||
|
||||
**3. Clojure**
|
||||
|
||||
Clojure语言是在JVM上,运行的一个现代版本的Lisp语言,所以它也继承了Lisp的元编程机制。
|
||||
|
||||
**4. Ruby**
|
||||
|
||||
喜欢Ruby语言的人很多,一个重要原因在于Ruby的元编程能力。而Ruby也声称自己继承了Lisp语言的精髓。其实,它的元编程能力表现在,能够在运行时,随时修改对象的属性和方法。虽然实现方式不一样,但原理和JavaScript其实是很像的。
|
||||
|
||||
元编程技术使Ruby语言能够以很简单的方式快速实现功能,但因为Ruby过于动态,所以编译优化比较困难,性能比较差。Twitter最早是基于Ruby写的,但后来由于性能原因改成了Java。同样是动态性很强的语言,JavaScript在浏览器里使用普遍,厂商们做了大量的投入进行优化,因此,JavaScript在大部分情况下的性能,比Ruby高很多,有的[测试用例](https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/node-yarv.html)会高50倍以上。所以近几年,Ruby的流行度在下降。**这也侧面说明了编译器后端技术的重要性。**
|
||||
|
||||
**5. C++语言**
|
||||
|
||||
C++语言也有元编程功能,最主要的就是模板(Template)技术。
|
||||
|
||||
C++标准库里的很多工具,都是用模板技术来写的,这部分功能叫做STL(Standard Template Library),其中常用的是vector、map、set这些集合类。**它们的特点是,**都能保存各种类型的数据。
|
||||
|
||||
看上去像是Java的泛型,如vector< T >,但C++和Java的实现机制是非常不同的。我们在[35讲](https://time.geekbang.org/column/article/165078)中曾经提到Java的泛型,指出Java的泛型只是做了类型检查,实际上保存的都是Object对象的引用,List< Integer >和List< String >对应的字节码是相同的。
|
||||
|
||||
C++的模板则不一样。它像Lisp的宏一样,能够在编译期展开,生成C++代码再编译。vector< double >和vector< long >所生成的源代码是不同的,编译后的目标代码,当然也是不同的。
|
||||
|
||||
常见语言的元编程特性,你现在已经有所了解了。但是,关于是否应该用元编程的方法写程序,以及如何利用元编程方法,却存在一些争议。
|
||||
|
||||
## 是否该使用元编程技术?
|
||||
|
||||
我们看到,很多支持元编程技术的语言,都声称继承了Lisp的设计思想。Lisp语言也一致被认为是编程高手应该去使用的语言。可有一个悖论是,Lisp语言至今也还很小众。
|
||||
|
||||
Lisp语言的倡导者之一,Paul Graham,在互联网发展的早期,曾经用Lisp编写了一个互联网软件Viaweb,后来被Yahoo收购。但Yahoo收购以后,就用C++重新改写了。**问题是:**如果Lisp这么优秀,为什么会被替换掉呢?
|
||||
|
||||
**所以,一方面,**Lisp受到很多极客的推崇,比如自由软件的领袖Richard Stallman就是Lisp的倡导者,他写的Emacs编辑器就采用了Lisp来自动实现很多功能。
|
||||
|
||||
**另一方面,**Lisp却没有成为被大多数程序员所接受的语言。这该怎么解释呢?难道普通程序员不聪明,以至于没有办法掌握宏?进一步说,我们应该怎样看待元编程这种酷炫的技术呢?该不该用Lisp的宏那样的机制来编程呢?
|
||||
|
||||
程序员的圈子里,争论这个问题,争论了很多年。**我比较赞同的一个看法是这样的:**首先,像Lisp宏这样的元编程是很有用的,你可以用宏写出非常巧妙的库和框架,来给普通的程序员来用。但一个人写的宏对另外的人来说,确实是比较难懂、难维护的。从软件开发管理的角度看,难以维护的宏不是好事情。
|
||||
|
||||
**所以,我的结论是:**
|
||||
|
||||
首先,元编程还是比较高级的程序员的工作,就像比较高级的程序员才能写编译器一样。元编程其实比写编译器简单,但还是比一般的编程要难。
|
||||
|
||||
第二,如果你要用到元编程技术,最好所提供的软件是容易学习、维护良好的,就像React、Vue和Spring那样。这样,其他程序员只需要使用就行了,不必承担维护的职责。
|
||||
|
||||
**其实,我们学编译技术也是一样的。**你不能指望公司或者项目组的每个人,都用编译技术写一个DSL或者写一个工具。毕竟维护这样的代码有一定的门槛,使用这些工具的人也有一定的学习成本。我曾经看到社区里有工程师抱怨,某国外大的互联网公司里面DSL泛滥,新加入的成员学习成本很高。所以,一个DSL也好、一套类库也好,必须提供的价值远远大于学习成本,才能被广泛接受。
|
||||
|
||||
为了降低使用者的学习成本,框架、工具的接口设计应该非常友好。**怎样才算是友好呢?**我们可以借鉴特定领域语言(DSL)的思路。
|
||||
|
||||
## 发明自己的特定领域语言(DSL)
|
||||
|
||||
框架和工具的设计者,为了解决某一个特定领域的问题,需要仔细设计自己的接口。好的接口设计是对领域问题的抽象,并通过这种抽象屏蔽了底层的技术细节。这跟上一讲我们提到语言设计的抽象原则是一样的。这样的面向领域的、设计良好的接口,很多情况下都表现为DSL,例如React的JSX、Spring的配置文件或注解。
|
||||
|
||||
DSL既然叫做语言,那么就应该具备语言设计的特征:通过简单的上层语义,屏蔽下层的技术细节,降低认知成本。
|
||||
|
||||
我很早以前就在BPM领域工作。像JBPM这样的开源软件,都提供了一个定义流程的模板,也就是DSL。**这种DSL的优点是:**你只需要了解与业务流程这个领域有关的知识,就可以定义一个流程,不需要知道流程实现的细节,学习成本很低。
|
||||
|
||||
[15讲](https://time.geekbang.org/column/article/136557)的报表工具的例子,也提供了一个报表模板的参考设计,这也是一个DSL。使用这个DSL的人也不需要了解报表实现的细节,也是符合抽象原则的。
|
||||
|
||||
**我们在日常工作中,还会发现很多这样的需求。**你会想,如果有一门专门干这个事情的DSL就好了。比如,前两年我参与过一个儿童教育项目,教师需要一些带有动画的课件。如果要让一个卡通人物动起来,动画设计人员需要做很多繁琐的工作。当时就想,如果有一个语言,能够驱动这些卡通人物,让它做什么动作就做什么动作,屏蔽底层的技术复杂性,那么那些老师们就可以自己做动画了,充分发挥自己的创造力,而不需要求助于专门的技术人员。
|
||||
|
||||
当然,要实现这种DSL,有时候可以借助语言自带的元编程能力,就像React用JavaScript就能实现自己的DSL。但如果DSL的难度比较高,那还是要实现一个编译器,这可能就是终极的元编程技能了吧!
|
||||
|
||||
## 课程小结
|
||||
|
||||
本节课,我带你了解了元编程这个话题,并把它跟编译原理联系在一起,做了一些讨论。学习编译原理的人,某种意义上都是语言的设计者。而元编程,也是让程序员具有语言设计者的能力。所以,你可以利用自己关于编译的知识,来深入掌握自己所采用的语言的元编程能力。
|
||||
|
||||
**我希望你能记住几个要点:**
|
||||
|
||||
<li>
|
||||
元编程是指用程序操纵程序的能力,也就是用程序修改或者生成程序。也有人用另外的表述方式,认为具有元编程能力的语言,能够把程序当做数据来处理,典型的代表是Lisp语言。
|
||||
</li>
|
||||
<li>
|
||||
编译技术的本质就是把程序当做数据处理,所以你可以用编译技术的视角考察各种语言是如何实现元编程的。
|
||||
</li>
|
||||
<li>
|
||||
采用元编程技术,要保证所实现的软件是容易学习、维护良好的。
|
||||
</li>
|
||||
<li>
|
||||
好的DSL能够抽象出领域的特点,不需要使用者关心下层的技术细节。DSL可以用元编程技术实现,也可以用我们本课程的编译技术实现。
|
||||
</li>
|
||||
|
||||
## 一课一思
|
||||
|
||||
你之前了解过元编程技术吗?你曾经用元编程技术解决过什么问题呢?欢迎在留言区分享。
|
||||
|
||||
最后,感谢你的阅读,如果这篇文章让你有所收获,也欢迎你将它分享给更多的朋友。
|
Reference in New Issue
Block a user