モナドとはプログラミング言語の機能で、その機能を備えている言語(例えばHaskell)じゃないと「モナド使えない」とか、「モナドとは関係ねー」とか思っている人がいるんじゃないでしょうか。そんなことは全然ないですよ。
モナドの実用的な応用事例として、僕が最初に思い浮かべるのは X Window System だったりします。X Window System のアーキテクチャはモナディックですもの -- と言ってみても分かりにくいですよね。それで、X Window System の一千万倍(?)くらい簡単なグラフィックスのシステムをJavaScriptで作ってみました。
Firefox + Firebug を前提としたタートルグラフィックスです。タートル・モナド - 檜山正幸のキマイラ飼育記 メモ編 にソースがあるので、これをFirefoxに読み込んでください。Firebugのコンソールをインタプリタ・フロントエンドとして使います。(オンライン版をいますぐ試してみる。)
- タートルシステムを初期化して開始するために、start() とします。タートル(小さな黒い三角形)がキャンバス上に現れます。
- デフォルトでは、タートルコマンドがキューイングされるので、キューイングを無効化するために qmode(false) とします。
後は適当に、タートルコマンドを実行します。
/* 対話的に使用する便利関数群 */
/*
* start() -- 初期化
* fd(r) -- タートル前方に直進 forward
* bk(r) -- タートル後方に直進 back
* rt(a) -- タートル右に回転 right 角度aは360度方式
* lt(a) -- タートル左に回転 left 角度aは360度方式
* x() -- 溜まっているオペレーション・リクエストの実行
* q() -- キューの表示
* qmode(f) -- キューイングするかどうかの設定
* fがtrueならキューイングモードになる
*/
オモチャとしてサッパリ面白くないですが、我慢してしばらくはイジッテくださいな。
動きがわかったら、次の3つのソースを見比べてみてください。
どれも似たり寄ったりです。同じスケルトンからコピー&モディファイ方式で作ったものですから。
これらは、モニャドセミナー4の素材として考えた5つのモノイダル・スタンピング・モナドのうちの3つです。
http://d.hatena.ne.jp/m-hiyama-memo/19990731 から:
今日のモノイドさん、モナドさん
- 有限カウンタ 0から9 有界
- 有限カウンタ 0から9 サイクリック(ラップアラウンド)
- 出力モードのテキストファイル
- タートルグラフィックス ペンなし
- タートルグラフィックス ペンあり
CounterMonadは有界カウンタとサイクリックカウンタの両方を実装してます。CyclicCounterMonadはサイクリックカウンタだけの実装、より単純になっています。ファイルIOはJavaScriptではやりにくいので「出力モードのテキストファイル」はありません。TurtleMonadは「タートルグラフィックス ペンなし」の実装です。「ペンあり」は面倒なのでやってません^^;
それぞれのモナドのもとになったモノイドは次のとおり。
- CounterMonad -- 'u', 'd' の2文字から構成される文字列の連接演算、単位は空文字列。
- CyclicCounterMonad -- 整数の足し算、単位は0。
- TurtleMonad -- タートル操作基本命令のリストの連接演算、単位は空リスト
タートルモナドのモノイドはアフィン変換の行列掛け算を使うつもりでしたが、回りくどいだけで意味なさそうなので、リスト連接に変更しました。
カウンタやタートルを操作する“関数”(より正確には、任意の計算単位)がクライスリ射になります。戻り値は普通に return を使いますが、モノイドの要素を出力するには、Counter.operate, Turtle.operate というメソッドを使います。operateメソッドの内部で、クライスリ結合はやってくれるので、クライアントコードは、特に何もする必要はありません。
デフォルトのモードをキューイング・モードにしたのは、そのほうがクライスリ結合の状況がハッキリとわかると思ったからです。実行(execOperationsメソッド呼び出し)をこまめにやっても、遅延させて後でまとめてやっても最終結果は同じです。
カウンタやタートルの実装には、クライスリ圏からのラッピング/アンラッピング対応を使っています。
上の図のMの値を保持しているのは、_operations 変数です。モノイド演算の実行は、operateメソッド内で *Op.mult を呼んでいる所で行っています(* は CounterまたはTurtle)。初期化や再初期化において、 *._operations = *Op.unit();
としているのはアンラップ操作に対応しています。
ホワイトボードの絵では描いてありませんが、モノイドが作用する状態空間の状態値は次の変数に保持されています。
- Counterでは、_value 変数
- Turtleでは、_x, _y, _dir 変数