CSUG / csug

China Scala User Group
261 stars 64 forks source link

Scala 与 DSL #10

Open zhongl opened 11 years ago

zhongl commented 11 years ago

最早结识DSL, 还是因为看了Martin Flower的一篇文章叫FluentInterface, 它真正让我明白原来代码也可以这样美的.

TimeInterval meetingTime = fiveOClock.until(sixOClock);

注意, 本文中提到的DSL被狭义的等同于了Internal DSL. 言外之意, 它还有External的部分. 关于DSL更全面的内容, 还请参阅Martin Flower的专著.

以至于后来我爱上写单元测试, 很大程度要归功于mockitoDSL应用上的如火纯青.

尽管jMock要早于mockito, 而我个人却更喜欢后者.

在自己尝试着用Java写DSL的时候, 那永远摆脱不掉的.(), 总让我纠结不已, 当然, 这只是个人洁癖而已.

Scala在实现DSL是很有优势的, 这在combinator.Parsers中已表明了这点, 而且在Programming Scala一书中, 作者专门用一个章节来讨论.

值得强调的是, 阻碍我们设计实现DSL的是我们自己的想象力, 而不应该是一门语言的表现力.

在此引出本文的目的: 大家来聊聊你用Scala写过的哪些DSL?

zhongl commented 11 years ago

我在yascli设计了编写命令行应用的DSL, 如:

import com.github.zhongl.yascli._
import com.github.zhongl.yascli.Converters._

object Example extends Command(name = "example", description = "a example of single command") with Application {
    private val flag0       = flag("-f" :: "--flag" :: Nil, "enable flag")
    private val singleValue = option[String]("--single-value" :: Nil, "set single value", "value")
    private val param       = parameter[String]("param", "set param")

    override def run() {
        if (flag0()) println("enable flag0.")
        println(singleValue())
        println(param())
    }
}

最近想要改进, 但感觉自己的想象力有点枯竭, 故拿出来献丑, 求灵感!

hongjiang commented 11 years ago

我没写过DSL方面的东西,也是从PIS 书中了解到的 parser combinator。 发现scala语言已经内置了,并且提供的接口确实很方便,只要把语义规则想明白了,实现起来非常简洁,背后的算法我完全不用关注(除非要考虑性能)。这点最初很让我震惊,原本以为要很复杂的事情几行代码就可以解决了。

另外也感觉内置的这个parser combinator有些“鸡肋”,主要可能是因为性能问题,比如cobar里用于解析sql的parser,或fastjson里的parser都做了很大优化,稍微要求一点性能的情况下都不太适用。不过这根dsl无关,跑题了。

zhongl commented 11 years ago

@hongjiang 说到Parser, 这里我推荐parboiled, 据说性能要优于内置的Parser. 不过我倾向于使用Internal DSL, 几乎没有使用它的场景.

noroot777 commented 9 years ago

内部DSL的流程控制该怎么实现呢,纠结

henix commented 9 years ago

我最近写的一个 CSS Selector 的 DSL: https://github.com/henix/ssoup

jsoup 本来是外部 DSL ,即它需要解析一个字符串:

element.select("body > div#page > div#nav");

在看了 jsoup 的代码之后,我发现它实现 selector 效率偏低,每个 selector 都要遍历整个 DOM 树。故自己实现一个内部 DSL,一方面写起来容易,另一方面只访问部分 DOM 节点,效率提升。

val nav = select(body > "div#page" > "div#nav").headOption

这里的 > 是运算符。

另外还写过一个中文单元测试的 DSL http://www.douban.com/note/432612442/ ,其实就是把 ScalaTest 的 it should ... in ... 翻译成中文了。。。

hongjiang commented 9 years ago

赞啊