Introduction - DSL

3/30/2025 12:57:32 AM

起因

在学期初的时候,跟着大三上学期教计网的老师去做项目,遂接触了一个特别的玩意: P4
这个东西和这个学期(大三下)的云计算中提到的 SDN(软件定义网络) 有点儿关联,但这里要提的倒也并不是SDN。
在写了一段时间这个玩意的代码以后,我大抵才真正明白了先前听其他人挂在嘴边的 DSL 到底代表了什么。

那么它到底是什么呢

官方解释

百度百科和Wiki上抄的:
DSL(Domain-Specific Language)即领域特定语言,是一种专门为特定领域设计的计算机语言。与通用编程语言(如C、Java、Python等)不同,DSL通常针对特定问题域进行优化,提供更高效、更易用的语法和功能。DSL可以是独立的编程语言,也可以是嵌入在其他语言中的子集。常见的DSL包括SQL(用于数据库查询)、HTML(用于网页标记)、CSS(用于样式表)等。

例子

P4

这个玩意天生就是为了网络的数据包处理而设计的,虽然在相当程度上它的语法借鉴了C系语言的设计,但是它所构建出来的程序却是完完全全为了网络而服务的。
它没有任何乱七八糟的 stdio printf,甚至连 for while 这样的循环语句都没有。
然而它定义了一系列用来解析、处理、转发数据包的语法,诸如 parser control table 之类的概念。
这些概念在网络中是非常常见的,甚至可以说是 SDN 的核心。

SQL

这个语言或许更贴近写Java的做后端的宝宝体质。
SELECT INSERT UPDATE DELETE 这样的语法,都是为了操作数据库而生的。
它的设计理念就是为了让开发者更方便地操作数据库中的数据。
同理,倘若它脱离了DB这块领域,就会变得毫无价值。

HTML/CSS

这是更适合前端宝宝体质的两个万恶之源语言。(如果你称之为语言的话)
但恐怕不是专门做前端的同学也多多少少接触过,当你在桌面端打开网页以后摁下F12,显现出来的那一坨东西就是HTML
CSS则更加狭隘,它是专门用来为HTML提供样式的。
这两个或许是我们最最最常接触到的两个DSL了,但我们往往不会在意DSL这个东西。

优势在哪里?

说了这么多,我们其实不难发现,DSL不过是我们预先定义好的一套规则和语法,它能帮助开发者在最大程度上排除掉与领域核心无关的瓜葛,让最终代码最大程度上贴近领域中的概念。

在前期的开发中,我们可以通过DSL来快速地搭建起一个框架,甚至是一个完整的业务系统。

在后期的维护中,无论是新人还是老人,都会因为DSL的存在而更容易理解代码。从而快速定位到需要修改的部分,从而能以最快的速度重建新的业务。

“通用” 的 DSL ?

谈到这里,不知有人是否有过这样的想法:
是否存在这样一种DSL或设计,能在任何领域中都可以帮上忙?

事实上,DSL的定义本身就回答了这个问题:
DSL一定是为某个特定的问题域而生的。一旦问题域发生变化,DSL的设计也必须随之变化以贴合新的领域。
哪怕很多看上去一模一样的领域,但是各自因为需求或者背景的不同,所需要的DSL也会有很大的差异。

因此,不存在“通用”的DSL。那是 通用编程语言 们要做的事情。

我也想嗯造个DSL!

大众如HTML,小众如P4,我们可以发现,DSL其实好像一抓一大把,只不过大多数人没有意识到而已。

如果你真的因为我上面说的这些胡言乱语而动了心思,想为自个的项目或者业务系统造一个DSL出来的话,不妨先考虑一些前置条件。

为什么你会需要一个DSL?

选择、使用,亦或者造一个DSL。一定会有一个前提:你处在一个足够复杂的问题域中。

这个问题域,可以是一个个人项目,一个公司的业务系统,亦或者是一个产品。
换言之,如果你所处的领域足够复杂,足够庞大,甚至是足够混乱,那么你就会需要一个 DSL 来帮助你的团队统一口径,理清思路。

再定义

现存的DSL,我们大体可以分为两种:

孰优孰劣?

事实上,大多数项目体量与团队能力其实并不能支撑他们去造一个独立的DSL出来,解析器、编译器、运行时环境等等这些东西都是需要重点考虑的内容,可能会引入远远超出预期的复杂度。

但是,如果你的项目与业务的复杂度真的值得你真的投入大量人力物力时间成本去单独造一个DSL出来,它带来的价值是难以估量的,甚至比起你目标作用的这个项目来说,它的潜在价值可能还要更大。

对于大多数人来说,使用现有的DSL,亦或者选择后者——Internal DSL,可能是更为明智的选择。

我应该怎么做?

如果你真的认真看了上面的内容,你就会发现 这个文字的作者其实没有能力带你从零开始构造一个船新的外部DSL出来(✗) 还是内部DSL在多数时候会合理一点。
所以我们在这里就把重点放在内部DSL上吧。

理解领域

就如我上面所述,DSL的核心在于对领域的理解。
DSL所能展现的,一定是领域最为核心的概念,如果缺乏对领域的本质认识,构建出来的DSL会很快退化。

比如银行的业务运作,远远不止是存取款、转账、贷款等这些概念,甚至连账户、客户、交易等概念都只是表象,它们背后还有隐藏着更为复杂的业务逻辑。
再复杂到我上面所述的P4。如果不熟悉计网,很多网络领域的核心概念都无法理解。

具体如何接触、分析、理解领域并不是本篇文字的重点。我只是在这里提一嘴(✗)

语言选择

按定义来讲,内部DSL其实无关乎语言的选择,因为你的重点应该是你对领域的理解,而非具体依赖于哪个语言。
但说实在话,一个好的语言,它的表达能力 (糖度) 确实能为你构建DSL提供很大的帮助。

对于我个人而言,我会推荐 C# Kotlin F# 这三种语言。
这三者都较好地吸收与应用了函数式编程范式的一些精华,且能更好做到工程化与易用性的平衡。

举个例子

// Data source
List<int> numbers = [1, 2, 3, 4, 5];

// LINQ query
var evenNumbersMethod = numbers.Where(n => n % 2 == 0);

// Equivalent syntax
var evenNumbers = from n in numbers
                  where n % 2 == 0
                  select n;

上述代码展示了C#中的LINQ语法,它允许我们以声明式的方式查询数据源。
它的语法与SQL非常相似,且它的设计理念就是为了让开发者更方便地操作数据。

我们可以称LINQ的设计即为一个内部DSL,而这个DSL的领域是 数据查询

和DDD的关系

天呐这个人又在聊DDD,跑了跑了

所谓 DDD ,即 Domain Driven Design(领域驱动设计)

我们完全可以说,DSL是DDD的核心目标。

DDD的核心在于对领域的理解与建模,通过对领域的深入理解来指导软件设计与开发。它最最最最最核心的概念是 Domain Model(领域模型) 。采用DDD的目标也是得到这样一个领域模型。

而DSL,就是通过DDD得出的领域模型的具体实现。它将领域模型中的概念与关系转化为可执行的代码,从而实现对领域的建模与操作。

所以,得出DSL的过程,其实就是利用DDD不断迭代与完善领域模型的过程。

当然,DDD被提出的同时,也给出了一些推荐的设计原则与模式。
具体如何使用这些原则与模式来设计你的代码,我们日后再谈吧。