kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Java 7のクロージャ(BGGA版)のプロトタイプを試してみた

Java API、使ってますか? (53) Java SE 7の要注目機能"クロージャ"はどうなるのか

の記事を参考に、ここからプロトタイプ実装をダウンロードしてきてインストールしてみた。

注意点として、上記の紹介記事ではJDK 5.0以降が必要という風に書かれているが、JDK 5.0だと次のようにUnsupportedClassVersionErrorが出てコンパイラが実行できなかった(JDK 6.0だと動作することは確認)。

Error occurred during initialization of VM
java/lang/UnsupportedClassVersionError: Bad version number in .class file

さて、早速、まずは上記リンク先の紹介記事に書かれているコードをコンパイルしてみた。

>bin\javac FirstClosure.java

コンパイルは無事に成功し、以下のファイルが生成された。

FirstClosure.class
FirstClosure$1.class
javax\lang\function\I.class
javax\lang\function\OO.class
javax\lang\function\unrestricted\I.class
javax\lang\function\unrestricted\OO.class

実行してみる。

>bin\java FirstClosure
1234

次は、定番の(?)map関数を定義してみる。

import java.util.*;

public class MapFunction {
  public static <A, B> List<B> map({A => B} fn, List<A> list) {
    List<B> newList = new ArrayList<B>();
    for(A a:list) newList.add(fn.invoke(a));
    return newList;
  }
  public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    System.out.println(map({Integer n => n * 2}, list));
  }
}

コンパイル。

>bin\javac MapFunction.java

実行。

>bin\java MapFunction
[2, 4, 6, 8, 10]

とりあえず、BGGA版のクロージャ(というか無名関数)は、記法は違うものの、慣れればScalaの無名関数と同じような感じで使えそう(当たり前だが)。ただ、Scalaと比べた場合、無名関数の引数の型を省略できないのが、ちょっと嫌かも。

追記:Rubyなどにある、ブロックの実行が終わったら必ず閉じるopenのようなものも実装してみた。

import java.io.*;
import static java.lang.System.*;
public class ClosingResource {
  public static <T, throws E> T open(
    String filePath, {Reader => T throws E} block
  ) throws FileNotFoundException, E {
    FileReader reader = new FileReader(filePath);
    try {
      return block.invoke(reader);
    } finally {
      try { 
        out.printf("closing %s...%n", filePath);
        reader.close(); 
      } catch (IOException ex) { 
        ex.printStackTrace();
      }
    }
  }
  public static void main(String[] args) throws Exception {
    String result = open(
      "input.txt",
      {Reader r => 
        out.println("read started");
        StringBuilder buf = new StringBuilder();
        for(int ch; (ch = r.read()) != -1;) buf.append((char)ch); 
        out.println("read finished");
        new String(buf)
      }
    );
    System.out.println(result);
  }
}

例外に対して透過にするためにをつけるのがめんどいのと、複数の文から成る無名関数から値を返したいときの記法(;をつけない)がちょっとキモイ。

コンパイル。

>bin\javac ClosingResource.java

実行。

>bin\java ClosingResource
read started
read finished
closing input.txt...
a
b
c

input.txtの内容:

>type input.txt
a
b
c