特に説明はしません。IOモナドなんて言っても、別に難しいことではなく、結局やってる事はこんな感じですというのがわかってもらえれば。もちろん、実際のHaskell処理系がこのような実装になっているという意味ではなく、JavaプログラマはこのようなイメージでIOモナドを理解すればいいのではないかという提案に過ぎないので、その点は注意してください。
package iomonad; import java.util.Scanner; public class IOLib { enum Unit{VALUE};//一つしか値を持たない型 public static Unit UNIT = Unit.VALUE; /** * HaskellのIO 'a型に相当 */ public interface IO<A> { /** * IOを「実行」して、A型の値を得る処理. * ユーザが直接呼び出すと参照透明性を破壊する恐れがある * HaskellのunsafePerformIO相当. */ public A perform(); // } /** * Haskellの'a -> 'b型に相当.要は関数を表すオブジェクト */ public interface F<A, B> { public B apply(A arg); } // Haskellのreturn aに相当 public static <A> IO<A> returns(final A a) { return new IO<A>() { public A perform() { return a; //do nothing } }; } // ioA >>= f public static <A, B> IO<B> bind(final IO<A> ioA, final F<A, IO<B>> f) { return new IO<B>() { public B perform() { return f.apply(ioA.perform()).perform(); } }; } // ioA >> ioB public static <A, B> IO<B> concat(final IO<A> ioA, final IO<B> ioB) { return bind(ioA, new F<A, IO<B>>() { public IO<B> apply(A arg) {//arg is ignored return ioB; } }); } // putStrLn str public static IO<Unit> putStrLn(final String str) { return new IO<Unit>() { public Unit perform() { System.out.println(str); return UNIT; } }; } // putStr str public static IO<Unit> putStr(final String str) { return new IO<Unit>() { public Unit perform() { System.out.print(str); return UNIT; } }; } private static Scanner scanner = new Scanner(System.in); // getLine public static IO<String> getLine = new IO<String>() { public String perform() { return scanner.nextLine(); } }; }
package iomonad; import static iomonad.IOLib.*; public class IOMain { /* ユーザプログラム */ //putStr "please input string: " >> getLine >>= \line -> putStrLn line public static IO<Unit> main = concat( putStr("please input string: "), bind( getLine, new F<String, IO<Unit>>() { public IO<Unit> apply(String line) { return putStrLn(line); } } ) ); /* 言語処理系が裏側でやってくれる処理の部分 */ public static void main(String[] args) { main.perform(); } }