Spiga

jQuery is DSL (Part 1 - DSL)

2009-08-10 23:47 by Cat Chen, 4408 visits, 网摘, 收藏, 编辑

jQuery刚刚出来的时候,我没有太多关注它,觉得这不过是Yet Another JavaScript Library。早期的jQuery专注于DOM节点的筛选与操作,不提供众多的基础类扩展,更不提供UI组件,因此体积能够做到很小。然而,我实在看不出它和我熟悉的Prototype比有什么明显的优势——jQuery能做的各项独立的操作,Prototype都能做。

后来用jQuery的人越来越多,并且大家都爱用它的链式方法调用,甚至还把这种写法推广到其它语言中去。例如ASP.NET MVP Omar AL Zabir就把他的服务器端C#组件设计为支持链式方法调用的。这时候我才开始关注jQuery,并且逐渐喜欢上了链式方法调用的写法,也在我自己的JavaScript组件中实现类似的API(参考AsyncOverload)。最后,我突然明白到,这其实就是一种Internal DSL嘛!

在这篇文章里,我准备先讨论Internal DSL,在下一篇文章里面再解释为什么jQuery是Internal DSL。现在我们就从最根本的问题开始吧——

什么是Internal DSL?

DSL是指Domain Specific Language,也就是用于描述和解决特定领域问题的语言。例如说,我们有专门描述字符串特征的正则表达式,有专门描述数据库查询的SQL,有专门描述XML结构的DTD和XSD,甚至有专门描述XML变换的XSLT,这些都是DSL。

