続・Javaで複合モナド
前回の自分用宿題
・ 拡張スタイルの関数(というかbind)の導出
これをやってみる。あと逆に拡張スタイルから代数スタイルの導出もやる。
- unit, flatten, mapからのbindの導出
- unit, bindからのflatten, mapの導出
- e_unit, e_bind, l_unit, l_bind, combinationsから el_bindの導出
unit, flatten, mapからのbindの導出
bind(m, f) = flatten(map(f, m))であった。※bindは引数の順序を変えてる
public class Enum<T> { ... // extension style public static <X, Y> Enum<Y> e_bind(Enum<X> e, Function<X, Enum<Y>> f) { return e_flatten(e_map(f, e)); } // Function version (= e_ext) public static <X, Y> Function<Enum<X>, Enum<Y>> e_bind(final Function<X, Enum<Y>> f) { return new Function<Enum<X>, Enum<Y>>() { public Enum<Y> apply(Enum<X> e) { return e_bind(e, f); } }; } }
テスト。関数が等しいということは直接テストできないけど、とりあえず同じ入力に対する出力が一致することを確認する。
// サンプル関数1. 1を加えてEnum<Long>で戻す Function<Long, Enum<Long>> sum1 = new Function<Long, Enum<Long>>() { public Enum<Long> apply(Long x) { return e_unit(Long.valueOf(x.longValue() + 1)); } }; // サンプル関数2. nの素因数をEnum<Long>で戻す Function<Long, Enum<Long>> factors = new Function<Long, Enum<Long>>() { public Enum<Long> apply(Long n) { if (n < 1) { throw new IllegalArgumentException("arg should be positive."); } if (n == 1) { return new Enum<Long>(1L); } Set<Long> result = new HashSet<Long>(); long k = n; for (long i = 2, r = Math.round(Math.sqrt(k)); i <= r; i ++) { while (k % i == 0) { result.add(i); k /= i; r = (int) Math.round(Math.sqrt(k)); } } if (k != 1) { result.add(k); } return new Enum<Long>(result); } }; // 拡張スタイルのモナド則 // (return x) >>= f ≡ f x @Test public void testRule1() { assertEquals( e_bind(e_unit(195955200000000L), factors), factors.apply(195955200000000L) ); } // m >>= return ≡ m @Test public void testRule2() { Enum<Integer> m = new Enum<Integer>(0, 1, -3, 7, 2); assertEquals( e_bind(m, Enum.<Integer>e_unit()), m ); } // (m >>= f) >>= g ≡ m >>= ( \x -> (f x >>= g) ) @Test public void testRule3() { Enum<Long> m = new Enum<Long>(81L, 82L, 83L, 84L, 85L); assertEquals( e_bind(e_bind(m, factors), sum1), e_bind(m, new Function<Long, Enum<Long>>() { public Enum<Long> apply(Long l) { return e_bind(factors.apply(l), sum1); } }) ); }
ちなみに最後のe_bind(e_bind(m, factors), sum1)は81, 82, 83, 84, 85の素因数の和集合のそれぞれに1を足す処理で、結果は{84, 18, 3, 4, 6, 42, 8}とかになります。特に使い途は無い。
l_*やel_*についても同じようにl_bind、el_bindを作れる。
unit, bindからのflatten, mapの導出
今度は逆に、e_unitとe_bindを使ってe_flattenとe_mapを定義してみる。
まずe_map。名前が元のe_mapと被らないようにe_map_by_extにしています。
public class Enum<T> { ... // mapをunitとbindで書いてみる public static <X, Y> Enum<Y> e_map_by_ext(final Function<X, Y> f, Enum<X> e) { return e_bind(e, new Function<X, Enum<Y>>() { public Enum<Y> apply(X x) { return e_unit(f.apply(x)); } }); } */
bindが取る関数はf:X→Enum
次にe_flatten。unitとbindでネスト数を減らすのは無理な気もしたけどそうでもなかった。
*/ // flattenをunitとbindで書いてみる public static <X> Enum<X> e_flatten_by_ext(Enum<Enum<X>> ee) { return e_bind(ee, e_bind(Enum.<X>e_unit())); } }
e_bind(Enum.
l_*についても同じようにl_unit, l_bindからl_map, l_flattenを作れる。
e_unit, e_bind, l_unit, l_bind, combinationsから複合モナドの関数の導出
el_unit, el_map, el_flattenがe_unit, e_map, e_flatten, l_unit, l_map, l_flattenとcombinationsから導出できるということは、e_unit, e_bind, l_unit, l_bind, combinationsからも導出できるはず。ということはそれらからel_bindも導出できるはず。
まずel_unitはe_unit(l_unit)なので前回と同じ。
el_map(f, el) := e_map(l_map(f), el)なのでe_mapとl_mapを展開する。
public static <X, Y> EnumOfList<Y> el_map(Function<X, Y> f, EnumOfList<X> el) { return new EnumOfList<Y>(e_map(l_map(f), el)); } // ↓↓↓ public static <X, Y> EnumOfList<Y> el_map_by_ext(final Function<X, Y> f, EnumOfList<X> el) { return new EnumOfList<Y>( e_bind(el, new Function<List<X>, Enum<List<Y>>>() { public Enum<List<Y>> apply(List<X> x) { return e_unit(l_bind(x, new Function<X, List<Y>>() { public List<Y> apply(X x) { return l_unit(f.apply(x)); } })); } }) ); }
もうちょっと整理できそうな気もする。
el_flatten(elel) := e_flatten(e_map(e_map(l_flatten), e_map(combinations, elel)))を展開する。
public static <X> EnumOfList<X> el_flatten(EnumOfList<EnumOfList<X>> elel) { return new EnumOfList<X>( e_flatten( e_map( e_map(List.<X>l_flatten()), e_map(Util.<List<X>, EnumOfList<X>>combinations(), elel) ))); } // ↓↓↓ public static <X> EnumOfList<X> el_flatten_by_ext(EnumOfList<EnumOfList<X>> elel) { return new EnumOfList<X>( e_bind( e_bind(elel, new Function<List<EnumOfList<X>>, Enum<Enum<List<List<X>>>>>() { public Enum<Enum<List<List<X>>>> apply(List<EnumOfList<X>> x) { return e_unit(Util.<List<X>, EnumOfList<X>>combinations().apply(x)); } }), e_bind(new Function<List<List<X>>, Enum<List<X>>>() { public Enum<List<X>> apply(List<List<X>> x) { return e_unit(l_bind(x, l_bind(List.<X>l_unit()))); } })) ); }
e_flatten(e_map)はe_bindなのでそのまま置き換えて、それ以外のところはごりごり変換している。
もうちょっと整理できそうな気もする。
el_bindはel_flatten(el_map)なので、ええと…
public static <X, Y> EnumOfList<Y> el_bind(EnumOfList<X> el, Function<X, EnumOfList<Y>> f) { return el_flatten(el_map(f, el)); } // ↓↓↓ public static <X, Y> EnumOfList<Y> el_bind_by_ext(EnumOfList<X> el, final Function<X, EnumOfList<Y>> f) { return new EnumOfList<Y>( e_bind( e_bind( e_bind(el, new Function<List<X>, Enum<List<EnumOfList<Y>>>>() { public Enum<List<EnumOfList<Y>>> apply(List<X> x) { return e_unit(l_bind(x, new Function<X, List<EnumOfList<Y>>>() { public List<EnumOfList<Y>> apply(X x) { return l_unit(f.apply(x)); } })); } }), new Function<List<EnumOfList<Y>>, Enum<Enum<List<List<Y>>>>>() { public Enum<Enum<List<List<Y>>>> apply(List<EnumOfList<Y>> l) { return e_unit(Util.<List<Y>, EnumOfList<Y>>combinations().apply(l)); } }), e_bind(new Function<List<List<Y>>, Enum<List<Y>>>() { public Enum<List<Y>> apply(List<List<Y>> x) { return e_unit(l_bind(x, l_bind(List.<Y>l_unit()))); } }) )); }
型はあってる……
もうちょっと整理できそうな気もするけど、とりあえずunitとbindとcombinationsでも複合モナドが導き出せることが分かった。
*1:検索したら「join = bind id」って結構出てますね