2012年12月9日日曜日

Xtend の Lambda とストリーム処理


この記事は、Java Advent Calendar 2012 の第9日目です。
前の記事は すふぃあ氏(@empressia)の記事 です。
明日の記事は 谷本 心氏(@cero_t) です。

Java に Lambda が来る!

Java 8 に Lambda が入ることはまことに喜ばしい限りですね。でも、使えるのはまだちょっと先のことになりますし、Java 8でホントに入るの?という心配もあります (^_^A;
Java 8 が出ても、すぐには使えない仕事も多いですしね。Java プロジェクトで実際に Lambda を使うのは、しばらく先のことになってしまうかもしれません...。


そこで Xtend です!

Java プロジェクトで Xtend を使えば、今すぐに Lambda を使えます!
Xtend については、以前書いた記事(社内向けのXtend紹介資料) を参考にしてもらうことにして、今日は Xtend の Lambda とストリーム処理のシンプルさについて、ご紹介しようと思います。

Xtend の Lambda の基本

Xtend の Lambda は、Java のそれと同じく、単一メソッドインタフェースに対するシンタクスシュガーです。Xtend の Lambda は、Java と同じイディオムで使用でき、相互運用性もある点で便利です。
ただ、記法は異なります。

従来の Java の以下のような局面では、Lambda を使うことができます。

List<Person> men = ids.map(new Function1<ID, Person>() {
 @Override
 public Person apply(ID id) {
  return Person.get(id);
 }
}).filter(new Function1<Person, Boolean>() {
 @Override
 public Boolean apply(Person p) {
  return p.sex == MALE;
 }
}});

Java 8 の Lambda ならば、以下のように書くでしょう。

List<Person> men = people.map(id -> Person.get(id)).filter(p -> p.sex == MALE)

Xtend では以下のように書けます。

ポイントは、型推論と暗黙引数 it ですね。

val men = ids.map[id| Person::get(id)].filter[p| p.sex == MALE]
val men = ids.map[Person::get(it)].filter[sex == MALE]

Xtend の Lambda は、-> 記号ではなく、[]で囲んだ形になっています。
これは一見妙な感じもしますが、DSL 風の API を Lambda で作るには、結構向いているのです。

Xtend の関数型変数の記述

Xtend で関数型変数を表すシュガーは以下のようになりますが、型推論でより簡単に記述できます。

val (ID)=>Person mapper = [Person::get(it)]
val predicate = [Person p| p.sex == MALE]

Xtend の遅延コレクション評価は Stream 要らず

Java 8 では、遅延コレクション評価のために、Stream が追加されます。
曰く、無限データストリームを処理するのには遅延評価()が必要だが、外部イテレータでは記述が面倒になるので、遅延的な内部イテレータを提供する新しい仲介型が必要だ、ということです。Streamを返すように、既存のコレクション実装を修正すれば、皆が幸せになれると。

Xtend も、遅延コレクション評価の簡潔な記述を提供します。
しかし、Xtend が Java 8 と異なるのは、Stream のような新しい仲介型は導入せず、Iterable/Iterator をそのまま使える点です。
つまり、既存のコレクション(Iterable)も、改造無しにストリーム処理できるのです。

例えば、Xtend で以下のように記述すると、バルク処理ではなく、ストリーム処理になります。このことを少し詳細に見てみましょう。

ids.map[Person::get(it)].filter[sex == MALE].forEach[println(it)]

上記の式のチェーンのつなぎ目の型遷移は、以下のようになっています。

val Iterable<ID> ids = ...
val Iterable<Person> people = ids.map[Person::get(it)]
val Iterable<Person> men = people.filter[sex == MALE]
men.forEach[println(it)]

ここで生じる疑問は2点です。

  1. Iterable に map や filter というメソッドは無いはずでは?
  2. Iterable から Iterable への変換時にバルク処理になっているのでは?


1の答え。それは、Extension method です。

Xtend には Extension method があるので、Iterable に対して外からメソッドを追加したように記述できます。
具体的には、IterableExtensions によって、これらのメソッドが提供されています。


2の答え。それは、IterableからIterableを作るのに評価は不要ということです。

前段の Iterable を、後段の Iterable でラップして行くのです。
最終的な評価は、forEach の中で Iterator 経由で遅延的に行われることになります。これによって、ストリーム処理による遅延評価()が可能になっているわけですね。
ちなみに、この処理の実装として、Xtend の裏で Guava が使われています。

まとめ

Xtend を使うと、今日からすぐに Java プロジェクトに Lambda とストリーム処理を利用できます。

Xtend は、型推論や Extension method の導入によって、生の Java よりもプログラムを大幅に簡潔にしてくれます。
にもかかわらず、Xtend は Java の型システム、Java の思考方法を維持しているので、Java プログラマに大きな学習負担を強いることなく、実装効率を向上してくれます。
Java プログラマなら、1日勉強すると、Xtend をだいたい使いこなすことが出来ます。
Xtend は、Java ソースを生成するので、納品 Java プロジェクトにも使いやすいという特徴があります。

