uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

G*ワークショップZ Jun 2013 - GR8Conf報告&ライブコーディング&GroovyServ/Improx

今週末は、G*Workshopです。
http://jggug.doorkeeper.jp/events/3873

ご興味のあるかたは是非どうぞご参加下さい。
今回も、前回に引き続きNTTSOFTの「新」品川オフィスです。
つまりビルが前々回までと異なります。
ご注意くだだい。

Groovy基礎勉強会で発表してきました

@kyon_mmさんたちが企画した、Groovy基礎勉強会で発表してきました。
機会を頂きありがとうございました>kyonさん、スタッフの皆様、場所を提供頂いたオラクルのみなさん。

コンパイラ処理系としてのGroovyの中身」を覗いてみよう、というのが今回のテーマでした。
実際、今回しらべて、Groovyのステートマシンベースのコンパイルプロセスは、かなり判りやすいものだと思ったるのですが、それをストレートに解説してみた積りでしたが、どんなもんでしょう。


とまれ、発表者の皆様、聴講者の皆様、お疲れまでした。
70名近い参加者の方がいらして、緊張しましたが、個人的にはとても楽しめました。
なにしろ喋りたいことをしゃべったからな…。
コンパイラは楽しいよね〜!
懇親会とLTも激しく濃く(途中でぬけちゃいましてすみません)、おつかれさまでした。

以下、@kimukou2628さんによるまとめ。

以下、当日資料です。

Staticalizer - G* Advent Calendar 2012-

f:id:uehaj:20121201155702j:plain

今年もやってまいりました恒例行事「G* Advent Calendar 2012」その4日目ということで、私が作ったプログラム「Staticalizer」を紹介します。発音は「スタティカライザー」で二〜三回舌を噛む感じで発音します。

これは、Groovy2.0で導入された静的Groovy機能をより便利につかうためのプログラムです。もとはJGGUG合宿で作りはじめたもので、品質はまだまだなのですが、この機会を借りまして公開します。

背景

Groovy 2.0で導入された静的Groovyはなかなか画期的なものだと思います。いくつかの利点がありますが、その一つは性能です。静的コンパイルを行うことで、動的型によるオーバーヘッドを回避でき、Javaに匹敵するほどの性能が理論的には期待できそうです。既存のGroovyコードを高速化してみたくなるかもしれません。

(参考: "Groovy 2.0の新機能")

それをやるのは、とっかかりとしては@CompileStaticや@TypeCheckedといったアノテーションを付けるだけなのですが、型情報が不足している場合などには怒られてしまいます。

例えば、

import groovy.transform.*

@CompileStatic
def greet(a) {
  println "Hello "+a.toUpperCase()
}

greet("world")

は以下のようなエラーです。

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
/tmp/work/hello.groovy: 5: [Static type checking] - Cannot find matching method java.lang.Object#toUpperCase(). Please check if the declared type is right and if the method exists.
 @ line 5, column 20.
     println "Hello "+a.toUpperCase()
                      ^

引数aの型が判らないために、「toUpperCase()」というメソッド呼び出しが正当かどうかコンパイル時に決定できないためです。このようなエラーを修正するためには元のコードを

import groovy.transform.*

@CompileStatic
def greet(String a) { // ← aの型を指定
  println "Hello "+a.toUpperCase()
}

greet("world")

のように修正する必要があります。

型推論と解決すべき課題

