kmizuの日記

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

JavaプログラマのためのIOモナド

特に説明はしません。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();
  }
}