当然,并非我们关注的领域都有现成的DSL,这时候我们有三个选择:

  1. 使用通用语言描述该领域的问题(non-DSL)
  2. 发明一门全新的语言描述该领域的问题(External DSL
  3. 在一门现成语言内实现针对领域问题的描述(Internal DSL
例如说,我们现在要描述一个很简单的金融领域问题,“我在花旗银行存款$200”这样一句话对应的三种法写法可能是:(假设已经存在I和CitiBank两个实体实例)
  1. I.DepositTo(new USD(200), CitiBank); /* C# */
  2. I deposit 200USD to CitiBank /* E-DSL */
  3. I.deposit(200.USD()).to(CitiBank); /* I-DSL */
第1种做法的成本最低,你只需要有OO的思想就可以了,你总能把实体类设计出来,但可能和人类描述此领域问题的思维方式有一定偏差(为什么USD可以new?为什么不是deposit [something] to [somewhere]?)。

第2种做法的成本最高,你需要写一个全新的解释器,至少是写一组全新的规则,然后让YACC这类工具帮你生成一个解释器,但这样出来的语法最贴近人类思维方式,甚至就如同自然语言一样流畅。

第3种做法术语上述两者的折中方案,如果语法不太复杂可以使用Builder模式实现语法分析,写出来的语法相当贴近自然语言,但还是有学习门槛。由于脚本语言有相当的灵活性,所以现在很多人倾向于选择在脚本语言内实现Internal DSL。

如何构造Internal DSL?

常见的两种Internal DSL实现方法是Method ChainingFunction Sequence。如果我们需要描述一台机器的硬件组成,两种实现方式的代码分别如下:

/* Method Chaining */
computer()
  .processor()
    .cores(2)
    .i386()
  .disk()
    .size(150)
  .disk()
    .size(75)
    .speed(7200)
    .sata()
  .end();

/* Function Sequence */
computer();
  processor();
    cores(2);
    processorType(i386);
  disk();
    diskSize(150);
  disk();
    diskSize(75);
    diskSpeed(7200);
    diskInterface(SATA);

无论是哪一种写法,中间都必须写一个分析器层。就如同语法分析器需要使用状态机一样,Internal DSL的实现也必须内置一个状态机,以记录当前执行到什么状态了,并且接下来可以转移到哪些有效状态。

由于这不是一篇专门讲语法分析器和状态机实现的文章,所以我们把关注点保持在API层面就可以了,不深入讨论其实现细节和成本。我们知道链式方法调用能够实现Internal DSL就够了,至于jQuery是如何利用好这一点的,我们在下一篇文章里再作讨论。

小结

在这篇文章里,我们了解了Internal DSL与External DSL之间的区别,同时还了解到实现Internal DSL的具体方式,这为我们接下来讨论jQuery的Internal DSL式接口做好了铺垫。在下一篇文章里,我们将深入地来看看为什么jQuery的接口要如此设计,它能为用户带来了怎样的便利,同时它自身的实现上又有什么优势。

如果你不希望错过下一篇文章,你可以考虑订阅我的博客:

10
0
(请您对文章做出评价)
« 上一篇:让 JavaScript 轻松支持函数重载 (Part 2 - 实现)
» 下一篇:jQuery is DSL (Part 2 - jQuery)
Add your comment

29 条回复

  1. #1楼 拼命占便宜      2009-08-11 00:01
    这个要顶! 好文!
      回复  引用  查看    
  2. #2楼 ziqiu.zhang      2009-08-11 08:13
    Cat的好文~来支持了。
    能够抽象成理论的才是大师。
      回复  引用  查看    
  3. #3楼 Galactica      2009-08-11 08:48
    jQuery绝对不是DSL!
      回复  引用  查看    
  4. #4楼 Q.Lee.lulu      2009-08-11 09:10
    不觉得是DSL。。。
      回复  引用  查看    
  5. #5楼 四有青年      2009-08-11 09:20
    @拼命占便宜
    好吧
      回复  引用  查看    
  6. #6楼 Jeffrey Zhao      2009-08-11 09:27
    引用Galactica:jQuery绝对不是DSL!

    基本上没有一个什么编程语言“是”DSL的,只有一个东西有没有当作DSL来用。
    C#,Erlang,Scala都可以在某个领域或者根据某个设计当作DSL来用(网上有许多文章)。
    说当DSL来用,其实不就是在各自语法范畴内,设计合适的API,获得较好的语义嘛。
      回复  引用  查看    
  7. #7楼 Nick Wang (懒人王)      2009-08-11 09:53
    这个要关注。

    觉得JQuery的基于集合的操作和LINQ有点像。
      回复  引用  查看    
  8. #8楼 Anders Cui      2009-08-11 10:36
    文中提到的两种方式,阅读起来感觉仍不是太好,与自然语言距离比较远,看来F#在这方面还是有优势的。
      回复  引用  查看    
  9. #9楼 Galactica      2009-08-11 10:43
    引用Jeffrey Zhao:
    引用Galactica:jQuery绝对不是DSL!

    基本上没有一个什么编程语言“是”DSL的,只有一个东西有没有当作DSL来用。
    C#,Erlang,Scala都可以在某个领域或者根据某个设计当作DSL来用(网上有许多文章)。
    说当DSL来用,其实不就是在各自语法范畴内,设计合适的API,获得较好的语义嘛。


    Internal DSL的理念,但是不包括以API的方式实现.

    个人理解,DSL应该是和Software Factory一起来使用,应该有专门的提供商提供 Domain Specific Software Factory ,然后由精通领域业务的需求分析人员使用文字或图形的DSL来组装软件.

    DSSF应该包括通讯模块,以适应部署环境需求;
    DSSF应该包括UI模块,以适应客户对UI的需求;
    ...;

    相对的开发流程就变为需求分析人员的用例规约.

    DSL应该用于持续化集成,应该从特定业务领域的语义自上而下的分析,而非把精力放在使用何种现有语言来"伪装"成DSL.因为在实际的项目中,你会发现这种"伪装"的DSL并没有带给客户更多的效益,反而增加了开发人员的学习成本.
      回复  引用  查看    
  10. #10楼[楼主] Cat Chen      2009-08-11 10:57
    @Jeffrey Zhao
    没错,jQuery正是将JavaScript用做DSL的最好例子。

    这个例子很适合用来举例,因为JavaScript是大多数Web开发人员都懂的语言,尽管他们用的服务器端语言可能不同。同时,描述网页行为也是一个大家都能理解的领域,这比拿一个企业应用的金融领域DSL来举例要让人容易接受。
      回复  引用  查看    
  11. #11楼 Jeffrey Zhao      2009-08-11 11:11
    @Galactica
    你说得这个难道不是External SDL及其周边产品吗?
    Internal DSL的优势就是成本低,可以与现有项目直接集成(因为毕竟是现成的语言)。
    而且如果设计的好,学习成本也不会高,因为它也是更好的描述业务的方式。
      回复  引用  查看    
  12. #12楼 lola      2009-08-11 11:17
    楼主思维站的如此之高,佩服
      回复  引用  查看    
  13. #13楼[楼主] Cat Chen      2009-08-11 11:25
    @Jeffrey Zhao
    没错,Internal DSL是用于低成本取代non-DSL的,而不是用来和External DSL竞争的。就好比说,jQuery能够后来追上,获得比Prototype更多的粉丝,这就是很好的一个例子。在功能点上,jQuery和Prototype重叠范围很大,但jQuery成功利用了Internal DSL的特性,得到了更好的效果。
      回复  引用  查看    
  14. #14楼 zzfff[未注册用户]2009-08-11 11:48
    language最重要的特性是抽象,只有External DSL才“有”资格称作language(tokenize->syntax parse->AST->生成目标代码)
    Internal DSL最好别打language的擦边球,很误导人:)
      回复  引用    
  15. #15楼 Galactica      2009-08-11 11:48
    引用Jeffrey Zhao:
    @Galactica
    你说得这个难道不是External SDL及其周边产品吗?
    Internal DSL的优势就是成本低,可以与现有项目直接集成(因为毕竟是现成的语言)。
    而且如果设计的好,学习成本也不会高,因为它也是更好的描述业务的方式。


    你是站在实现的角度来考虑这个问题,而我是站在需求的角度来考虑这个问题.也就是说,你认为DSL是开发人员使用的,而我认为DSL应该是需求分析人员,甚至是领域专家使用的.
      回复  引用  查看    
  16. #16楼 王德水      2009-08-11 12:04
    好文章,持续关注
      回复  引用  查看    
  17. #17楼 Jeffrey Zhao      2009-08-11 12:17
    引用Galactica:
    你是站在实现的角度来考虑这个问题,而我是站在需求的角度来考虑这个问题.也就是说,你认为DSL是开发人员使用的,而我认为DSL应该是需求分析人员,甚至是领域专家使用的.

    是啊,这点我知道。Internal DSL我认为相当程度就是让开发人员来描述业务用的。
    或者说,是一种更方面得实现业务的方法。
      回复  引用  查看    
  18. #18楼[楼主] Cat Chen      2009-08-11 12:41
    引用Galactica:
    引用Jeffrey Zhao:
    @Galactica
    你说得这个难道不是External SDL及其周边产品吗?
    Internal DSL的优势就是成本低,可以与现有项目直接集成(因为毕竟是现成的语言)。
    而且如果设计的好,学习成本也不会高,因为它也是更好的描述业务的方式。


    你是站在实现的角度来考虑这个问题,而我是站在需求的角度来考虑这个问题.也就是说,你认为DSL是开发人员使用的,而我认为DSL应该是需求分析人员,甚至是领域专家使用的.


    这要区分应用场景。

    例如你要为一个企业进行IT化,他们的领域专家都不懂IT,那么DSL按照你所说的角度来设计就能获得较大的效益。至于jQuery,面向的用户是Web Developer,而不是完全不懂programming language的Interaction Designer,所以jQuery这样设计也是正确的。
      回复  引用  查看    
  19. #19楼 Nick Wang (懒人王)      2009-08-11 17:23
    Internal DSL和External DSL只是实现方式不同,如果Internal DSL的效果已经可以让用户使用好,就可以了。之所以需要External DSL就是因为Internal DSL的表达能力受到语言本身的语法限制。
      回复  引用  查看    
  20. #20楼 钢盅郭子      2009-08-12 09:32
    这个……不就是C# 3.0的扩展方法嘛
      回复  引用  查看    
  21. #21楼[楼主] Cat Chen      2009-08-13 15:32
    @钢盅郭子
    Extension Method是站在实现的角度来看,而DSL则是站在使用的角度来看。
      回复  引用  查看    
  22. #22楼[楼主] Cat Chen      2009-08-13 15:32
    @钢盅郭子
    况且,实现DSL并不一定要通过Extension Method,也不一定是用C#。
      回复  引用  查看    
  23. #23楼 暗香浮动      2009-08-13 18:00
    最近在关注DSL.同事也在关注jquery 持续关注中
      回复  引用  查看    
  24. #24楼 钢盅郭子      2009-08-13 21:11
    @Cat Chen

    DSL的概念当然要比C#扩展方法要大上许多
    但是否也可以将C#的扩展方法看成是某一种DSL呢
      回复  引用  查看    
  25. #25楼[楼主] Cat Chen      2009-08-13 22:08
    @钢盅郭子
    它是实现DSL的一种途径,但是不见得一定会被人用于实现DSL。
      回复  引用  查看    
  26. #26楼 怪怪      2009-08-14 09:41
    此文和讨论都不错。

    不过在缺乏多分派的面向对象语言中,随着处理的子问题的数目的增大,这种就会又不安全(从正确性角度来讲)、又不好实现、又不好使。

    也许动态语言在“好使”方面会好一些,在“安全”方面也能控制一下,不过为了这两点,实现又必然变得复杂,效率问题是另一个方面了。

    jQ这个规模也许刚刚好?

    P.S. 想了解下这种方式在背后是如何使用自动机的(具体的,比如jQ),有空介绍一下? :P
      回复  引用  查看    
  27. #27楼[楼主] Cat Chen      2009-08-14 10:47
    @怪怪
    我下一篇文章要说的正是,jQuery极其无耻地选用了一个不需要自动机的领域,状态永远只有一个。
      回复  引用  查看    
  28. #28楼 doyi[未注册用户]2009-09-03 16:53
    jQuery 2更明显。。。

    缺点就是如果遇到jQuery本身的bug就痛苦了~~
      回复  引用    
  29. #29楼[楼主] Cat Chen      2009-09-03 17:03
    @doyi
    因此jQuery自身的Unit Test显得非常重要。
      回复  引用  查看