静的Groovyでは「型推論」の機構が導入されており、場合によっては型を省略したままにしておくこともできますが、このようにメソッドを呼び出しをまたがると推論(「文字列定数"world"を渡しているのだから、aはStringだろう」)はできず、型を省略することができません。推論ができない理由の一つは、メソッドは複数箇所から呼び出される可能性があり、それぞれの呼び出しで異なる型の引数が与えられるかもしれないからです。例えば、greet(3), greet("abc")と別の箇所で呼ばれているかもしれません。もしそうなら、引数の型はすべての呼び出しで与えられる型の「LUB(Lowest Upper Bound」と呼ばれる型になるべきで、これは「共通の親クラスのうち一番下位にある型」です。例えば、3と"abc"のLUBはjava.lang.Objectですが、intとdoubleのLUBはjava.lang.Number型になります。

このようなLUBを調査するのはたいへんな手間になります。たとえば、複数のinterfaceをimplementsしている場合などはDAGの探索になってきます。

また、ある値が、呼び出したメソッドの戻り値で、その値を決定しているのはそのまた呼び出し先で…というような呼び出しが連鎖しているときも、悪夢となります。

大規模なdynamic groovyコードを後から静的にするのは、一般にはとても・非常に大変です*1

とはいえ、メソッド引数や返り値の型だけでも指定しておくことで、型推論が有効に動作する可能性が高くなる、とも言えます。

Staticalizerの登場

そのような状況で、今回作ったStaticalizerが役にたつはずです。staticalizerはGroovyプログラムを実行させて、実際の型(メソッド・コンストラクタ・クロージャの引数の型、メソッド戻り値の型)を収集記録し、その情報をもとにソースを書き換えることを支援してくれます。LUBの計算も自動的にやってくれます。

インストールと設定

staticalizer-0.1-bin.jarをこちらのbinの方からダウンロードし、どこか適当なところ(以降、STATICALIZER_HOMEと呼ぶ)に展開します。

そして、STATICALIZER_HOME/binをPATHに通します。

使い方

staticalizerの使い方には、コマンドラインから実行する方法と、AST変換のアノテーション(WithTypeLogging)を使う方法の2種類があります。まずは簡単なコマンドラインから実行する方法から。まず、

def foo(n) {
  Closure c = {a,b -> a+b}
  return c(n, n)
}
println foo(3)
println foo(3.5)

こんなGroovyコード(hello.groovy)があるとします。このとき、以下のようにstaticalizerコマンドを実行します。staticalizerコマンドは、引数の指定方法はgroovyコマンドと同じであり、groovyコマンドで実行できる任意のスクリプトを対象にして同様に実行できます。

$ staticallizer hello.groovy
6
7.0
Patch file 'staticalizer.patch' generated. Apply the patch with command line:
 % (cd /; patch -b -p0) < staticalizer.patch

この結果、カレントディレクトリにstaticalizer.patchというパッチファイルが生成されます。
この段階ではスクリプトのソースコードなどは変更されていません。パッチファイルの中身を確認して、

% cat staticalizer.patch
--- /work/hello.groovy 2012-01-04 12:01:17.000000000 +0900
+++ /work/hello.groovy.changed 2012-01-04 12:01:17.000000000 +0900
@@ -1,0 +1,1 @@
+// TODO: Change method argument type: foo(java.lang.Number n)
@@ -1,0 +2,1 @@
+// TODO: Change return type: java.lang.Number foo(...)
@@ -2,0 +4,1 @@
+// TODO: Change closure argument type: { java.lang.Number a,java.lang.Number b -> .. }


問題なければ以下のようにパッチコマンドを実行します*2

$ (cd /; patch -b -p0) < staticalizer.patch
patching file /work/hello.groovy

するとhello.groovyは以下のように修正されます。(元ファイルは拡張子が.origに変更されて同じディレクトリに保存されています。)

def foo(n) {
// TODO: Change method argument type: foo(java.lang.Number n)
// TODO: Change return type: java.lang.Number foo(...)
  Closure c = {a,b -> a+b}
// TODO: Change closure argument type: { java.lang.Number a,java.lang.Number b -> .. }
  return c(n, n)
}
println foo(3)
println foo(3.5)

このコメントをつらつらと参考にしながら、メソッドの宣言を修正します。また、本来の目的だったCompileStaticなどのアノテーションも付与していきます。ここは手動なのです。

import groovy.transform.CompileStatic
@CompileStatic
Number foo(Number n) {
  Closure c = {Number a,Number b -> a+b}
  return c(n, n)
}
println foo(3)
println foo(3.5)

完成!*3

アノテーションを使用する方法

staticalizerスクリプトを使用する代りに、静的化したいメソッド(もしくはクラス)に@WithTypeLoggingというアノテーションを付与することもできます。

import org.jggug.kobo.staticalizer.transform.WithTypeLogging
class X {
  @WithTypeLogging
  int foo(i) {
    return 0
  }
}

これをstaticalizerのjar(STATICALIER_HOME/lib/staticalizer-0.1.jar)をクラスパスに通した上で実行します(そのうちgrabで取れるようにしたいですね)。そのあとstaticalizer.patchが生成されるのはstaticalizerコマンドを使った場合と同様です。

なお、staticalizerコマンドは内部的処理には指定したスクリプトをこのアノテーション(AST変換)を適用して実行しているだけです。

アノテーションを使う場合、全メソッド・全クラスではなく、必要なメソッドやクラスに対して選択的に指定することができます。また、IDEで使用する場合など、都合が良い場合があると思います。

免責

突貫で作ったので、まだまだよほど品質が低いです。githubで公開してますのでissueで報告してくだしあ。

最後に

Toby55@新潟 さん、次お願い致します!

*1:最初から静的と決めて開発すればこの限りではない。

*2:Windowsで適用するには、cygwinなどのpatchコマンドもしくはunified diff形式を処理できるパッチ適用可能なツールが必要です。Eclipseでできるっけか?

*3:ちなみに、型情報の指定だけではすまないケースも多々あるので、これで静的化の作業がすべて済むわけではありません。

G*Workshop「Groovy2.0の新機能」を発表してきました

10/26金曜日は、JGGUG G*Workshopでした。
五十嵐さんのJavaOne報告、杉浦さんのVert.xなど盛り沢山で、G*としては大変盛況でした。参加者各位および発表者の皆さまにおかれましては、ありがとうございました。
以下は@kimukou2628さんによるまとめです。ありがとう!

わたしも、以下を話してみました。


発表内容について、少し補足させてください。

  • 非静的Groovyの大規模な既存コードベースがあるとき、これらを@CompileStatic, @TypeCheckedを使って静的Groovyに「移行」するのは確かにたいへんなのですが、新規にゼロから静的Groovyコードを開発する場合、ほとんど問題がありません。最初から意識して静的に書き、適宜都度修正するのは負担になりません。
  • 実施したベンチマークはいわゆるマイクロベンチマークです。実システムでの実行速度の参考にはほとんどなりません。別途ご確認を。
  • emacs+Flymake-groovyでの、静的型チェックのデモする予定だったのですが、時間がなかったのでできませんでした。以下、スクリーンショットを貼っておきます。
f:id:uehaj:20121028124234p:image 動的Groovyのコード。
f:id:uehaj:20121028124233p:image @CompileStatic(もしくは@TypeChecked)
を指定すると静的型エラーの
行が赤くなる(カーソルをエラー行に
のせるか、C-c eでエラー
メッセージ表示)。
f:id:uehaj:20121028124232p:image 修正(toUppperCase→toUpperCase)。
f:id:uehaj:20121028124231p:image 修正(返り値をintからStringに)。

なお、flymakeでは静的型エラーかどうかに関わりなく、コンパイルエラーをエラー表示しているだけなので、上は何の不思議でもありません。でも、なんか不思議。
では良い静的Groovyライフを。

G*Workshop in 福岡(Groovy 2.xの新機能)

もう1週間たっちゃいましたが、先週はG*Workshop初の九州開催ということで、福岡博多にて開催してまいりました。

@orange_cloverさんによるまとめ(ありがとうございます!)はこちら
個人的には

  • ラーメン旨い
    • 替え玉を頼まないと負けになる
  • 九州の人はみな熱い
    • 出席率高い! 懇親会の方が人が増える!!
  • 銘菓ひよこは福岡発祥である
  • 福岡名物はベルギービールである
  • ANAプレミアム特割は通常座席より安い(こともある)
  • イカが動く

へろへろでしたが、太宰府天満宮も行けて良かった。
きしださん、みなさんありがとうございました。
以下は自分の発表資料です。

他のみなさんの発表資料でSlideshareに上っているものはこちら。

御礼までにです。

第18回 G*ワークショップ周知

http://kokucheese.com/event/index/16071/
より
〜〜
今回のG*ワークショップは「G*なGUI祭り」として、G系デスクトップアプリケーション技術をフィーチャーしたセッションをお届けします。Grailsと並びG系を代表するフレームワークであるGriffonと、JavaFX2.0をよりシンプルに、よりGroovyに扱うことのできる新進気鋭のGroovyFXの豪華二本立てです。要チェックや!!

プログラム:

▶「Griffon不定期便 〜G*ワークショップ編〜」

by 奥清隆 (kiy0taka)
G*Magazineでも絶賛(?)連載中のGriffon不定期便がG*ワークショップにやってくる! G*なデスクトップアプリケーションフレームワークGriffonについて紹介します。

▶「プログラミングGroovyFX 〜ポストJavaFX ScriptはGroovyで決まり!〜」

by 杉浦孝博 (touchez_du_bois) & 関谷和愛 (kazuchika)
まもなく正式リリース予定のJavaFX 2.0をGroovyに使いこなす切り札、 GroovyFXの基本から応用までを多彩なサンプルを交えて詳しく解説します。 中核となるSceneGraphBuilder、TimelineBuilderから、SwingやHTML5、 FXML等との連携例までをカバーし、GroovyFXの全貌を明らかにします。



とのことです。