これらの特徴を持つ Xtend は、Java プログラマのための Better Java 言語として、とても魅力的ではないでしょうか。

ちなみに、私のチームでは、今では日常的に Xtend で Java プロジェクトを開発/納品しています。皆様も、ぜひ Xtend の Lambda お試しください。

2012年6月7日木曜日

XtendによるCSV集計サンプルコード

Xtendで、CSVファイルの内容をカラム毎に集計する、サンプルプログラムを書きました。会社の朝Xtend勉強会のお題で、その回答例として書いてみたものです。


以下がポイントでしょうか。
  1. 行コレクションに対する map/reduce を、クロージャで書いている
  2. 行からRecordオブジェクトへの変換を、Extension methodで書いている
  3. Recordの足し込みを、"+"演算子オーバロードで書いている

ありきたりの内容ですが、Javaプログラマ向けの最初の練習問題としては、まぁまぁかな?

2012年1月20日金曜日

社内向けのXtend紹介資料

私の所属する会社には、毎月1回「帰社日」という、社員が集まって色々なセッションを行う日があります。私は今日そこで、Xtendを紹介しました。今後Xtendの機能を紹介するとっかかりにもなりますので、そのスライド資料をここに公開しておこうと思います。

ちなみに他のセッションとして、Xtendの強力なライバルでもあり先生でもある、Groovy紹介セッションもありました。Groovy紹介セッションの後に、私のXtend紹介セッションの流れだったので、むしろ短い時間でXtendの魅力を伝えることができた気もします。

2012年1月19日木曜日

JavaをよりパワフルにするXtend

EclipseのXtextチームが、Xtendという新しいJVM言語処理系をリリースしています。
Javaでソフトウェアを開発している人にとって、Xtendは導入の検討に値する言語処理系です。
沢山の言語やフレームワークがある現在、Javaよりも開発しやすい言語の選択肢は沢山ありますが、にも拘らずJavaを採用しているのには、他の言語を採用できないそれなりの理由があると思います。
Xtendは、Javaのソースを生成する言語処理系であり、Javaを置き換えるものではないため導入の障壁が低く、しかもJavaの多くのものをそのまま利用しているため、学習コストがとても低いというのが、魅力です。

Xtend == Java + α != 新言語
Xtendは独立した新言語というより、Java言語のエクステンションです。Xtendは、Javaプロジェクトの中で、Java言語と一緒に使います。
Xtendは、XtendクラスをJavaクラスのソースに変換します。
Xtendは、Javaのインタフェース、列挙、アノテーションなどは、そのままの形で利用します。

Xtendの考えは、Javaのインタフェースなどの要素は十分簡潔かつパワフルであり、複雑冗長になりがちなクラス実装記述さえ簡潔に置き換えれば、Javaの開発効率は大幅に改善できる、というものです。
このように、XtendはJavaのクラス実装しか置き換えないため、その習得は、新しい言語処理系をまるまる学習するよりも、はるかに容易です。
「Javaクラスの簡潔な書き方を覚える」という、とても軽い気持ちで学ぶことができるんです。

Hello Xtend !
Xtendを始めるのは、とっても簡単です。
Eclipseを用意すれば、以下の要領ですぐに始められます。
  1. Eclipse MarketplaceからXtendをインストールする
  2. Javaプロジェクトを作る
  3. Xtendクラスを新規作成
    srcフォルダにXtendクラスを作ります
    [File]/[New]/[Xtend Class] -> Name: HelloXtend
  4. Xtendライブラリをビルドパスに追加する
    Xtendエディタ上のビルドパスエラーを、Ctrl + 1でQuickFixします
  5. xtend-srcフォルダにJavaソースが生成されていることを確認
  6. Xtendクラスを編集する
  7. Javaソースも生成されていることを確認
  8. HelloXtend.xtendをJavaアプリケーションとして実行
  9. 実行結果を確認

クロージャの例
Hello Xtendの例だけでは、Xtendの魅力が全く伝わらないですよね。
せっかくなので、Xtendのクロージャの例を見てみましょう。
コレクションのフィルタ処理が、簡潔に記述できることが分かると思います。



生成されるJavaソースの例
この時に生成されているJavaソースは以下のような感じです。
Xtendでのクラス実装がJavaソースにコンパイルされていることがわかります。
Xtendのトリックも、ちゃんとJavaコードになっています。


まだまだあるXtendの機能

Xtendには、クロージャの他にも、便利な機能がいくつもあります。
それらの機能が、クラス実装を簡潔にする手助けをしてくれます。
そしてそれらもまた、使うのはとても簡単なんです。
今度は、それらを紹介しようと思っています。