许可优化
许可优化
产品
产品
解决方案
解决方案
服务支持
服务支持
关于
关于
软件库
当前位置:服务支持 >  软件文章 >  Java Fluent API:现代DSL设计模式

Java Fluent API:现代DSL设计模式

阅读数 4
点赞 0
article_banner

论文导读系列(I)

Formal Language Recognition with the Java Type Checker[2016]

为什么Fluent API?

我们为什么需要DSL(领域特化语言)?

从个人观点来看 是为了更精简地表达

人们喜欢函数式语言的原因也是一样的

在函数式编程里杜绝所有的状态变化

是为了从交互式编程(Imperative programming)转向指令式编程(Declarative programming)

从而实现更精简的表达

可是世界上无数的系统 无数的领域

难道每个都实现自己的一套语言吗?

我觉得这是难以接受的

虽然现在想实现一个语言已经很方便了

只要提供一个BNF就会有工具帮我们实现自动机

可是不可否认这个过程门槛仍然是很高的

Fluent API是什么?

以防有人不知道我在这里简单描述一下

它就是能够串起来调用的一些函数,比如a().b().c()...

但是这里需要明确一点,Java的StringBuilder不属于Fluent API

因为它返回的永远是同一个对象,意味着所有的调用都发生在这个对象上

而Fluent API每个函数的返回类型都是任意的

StringBuilder这种调用方式被称为method chaining

Fluent API != Method Chaining

Fluent API将变量声明给省略了

这就是它的威力所在

极大地提高了语言的表达能力(精简性)

函数式语言不就是想要这个效果么?

也许聪明的你已经意识到

Fluent API是不是可以代替DSL呢?

事实上Fluent API就是DSL

但它是被嵌到宿主语言(实现语言)上的DSL

所以被称为EDSL(Embed DSL)

Fluent API就是DSL

似乎人们最近几年才理解到这一点

因为它拥有跟DSL同等的表达力

还能够用宿主语言的特性

它的性质决定了Fluent API今后一定会大火

为什么要类型检查?

原因是:

从经验上判断,对Fluent API进行类型检查是可行的

既然类型检查可行,我们就希望在编译时检查出问题减少运行时出现错误的几率

怎么写一个支持类型检查的Fluent API?

这里引入一个Box系统作为例子:

盒子支持两种操作,close & open

这也是最简单的一个DSL

现在我们来给它编码以支持类型检查

interface Box{

open(): Box;

close(): Box;

}

class TheBox implements Box{

open(){

return this;

}

close(){

return this;

}

}

new TheBox().close().open().close()

注意这里的open和close返回的是一个Box,以支持Fluent API

这样算是实现了类型检查吗?

当然 至少保证了Box类型不能调用explode函数

然后问题就暴露出来了:

众所周知,一个打开的盒子不能再打开,一个关闭的盒子不能再关闭

也就是说: box.open().open() or box.close().open() 都是不合法的

怎么写一个语义正确的支持类型检查的Fluent API?

Box类型其实应该细分为OpenBox和CloseBox

上个自己画的图, 这是一个自动机(伪), 由图可知,函数close, open,实际上是partial function:

0644de90f35cad074ca0f70e9a4eaf10.png

这种内部实际上拥有复杂状态的类型叫做stated-type

与此相对的举个例子就是Java的StringBuilder,就不细说了

下面是实现:

class CloseBox implements Box{

open(){

return new OpenBox();

}

close(): never{

throw "No I should not call this method"

}

}

class OpenBox implements Box{

open(): never{

throw "No I should not call this method"

}

close(): CloseBox{

return new CloseBox();

}

}

new OpenBox().open() // I want the type checker to report an error.

当我调用OpenBox的open方法的时候, 最理想的情况是编译器能报一个错误:

但是我发现typescript好像没办法做到这件事情 让我十分苦恼

我在stackoverflow上问了这个问题Can typescript define uncallable method in a class?

论文中用的是java,作者用了一点trick去实现了这件事情但是可读性贼差

我认为语言完全应该实现这个feature

比如我返回never类型那么类型系统就不应该让我调用这个方法

我也在typescript的github项目上提了个issue

这里我直接throw一个error,让我们的脑内编译器抛出一个错误吧

这就是一个语义正确的支持类型检查的Fluent API的实现

自动化Fluent API生成

上下文无关文法是可以描述stated-type的

比如我们给Box系统提供一个BNF, 他会是这个样子:

// epsilon是希腊字母那个, 代表空

Box := open close Box | epsilon

在Box这个语言里 open open 这样一个语句是不合法的

没办法通过编译

换个角度, stated-type实际上是一个语义问题

我们能从BNF手动写出一个 语义正确的提供类型检查的Fluent API

然后我们思考,从BNF中识别出stated-type的规律是什么?

如果发现了这个规律,不单只手写API的效率大幅提高,不用动脑子

我们甚至能够 自动生成Fluent API

然后就是论文的精髓所在了

识别语义?这不就是下推自动机的工作么?

下推自动机有一个栈

这个栈的本质其实就是语义

只有在当前栈(语义)状态下,才能够调用sigma方法

这就是: 通过对下推自动机的模仿(mimic)实现语义类型系统

然后按部就班将自动机翻译成Fluent API就好了

这就是自动化Fluent API的生成逻辑

总结

Fluent API🐂🍺


免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

相关文章
技术文档
QR Code
微信扫一扫,欢迎咨询~
customer

online

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 board-phone 155-2731-8020
close1
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

姓名不为空

姓名不为空
手机不正确

手机不正确

手机不正确
公司不为空

公司不为空

公司不为空