意外と制限があって難しい, 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
となる。
モナド変換子などについても。