6.1 为何选择Scala

Scala同面向对象语言一样,拥有丰富全面的抽象机制,塑造了DSL的简洁度和表现力。DSL不能脱离其实现模型而独立存在,我们说过,DSL是罩在实现模型外面的一层门面。本章将分析Scala发展为宿主语言的历程,围绕设计底层模型和外层DSL两方面展开。表6-1列举了Scala常用于DSL设计的一些代表性语言特性。

表6-1 用于DSL设计的代表性Scala特性

语言特性 Scala的具体做法
灵活的语法 Scala的表层语法简练,有许多手段可使DSL更贴近真实的领域用语 例如: - 可省略方法调用的点符号 - 分号推断 - 中缀运算符 - 可省略方法调用的圆括号
可扩展的对象系统 Scala是面向对象的。它与Java使用相同的对象模型,然后利用自身先进的类型系统,在很多方面进行了扩展 Scala的对象语义: - trait的用途是基于mixin的实现继承(见6.10节文献[12]) - 抽象类型成员和泛型类型参数这两项语言特性都可以使类具有正交扩展能力(见6.10节文献[13]) - 通过自类型标注(self-type annotation)对抽象的正交扩展进行约束(见6.10节文献[14]) - Case类的用途是实现值对象〔1〕 〔1〕普通类和case类的主要区别在于,case类的构造函数调用更加简单,可以使用默认的相等性语义,以及支持模式匹配
函数式编程能力 Scala是一种多范式的编程语言,结合了面向对象和函数式编程的语言特性 为何采取面向对象与函数式相结合的方式? - Scala中的函数是第一类值;从类型系统层面开始,就支持高阶函数。用户可将自定义DSL控制结构写成闭包,然后当做一般的数据类型传递 - 在纯粹的面向对象语言中,一切事物都要套入类的设计形式,不管它本来的领域含义应该是名词还是动词。Scala混合了面向对象与函数特性,更有利于模型贴近问题域的语义
静态检查的鸭子类型 Scala通过结构化类型定义(structural types,见6.10节文献[2])支持鸭子类型 与Ruby鸭子类型的差别: Scala的鸭子类型是静态检查的
限定了词法作用域的开放类 Scala通过它的implicit语言结构取得开放类的效果,3.2节的补充内容“Scala implicits”中有相关介绍 与Ruby猴子补丁的差别: Scala的implicits结构有词法作用域的限制;通过隐式转换方式添加的行为,需要明确导入具体的词法作用域才生效(详见6.10节文献[2])
隐含参数 API调用中可以省略部分参数,让编译器去推断。这样做可以精简语法,提高DSL脚本的可读性
模块化的对象组合方式 有独特的对象概念,可用来定义具体的DSL模块。你可以将一些DSL结构定义为抽象成员,推迟进行具体实现

[1] 普通类和case类的主要区别在于,case类的构造函数调用更加简单,可以使用默认的相等性语义,以及支持模式匹配。

这些特性汇集在一起,使Scala成为一种非常适用于内部DSL设计的JVM语言。不过它毕竟是一种新语言,难免令人犹豫。在团队里引入一种新语言,从技术和文化方面来说,都是很大的挑战。公司可能已经在JVM平台上投入巨大资源,相当数量的客户应用也运行于Java平台,程序员群体也花费了无数精力去熟悉各式各样的Java框架。你能为了迎接一种新的编程语言,而放弃多年的积累吗?幸好,Scala允许你循序渐进地完成转换。请看下一节。