意外と制限があって難しい, do記法について。
Haskell 2010 で, 構文は次のようになっている。[ ] は省略可, | はどちらか.
do { stmts }
;] (n ≧ 0)
;<- exp ;let decls ;;
ただし, expは式, patは変数やパタンマッチ, declsは宣言。let文がいくつも書けることが分かる。
上記の { 〜 }, ; については、レイアウトルール (off-sideルール) により、字下げしたときは省略できる。普通は省略するように字下げする。
Haskell のdo記法は構文糖 (syntactic sugar). 理解のために, 脱糖 (desugar) したときどうなるか, を見てみる。
次のプログラムは,
次のように書き換わる;
前から順に >> 演算子か >>= 演算子で繋ぐ形に書き換えられる。(>>)をthen演算子, (>>=)をbind演算子という。いずれも、左側を実行してから右側を実行する演算子。これで順次実行ができる。
>>= のほうは、ラムダ式で受ける形。
厳密な書き換えルールは次のようになっている。これも Haskell 2010 から;
| 1. | do { e }
| = | e | ||||||
| 2. | do { e ; stmts }
| = | e >> do { stmts }
| ||||||
| 3. | do { pat <- e ; stmts }
| = |
| ||||||
| 4. | do { let decls; stmts }
| = | let decls in do { stmts }
| ||||||
書き換えルール3のpatは変数とは限らず、パタン。パタンマッチの失敗 (fail) があるので、このようになっている。失敗については後述。
ルール4から, let文は書いたところから後ろにしか効果がないことが分かる。
do記法内の式として, どんな型の式でも書けるのかどうか。
結論から言えば, 値の型は, 型クラス Monad を実装するデータ型でなければならない。インターネット上の解説で, IO a 型でなければならない、というのがあるが、誤り。もちろん IO モナドも書けるが, IO限定ではない。main関数が IO ()型なので勘違い?
(2020.5) モナドが何であるか、どのような制約を満たすか、などについては、ページを分けました; モナド, 具象データ型, モナド則の嬉しさ
パタンマッチの失敗は、例外とは違う。しかし、doの書き換えルールによって, do式内の後続の式 (文) は実行されない。
実行結果;
[2,3] nothing nothing [8,9,10] fail: Prelude.!!: index too large
なぜこのようになるのか。Maybeは, 型クラスMonadの実装として, 次のように定義されている。
instance Monad Maybe where
(Just x) >>= k = k x
Nothing >>= k = Nothing
return = Just
fail _ = Nothing
行5 で、indexが1のときは, パタンマッチ以前に Nothing >>= k = Nothingで, 値は Nothing となる。
index が 2のときには, []なので, 書き換えルール3でいう patにマッチしない。ok _のほうから failが呼び出される。この値は Nothing.
結局、do式の値は Nothingとなる。
モナド変換子などについても。