JavaOne2010 9/23 -- Writing Domain-Specific Languages (DSLs), Using Groovy
Groovyコミッタ、我らがPaul King*1のセッション。全面的にGroovyでお送りしております。
JavaOne最後のセッションなので、リアルタイムついったー実況、いわゆるtsudaりに挑戦してみました。
というわけで、ツイートを補足する感じでまとめてみます。
from Twitter
07:23 [DSL/Groovy]最終のコマは、Writing Domain-Specific Languages (DSLs), Using Groovy。Paulのセッション。 #javaonejp
07:25 [DSL/Groovy]Paulによると、@uehajの日本語DSLをサンプルとして混ぜ込んだらしいです。ホントかな。 #javaonejp
07:41 [DSL/Groovy]スライドの情報量が多すぎて要約できない #javaonejp
資料、SlideShareに公開されてました!
http://www.slideshare.net/paulk_asert/groovydsls
ページ数が152ページとか大杉!
真っ向から立ち向かって逐一まとめたレポートなんぞ書けません。
from Twitter
07:45 [DSL/Groovy]DSLとは?:DSLの原点、Twitterでの発言の引用、内部/外部DSLの特徴。DSLを作るときのパターンの種類名の紹介(内容はなし)、など。 #javaonejp
DSLとはなんぞや、という話が18ページぐらいまでつづきますが、情報量が多すぎてついて行けないので資料を参照してください。
from Twitter
07:45 [DSL/Groovy]次はGroovyの紹介。こんなに本が出てるんだぜ、とか。基本文法とか、Javaと比べてこんなにシンプルに書けるんだぜ、とか。 #javaonejp
というわけで、ここからずっとGroovyのターン!!!!
from Twitter
07:47 [DSL/Groovy]Grapes/Grabの紹介。アノテーションかくだけでMavenリポジトリからJarをDLしてクラスパスへの追加が自動でできますよ。 #javaonejpし
// Google Collections example @Grab('com.google.collections:google-collections:1.0') import com.google.common.collect.HashBiMap HashBiMap fruit = [grape:'purple', lemon:'yellow', lime:'green'] assert fruit.lemon == 'yellow' assert fruit.inverse().yellow == 'lemon'
個人的にはGroovyのキラー機能。
ポータブルなスクリプトを書くのに最高の機能です。すばらしい。
from Twitter
07:48 [DSL/Groovy]Groovyを使ったDSLの例の紹介。Spockとか。 2.hours とか。 #javaonejp
07:49 [DSL/Groovy]DSLのためのGroovyの機能の紹介。import文でasを使うとクラスに任意の別名が付けられる。static importのメソッド名にも別名が付けられる。Calendar.getInstance as now とか。 #javaonejp
@Grab('com.google.collections:google-collections:1.0') import com.google.common.collect.HashBiMap as HashMap def m = new HashMap() m.key = 'value' assert m.inverse().value == 'key'
とか
import static java.util.Calendar.getInstance as now println now().format('yyyy/MMM/dd')
とか。
from Twitter
07:53 [DSL/Groovy]文法自体もコンパクトだからBoilerplateコードもほとんどなくて内部DSLが書きやすい #javaonejp
07:54 [DSL/Groovy]with句を使うと、メソッドのレシーバを省略しr書ける。list.with { add "a"; remove "b" } #javaonejp
map = [a:10, b:4, c:7] map.with { assert (a + b) / c == 2 }
from Twitter
07:57 [DSL/Groovy]クロージャの説明。(実際Groovyのクロージャは使いやすいと思う。記述はシンプルだし、メソッドも簡単にクロージャとして取り出せるし、引数のカリー化も簡単だし、暗黙変数itもいい感じ) #javaonejp
クロージャの呼び出しは、普通のメソッド呼び出しっぽく書ける。
int myConst = 4 def multiplier = { number -> number * myConst } assert multiplier(10) == 40
実は上の呼び出し記法はシンタックシュガーで正式にはcallメソッドを呼ぶ。
Clojure other = { it + myConst } assert other.call(10) == 14
↑引数宣言を省略すると、暗黙変数itとして、1つだけ引数を受け取れる。結構便利。
from Twitter
08:01 [DSL/Groovy]演算子オーバロード。Groovyではほとんどの演算子は、対応するメソッド呼び出しのシンタックスシュガーに過ぎない。メソッドを独自に実装すれば演算子の振る舞いが変更できる。 #javaonejp
たとえば、 a + b の振る舞いは、aのクラスのplusメソッドをオーバライドすると書き換えられる。
http://groovy.codehaus.org/Operator+Overloading
Operator Method a + b a.plus(b) a - b a.minus(b) a * b a.multiply(b) a ** b a.power(b) a / b a.div(b) a % b a.mod(b) a | b a.or(b) a & b a.and(b) a ^ b a.xor(b) a++ or ++a a.next() a-- or --a a.previous() a[b] a.getAt(b) a[b] = c a.putAt(b, c) a << b a.leftShift(b) a >> b a.rightShift(b) switch(a) { case(b) : } b.isCase(a) ~a a.bitwiseNegate() -a a.negative() +a a.positive()
Operator Method a == b a.equals(b) or a.compareTo(b) == 0 ** a != b ! a.equals(b) a <=> b a.compareTo(b) a > b a.compareTo(b) > 0 a >= b a.compareTo(b) >= 0 a < b a.compareTo(b) < 0 a <= b a.compareTo(b) <= 0
from Twitter
08:03 [DSL/Groovy]Groovyのswitch文は強力。intに限らずどんなオブジェクトでもマッチングできる。もちろんString以外でもOK. #javaonejp
Java7or8で、Stringによるswitchに対応しますが、Groovyならあらゆるオブジェクトを条件式に使えます。
from Twitter
08:05 [DSL/Groovy]Builder: Groovyのビルダはツリー構造を持つ情報を定義して、それで何かを出力/実行するのにとても便利。HTML/XMLとか。SwingBuilder、AntBuilderも便利。 #javaonejp
たとえば、MarkupBuilderを使うと、
import groovy.xml.* def page = new MarkupBuilder() page.html { head { title 'Hello' } body { ul { for (count in 1..5) { li "world $count" } } } }
と書くだけで、↓のHTML構造が生成できます。
<html> <head> <title>Hello</title> </head> <body> <ul> <li>world 1</li> <li>world 2</li> <li>world 3</li> <li>world 4</li> <li>world 5</li> </ul> </body> </html>
レポート生成スクリプトとかでも便利。
from Twitter
08:09 [DSL/Groovy]メタプログラミング:ExpandoMetaClass。クラス.metaClass.hoge = { クロージャ} で既存クラスにメソッドが追加できる。JDKのクラスでもOK. #javaonejp
クラスのmetaClassにクロージャを追加すると、クラスにメソッドを追加したことになります。
List.metaClass.sizeDoubled = {-> delegate.size() * 2 } LinkedList list = [] list << 1 list << 2 assert 4 == list.sizeDoubled()
お手軽で便利です。
from Twitter
08:10 [DSL/Groovy]ReentrantLockのmetaClassにtry-finallyでロック操作するようなwithLockを追加すると、lock.withLock { クロージャ } とか書ける。 #javaonejp
import java.util.concurrent.locks.ReentrantLock import static System.currentTimeMillis as now def startTime = now() ReentrantLock.metaClass.withLock = { critical -> lock() try { critical() } finally { unlock() } } def lock = new ReentrantLock() def worker = { threadNum -> 4.times { count -> lock.withLock { print " " * threadNum print "." * (count + 1) println " ${now() - startTime}" } Thread.sleep 100 } } 5.times { Thread.start worker.curry(it) } println "ROCK!"
from Twitter
08:12 [DSL/Groovy]Neo4Jというライブラリの自作DSLを披露。知らなかったけど組み込み型グラフデータベース、らしい。http://bit.ly/dCBwhI #javaonejp
08:14 [DSL/Groovy]ASTTransformation; Almirayのセッションでも紹介してたけど、コンパイル時に介入してコード要素の意味を変えたり、コード構造そのものを追加/変更/削除できる仕組み。コンパイル時にいじるので実行時ペナルティはない。 #javaonejp
08:15 [DSL/Groovy]まじめにASTの構造変更を書くと大変だけど、ASTBuilderを使うと直感的にとても簡単に書ける。 #javaonejp
08:18 [DSL/Groovy]Type Transformation:asを使うと型を変換できる。asType(Class)というメソッド呼び出しに相当してて、型変換は其処に実装されている。それぞれのクラスが元々対応している範囲で変換できる。 #javaonejp
def result = new AstBuilder().buildFromCode { println "Hello World" }
↑こういうコードは、↓というAST構造になります。
BlockStatement -> ReturnStatement -> MethodCallExpression -> VariableExpression("this") -> ConstantExpression("println") -> ArgumentListExpression -> ConstantExpression("Hello World")
from Twitter
08:18 [DSL/Groovy]Gparsec。聞き逃した。 #javaonejp
HaskelのParsecのGroovy版ですね。パーサコンビネータライブラリです。
BNF的に文法を定義して、それに基づいて入力をパースする、という。たぶん。
興味があれば↓の辺をどうぞ。
http://xircles.codehaus.org/projects/gparsec
と思ったら、全然情報ないですね...。
from Twitter
08:20 [DSL/Groovy]コインのDSLサンプル(PJ Coinとは無関係)。通貨のDSLをどうやって書くか。少しずつ進化させる。 #javaonejp
08:21 [DSL/Groovy]記法の進化。使う技術によって読みやすさが変わる。"2 * quarter".value → "2 * quarter" → "2.quarter" #javaonejp
08:22 [DSL/Groovy]GEP-3(DSL用Syntax拡張の仕様):"a(1).b(2).c(3)" → "a 1 b 2 c 3" と書けるようになる。 #javaonejp
GEP-3(※仕様ID, JSRみたいなモノ)とは、Groovy1.8*2から導入予定のDSL用のシンタックス拡張です。
GEP-3の導入により、ある条件に従ってメソッド呼び出しの括弧が省略できるようになります。
その応用例として...↓
from Twitter
08:24 [DSL/Groovy]でた、@uehajの日本語DSLの例。うけてる。 #javaonejp
08:26 [DSL/Groovy]ちなみに紹介されたのは、http://bit.ly/bGYt8S の平方根のやつ。 #javaonejp
09:13 [DSL/Groovy]@uehajのDSLがJavaOneで晒された瞬間 http://twitpic.com/2rbc4u #javaonejp
というわけで、JavaOneで発表された@uehaj製の日本語DSLは↓のようなもの。
// ---- 仕込み Object.metaClass.を = Object.metaClass.の = { clos -> clos.call(delegate) } まず = { it } 表示する = { println it } 平方根 = { Math.sqrt(it) } // ---- 実行 まず 100 の 平方根 を 表示する // ==> 10.0
http://d.hatena.ne.jp/uehaj/20100919/1284906117
これ、ほんとにこのままGroovyコードとして実行できます。
興味のある方は1.8-beta-2をインストールしてお試しください。
なにかムズムズする感じが味わえると思います(謎
from Twitter
08:30 [DSL/Groovy]ある日顧客から唐突にPaul宛に「DSLをつくってくれ」というmailがきた、という設定のサンプル紹介。 #javaonejp
08:32 [DSL/Groovy]DSLを活用したフレームワーク/ライブラリとして、Gpars、EasyB、Spock、Cucumberなどを紹介。 #javaonejp
08:33 [DSL/Groovy]本の紹介をして、おわり。Groovy in Action 2ndとか。 #javaonejp
というわけで、Javaなアプリで一部にDSLを導入したくなったら、Polyglot Programming的にGroovyを併用して、DSLをつくることも検討してみると良いと思います。割と現実的な話として。
以上、JavaOne2010のレポートは終了です!