FunctionalJavaには、F1.javaやF2.javaなど、メソッド1つだけのabstract classを変更すればFunctionalInterfaceになるものが大量にあるので、ちょっと試してみました。
以下にわかりやすい書き換え例を上げておきます。
思ったよりも結構ラムダの型推論が効いて、(varが存在しない以外は) C# に匹敵するくらいまで、型を書かなくてよくなって素晴らしいですね。
一番顕著な例がこんな感じ
https://github.com/xuwei-k/functionaljava/commit/5372e395f923e16271cc131bdf8f13b837d741f9#L11R566
変換前↓(良くも悪くも頭おかしい)
public static <A, B, C, D, E, F$, G, H, I> F<A, F<B, F<C, F<D, F<E, F<F$, F<G, F<H, I>>>>>>>> curry( final F8<A, B, C, D, E, F$, G, H, I> f) { return new F<A, F<B, F<C, F<D, F<E, F<F$, F<G, F<H, I>>>>>>>>() { public F<B, F<C, F<D, F<E, F<F$, F<G, F<H, I>>>>>>> f(final A a) { return new F<B, F<C, F<D, F<E, F<F$, F<G, F<H, I>>>>>>>() { public F<C, F<D, F<E, F<F$, F<G, F<H, I>>>>>> f(final B b) { return new F<C, F<D, F<E, F<F$, F<G, F<H, I>>>>>>() { public F<D, F<E, F<F$, F<G, F<H, I>>>>> f(final C c) { return new F<D, F<E, F<F$, F<G, F<H, I>>>>>() { public F<E, F<F$, F<G, F<H, I>>>> f(final D d) { return new F<E, F<F$, F<G, F<H, I>>>>() { public F<F$, F<G, F<H, I>>> f(final E e) { return new F<F$, F<G, F<H, I>>>() { public F<G, F<H, I>> f(final F$ f$) { return new F<G, F<H, I>>() { public F<H, I> f(final G g) { return new F<H, I>() { public I f(final H h) { return f.f(a, b, c, d, e, f$, g, h); } }; } }; } }; } }; } }; } }; } }; } }; }
変換後↓
public static <A, B, C, D, E, F$, G, H, I> F<A, F<B, F<C, F<D, F<E, F<F$, F<G, F<H, I>>>>>>>> curry( final F8<A, B, C, D, E, F$, G, H, I> f) { return a -> b -> c -> d -> e -> f$ -> g -> h -> f.f(a, b, c, d, e, f$, g, h); }
あとは、例えばこれ
public static <E> Parser<Stream<Character>, Digit, E> digit(final P1<E> missing, final F<Character, E> sat) { return StreamParser.satisfy(missing, sat, new F<Character, Boolean>() { public Boolean f(final Character c) { return Character.isDigit(c); } }).map(new F<Character, Digit>() { public Digit f(final Character c) { return Digit.fromChar(c).some(); } }); }
が、以下のようになるわけですが
public static <E> Parser<Stream<Character>, Digit, E> digit(final P1<E> missing, final F<Character, E> sat) { return StreamParser.satisfy(missing, sat, c -> Character.isDigit(c) ).map( c -> Digit.fromChar(c).some() ); }
c -> Character.isDigit(c)
という「単に引数をそのまま渡してメソッドを呼び出しているだけのパターン」は、以下のようにメソッド参照を使ってCharacter::isDigit
というように、さらに簡略化できて、ラムダの引数に名前付けなくてすむのも地味に嬉しいですね
public static <E> Parser<Stream<Character>, Digit, E> digit(final P1<E> missing, final F<Character, E> sat) { return StreamParser.satisfy(missing, sat, Character::isDigit ).map( c -> Digit.fromChar(c).some() ); }
このあたりは、(微妙に文法が違うだけで) Scala や C# とおなじです。
他にもFunctionalJavaではSAM*1 type が使われているところがとても大量にあるので、まだまだ全部は変換できていませんが、途中のものを公開しておきます。
https://github.com/xuwei-k/functionaljava/commit/5372e395f92
ところで、sbtがJava8に全く対応してなくて、mavenでビルドするのつらいです・・・(´・ω・`)はやく対応しないかな・・・。あと、maven詳しくなくて、調べるの面倒だったのでビルドファイルがcoreしか書き換えてなくてかなり雑なのですが、誰か・・・
*1:single abstract method