目录
0 序言
近年来,随着移动应用、人工智能、区块链、量子计算、芯片逻辑、音乐工程等领域的蓬勃发展,软件在各行各业的应用得到爆发式的增长,各行各业的研究人员和领域专家加入到软件开发的队伍,比如:芯片研发工程师需要通过编程来写芯片逻辑,人工智能算法研究人员需要通过编程写计算逻辑,音乐工程师需要通过编程构建音乐的交互式系统。这些领域专家不是计算机科班出身,但是他们对自己的领域研究非常深入,他们非常期待能有一门面向领域的编程语言来帮助他们屏蔽计算机底层的复杂度,聚焦自己的领域,高效的进行编程。
在这样的背景下,领域特定语言如雨后春笋般在各行各业兴起。比如苹果在 2019 年推出基于 Swift 的 UI 描述语言 SwiftUI,FaceBook 在 2019 年推出安全灵活的 Libra 区块链编程语言 Move,微软在 2017 年推出量子计算编程语言 Q#,伯克利使用高层次构建语言 Chisel(基于 Scala 语言扩展)编写 RISC-V 的芯片逻辑,因此业界对领域特定语言的研究也进入了一个高峰期。其实领域特定语言(DSL)的概念并不是新的概念,它最早可以追溯到上世纪 50 年代,比如科学计算领域使用的程式语言 Fortran。而领域特定语言真正得到快速发展,还是得力于领域特定语言在理论、工程技术、工具等方面的发展。
本文主要介绍领域特定语言(DSL)的基本概念、分类以及优劣势,并通过一个简单的例子入手讲解如何构建一个领域特定语言,并且结合 EDSL 的优势以及发展趋势,推导出宿主语言的特性需求,最后通过一个工业界的典型 DSL——SwiftUI 讲解元语言和对象语言的配合关系。
当然如果读者想了解更多有关 DSL 相关的技术内容,可以持续关注 SIG-DSL 扩展发表的相关文章。我们也欢迎读者朋友加入我们的 编程语言技术社区 SIG-DSL 扩展小组 ,和我们一起深入探讨 DSL 相关技术。
加入方式:文末有小助手微信,添加并备注加入 SIG-DSL 扩展
1 什么是 DSL
1.1 生活中的 DSL
单看上面的叙述,对于初次接触这一领域的你来说,大概率还是会迷惑什么是 DSL,引用维基的定义:领域特定语言指的就是专注于某个应用程序领域的计算机语言 [1]。现在我们将这一句话换一种说法:领域特定语言,就是让解决领域特定问题的编程模型使用领域专有的语言来 “说话”。即用代码来说话,其目的是让用户通过阅读代码就可以明白建模里的业务规则,而非一堆架构图上的框框和箭头。
如果要弄懂这个概念,首先我们要明白什么是领域专有的语言,这里举用一个和 Debasish Ghosh 所提到的买咖啡相似的用例 [2],这就如同,当你走进奶茶店点上一杯 “正常冰 3 分糖的多肉葡萄”,那么店员就会准确无误的给你制作一杯,550ml 当季葡萄果肉加绿茶底并配上醇香芝士再加上满满一杯子冰的饮品。你无需过多的描述每一项,只需按照菜单规则的名字来点单,而且这一说法只适用于奶茶饮品领域。
在这个场景中,点单的过程就是用特定领域的语言来表达一个问题,店员根据你的订单,把饮品做出来是对于问题的解答。这种从问题域映射到解答域的实现模型就是 DSL 的基本思路。如果把上述的场景做成软件,那么客人们点单时所用到的语言就是你要找的 DSL。
1.2 DSL 与 GPL
认识一个新事物,我常常会从正面和对立面两个方向来看,正面就如上段内容所述,而与 DSL 处在对立面的就是 GPL(General Purpose Language),即通用编程语言,像我们熟悉的 Java、Python 或者 C 语言等,这类语言没有特定的使用场景,你可以拿他们来实现任意领域的计算机程序,他们的出现并不是为了解决特定领域的问题,我们称这种能力为具有通用的表达力。
与其相反,DSL 都有相对应的专门的领域,在生活中常见的 DSL 如:HTML 是我们熟知的用于描述 Web 页面的标记语言;CSS 是用于描述页面样式的语言;SQL 是用于创建和检索关系型数据库的语言;这些语言都有一个特点,就是只能解决其特定领域的问题,正如你不可能拿一把菜刀来修汽车一样,你同样无法单独用 CSS 来搭建出一套证券交易系统,这也是正是 Martin Fowler 提出的 DSL 与 GPL 的根本区别,即 Limited Expressivity (有限的表达力)[3]。
通用编程语言可以给任何事物建模,而 DSL 只适合给一个专门的领域建模。对于 DSL 与 GPL 的区别,可以借用编程语言 Lab 架构师徐潇在 QCon 大会上提出的一个例子来阐明,他将两种语言比作建筑材料,GPL 如水泥,而 DSL 如水泥做好的 “预制件”。
![图片](https://i-blog.csdnimg.cn/blog_migrate/97a507c4682f2fa3a11e3b2b2cfee042.jpeg)
水泥可以做成任何建筑物,但是水泥管只能限定于做管道,而无法应用成楼板或其他建筑组件。但是当我们想要做管道的时候,我们更愿意接受可以直接使用的 “预制件”,而不是每次都从水泥铸型开始。这一例子也正是生动形象的描述出了 DSL 与 GPL 在表达力和易用度上的根本差异。
1.3 DSL 的分类
DSL 本身是一个宽泛的概念,它还可以细化成外部 DSL 和内部 DSL(Embedded DSL,简称 eDSL),像上述所列出的 HTML、CSS 以及 SQL,都是完全独立的语言,他们不依托于任何一门通用的宿主语言,我们称这种 DSL 为外部 DSL。
与之相对应的,如在 UI 领域,基于 Swift 建立的 SwiftUI;基于 Ruby 设计的 IOS 依赖管理组件 CocoaPods;基于 Kotlin 或者 Groovy 设计的 Android 主流编译工具 Gradle;这些在现有宿主语言上实现的 DSL,我们称之为 eDSL。
关于 eDSL 的详细介绍与优势对比,我们将在后续章节详细展开。
1.4 DSL 的优势与劣势
既然 DSL 的应用如此广泛,那么是否所有的项目都应该基于 DSL 开发?答案显然是否定的,没有一门技术具备完美解决所有问题的能力,DSL 同样在显著的优势下暗藏着危险,在确认是否引入 DSL 之前,我们需要评估一下 DSL 的优缺点。
首先从 DSL 所扮演的角色来看,由于 DSL 更趋向于解决固定范围的事情,所以基于 DSL 的开发关注点会更加的集中,它仅需要提供范围足够小的一个领域的 API 接口,并且会将领域中的专有概念抽象成更加精确的统一语言来表达其语义(这一点将会在下一章节中说明)[2],这也就意味着 DSL 在有限的范围内具有更强的表现力,且可以消减开发人员与领域专家之间沟通的代沟。
同时 DSL 是一层更高层的抽象,并不需要使用者懂得基于低层次语言的实现手法,所以对于没有编程经验和背景的领域用户来说,稍加学习便可以使用其来实现自己想要的程序,这也正是 DSL 最大的魅力。
当然缺点也同样存在,首先不可规避的是 DSL 是一种语言设计类的工作,而要从零设计一门语言,需要设计相关的语法、语义,同时还需实现其对应的编译器(语法解析、类型系统和代码生成等),这并不是一个靠堆积人力就能解决的问题,而是对复杂度和专业度要求很高的工作,所以越来越多的人会将 DSL “寄身”于别的宿主语言,也就是所谓的 eDSL。即便如此,eDSL 的设计难度还是很高,对于 eDSL 的介绍将在第三章展开,这也将是本文的重点内容。
再者就是一些配套开发工具的开