本周是编程范式系列的最后一次分享,让我们拉长视角,看向远方,进入『元编程』的领域,在《冒号课堂》中为它起了个很酷的名字:『超级范式』。
从通用语言到领域语言
先给大家做一个小练习:
以下哪些属于编程语言:
A.Java B.Html C.Spring Bean Definition D.Comment
答案是ABC,从中,我们可以感受到编程语言的一些特性:
- 必需:有语法结构、可运行
- 可选:可编译、可调试、可扩展
类似人类语言,作为与计算机的唯一沟通渠道,编程语言的发展也是多种多样的。
多样性体现在哪里呢?可以是解决的问题不同,可以是运行的环境不同,也可以是用户群不同等。
我们可以用一个词来概括这种不同,就是『领域( Domain )』。而仅服务于某一领域的语言,称为『领域语言( DSL )』。
领域语言示例
上面提到的 Html、Spring Bean Definition 以及 SQL 都是领域语言,这里列几个更有趣的。
Gradle 是一个灵活的通用构建工具,让 Java 项目在构建上有了跨越性发展。可以看到采用领域语言后,自有度有明显的提高,开发人员也更容易进行扩展。
println 'This is executed during the configuration phase.'
task configured {
println 'This is also executed during the configuration phase.'
}
task testBoth {
doFirst {
println 'This is executed first during the execution phase.'
}
println 'This is executed during the configuration phase as well.'
}
Cucumber 是一个能够理解用普通语言描述的自动化测试工具,在普通测试工具可运行可跟踪的基础上,案例可读性的大大提升了。
功能: 用户登录
为了能够浏览网站只对在线会员可见的那些内容
作为一名访客
我希望能够登录
场景: 用户登录功能
假如 没有这个用户
当 我以这个身份登录
那么 我应该看到<用户名或密码错误>的提示信息
而且 我应该尚未登录
在领域语言的帮助下,编程语言在『人性化』的方向上迈进了一步。
从领域语言到元编程
前面介绍了领域语言的概念,我们通常会将其列到"工具"的范畴。如何成为一个范式,就要引入『超级范式』元编程了。
元编程是编写、操纵程序的程序。在传统编程中,运算是动态的,但程序本身是静态的;在元编程中,二者都是动态的。
下面举个处理客户交易单的例子,采用通用语言 Scala 实现如下。
val t1 = FixedIncomeTradeImpl {
tradeingAccount = NOMURA,
instrument = IBM,
currency = USD,
market = NYSE,
quantity = 100,
unitPrice = 42
}
如果我们站在编程外行的角度看,这段代码是存在不少问题的。
- 等号的作用与常识不符,一般理解为比较,这里是赋值
- 有非常多令人费解的符号,比如 {}
- 没有表达出输入间的关联,比如 USD 应该是用来描述 unitPrice 的。
下面使用领域语言进行优化,更加符合自然语言的表达。
val equityTrade =
100 discount_bonds IBM
for_client NOMURA on NYSE at 42.ccy(USD)
具体实现上,采用了 Scala 的"隐式转换"特性,这是一种元编程的方法。
object TradeImplicits {
class InstrumentHelper(qty: Quantity) {
def discount_bonds(db: DiscountBond) = (db, qty)
def equities(eq: Equity) = (eq, qty)
}
}
目前来看,元编程还是比较依赖于语言层面的支持,以 Ruby 、 Groovy 为代表的动态语言支持最佳, Scala 提供了部分支持。但是从总体看,更为主流的Java语言尚未提供支持。比较可行方法是采用 Scala 、 Groovy 等 jvm 语言,嵌入 Java 代码中进行实现。
小结
元编程从实现复杂看并不亚于通用语言,篇幅有限无法深入,对细节感兴趣建议大家阅读《领域专用语言实战》。
最后,想用 Martin Fowler 的一段访谈来小结下。原文地址
InfoQ:面对某个领域的多种变化,我们应该怎样利用领域特定语言,比方说在开发某个产品线的时候?
MF:和其它许多事情一样,我不认为领域特定语言的代码和其它代码有多大的区别。领域特定语言能够表达人们更深层次的思想是因为,和普通的命令-查询式API相比,领域特定语言以一种更加自然的方式表述领域内的不同变化。
领域语言的价值在于更加『人性』,更易于使用。其出发点是一种『减法思维』,通过限定外部使用范围而获得了更好的内在价值。
InfoQ:领域特定语言看起来很擅长解决某个系统内的子域问题,比如说系统配置管理。项目组该如何在创造一种能够覆盖多个领域的复杂庞大的领域特定语言和多个小领域特定语言之间做出取舍?
MF:我们其实非常鼓励使用许多小的领域特定语言。比方说你不会试图去扩展正则表达式来设计HTML页面,也不会用CSS来进行文本匹配。对我们来说,让领域特定语言与众不同的正是其有限表达这一特性。
在使用上,领域语言之间可以互相组合,和而不同,目标是发挥 1+1>2 的效果。
InfoQ:一些组织和项目不接受领域特定语言的主要原因是什么?是因为复杂性,还是因为缺乏对问题域和解决方案域的了解?
RJP:除了新的工具和开发流程这些问题,使用领域特定语言还需要用新的思维方式思考问题。关于哪些是构成好的领域特定语言设计的因素,还有待于我们去逐步发掘。当然也少不了“我们很久以前就听说过”的关于业务可读/可写程序的观点以及它所引发的怀疑。
我最早接触『领域语言』概念,是在学习 Ruby 语言的过程中,当时的感受完全是颠覆式。由于涉及到思维方式的转换,也是限制领域语言发展的一个重要因素。目前,领域语言并未成为业界的主流解决方案,但其思考之深刻、思路之精妙让人非常惊艳,码农们如果希望开拓视野、提升自我,强烈建议学习。