什么是 DSL(领域特定语言)呢?
先说一下他好处吧,就是让更多人更容易地接触到编程,让编程语言更接近人类语言。许多语言在设计之初都在考虑是对人类更友好一些,还是对计算机更用好一些呢。DSL 无疑对人类更友好。可以让更多人加入的 developer 大军中来。
大家熟悉的 css 和 SQL 语言都是典型的 DSL 语言,我们在写 SQL 语言可能感触很深,感觉自己在写英文语句而非编程语言。
大家可能注意到了领域,所谓领域也就是限定语言是适用于一定范围的。CSS 专注定义我们页面结构和样式,包括颜色和尺寸等等,而 SQL 专注于对数据的操作。
DSL 包括两种类型,External 和 Internal
External 类型,你可以定义规则、定义语法、比较灵活,同时也带来大量的工作,需要自己写解析器。
Internal,语言本身提供 DSL 特性,无需自己写 parser 造轮子。但是也需要对语言了解熟悉,然后通过现有资源和条件来实现我们 DSL
context 和 fluency
context 可以理解为共同语言,那么什么是夫妻间的共同语言呢,简单我们可以谈论都看过电影,我们不需要给出过于详细信息对方也能读懂我们想法,理解彼此意图。
fluency 所谓流畅,就是 DSL 需要更接近人类语法,通过一系列 word 清楚表达一定意思。kotlin 虽然是一门静态语言,但是从他语法和特性来看可以实现一定 fluent。
kotlin 语言本身特性
代码结尾分号可省略
还得说 java 我们必须写 ; 结尾。
println("hello")
那么回到主题,分号结尾对我们 DSL 有什么影响呢,显而易见影响我们语言的 fluent 流畅性。
我们可以省略 . 代替用空格。
class Car{
fun drive(dist: Int){
println("driving...")
}
}
fun main() {
val car = Car()
car.drive(10)
}
我们这里调用 car 的 drive 方法是通过(.) car.drive(10)
,我们想写出car drive 10
这样看起来更接近人类语言。
infix fun drive(dist: Int){
println("driving...")
}
inflix 告诉编译器多这段代码检查可以放宽松些。
方法的扩展
为了提高 DSL 语言的可读性,我们在定义方法名要下一些功夫,但是有些第三方库提供方法名并不能满足 DSL 对 fluent 要求。
fun main() {
val car = Car()
var greeting = "hello"
fun String.shout() = toUpperCase()
println(greeting.shout())
// car drive 10
}
toUpperCase
这个方法名显然对 DSL 语言不那么友好,我们需要给他换一个名词来在 DSL 语言中使用。kotlin 又是如何实现的呢?kotlin 并不像其他语言对字节码进行注入,而是通过语法糖包裹来实现,编译器,将 shout 函数接受 String 类型作为参数然后进行重写
fun process(func:(n:Int) -> Unit, n:Int){
func(n * 2)
}
fun List<Tut>.findCoursesGreaterThan(course:Int){
filter {
it.courses > course
}.forEach(::println)
}
tuts.findCoursesGreaterThan(60)
Tut(title=rust base, courses=120, author=tina, category=native)
infix fun List<Tut>.findCoursesGreaterThan(course:Int){
filter {
it.courses > course
}.forEach(::println)
}
tuts findCoursesGreaterThan 60