ふと ECJ (eclipse compiler for Java) bug. インナークラスのコンストラクタを外から無名クラスとして継承すると型エラー のことを思い出した。
JavaコンパイラECJのインナークラスに関するバグを2件見つけて、パッチを送ったのだった。
現在のバージョンKepler Service Release 2 ではパッチが反映されていることを確認した。
これは、Java7以前でモナドをいかに軽量に書けるか?ということを試していて発見したのだった。
せっかくなので、全く意味のない例ではあるが、 これを使って State モナドを書いてみた。 ほげほげ.new Bind<..>(){...}.run() と書けるようになった、というところ(だけ)がポイントだ。
Java 7 ではこれが限界だけど、 Java 8 ではどれくらい軽量に書けるだろうか?
StateSample.java
package _sandbox;
import static _sandbox.State.*;
public class StateSample {
public static void main(String args[]) {
// in Haskell: snd . runState (get >>= \s -> put (s+1) >>= \_ -> get) 100
Integer x = new Get<Integer>().new Bind<Unit>(){
public State<Integer,Unit> f(Integer s) {
return new Put<>(s+1);
}
}.new Bind<Integer>(){
public State<Integer, Integer> f(Unit _) {
return new Get<>();
}
}.run(100).snd;
System.out.println(x); // 101
}
}
State.java
package _sandbox;
final class Pair<X,Y> {
public final X fst;
public final Y snd;
public Pair(X fst, Y snd) {
this.fst = fst;
this.snd = snd;
}
}
final class Unit{
public static final Unit unit = new Unit();
private Unit() {}
}
public abstract class State<S,A> {
public abstract Pair<S,A> run(S s);
// return
public static final class Return<S,A> extends State<S,A> {
public final A ret;
public Return(A ret) {
this.ret = ret;
}
public final Pair<S,A> run(S s) {
return new Pair<>(s,this.ret);
}
}
// bind (>>=)
public abstract class Bind<B> extends State<S,B> {
public abstract State<S,B> f(A a); // override this
public final Pair<S,B> run(S s) {
Pair<S,A> result = State.this.run(s);
return this.f(result.snd).run(result.fst);
}
}
// get
public static final class Get<S> extends State<S,S> {
public Get() {}
public final Pair<S,S> run(S s) {
return new Pair<>(s,s);
}
}
// put
public static final class Put<S> extends State<S,Unit> {
public final S newState;
public Put(S newState) {
this.newState = newState;
}
public final Pair<S,Unit> run(S _) {
return new Pair<>(this.newState,Unit.unit);
}
}
}