意味的にはexhaustiveなnon-exhaustive パターンマッチ
ReaderTは投げっぱなしで勉強会終了。モナドの合成はまだよくわからない。
で、気になったところが一点。p.312のfillTemplate関数のwhere節の中のfill関数の実装で、"break (== '$') tmpl"の結果を受けるパターンマッチの後半が"(s,cont)"になっているのは何故だろうか?
http://www.loveruby.net/ja/stdhaskell/samples/lazylines/Template.hs.html より、
fillTemplate repo id params = return . fill =<< loadTemplate repo id where fill "" = "" fill tmpl = case break (== '$') tmpl of (s, ('$':cs)) -> s ++ expand var ++ fill cont where (var, cont) = span isAlpha cs (s, cont) -> s ++ fill cont expand var = fromMaybe ('$':var) (lookup var params)
breakは、(a -> Bool) -> [a] -> ([a], [a])という型を持っていて、与えたリストを前からスキャンして、「与えた述語を満たさないもの」と「与えた述語をはじめて満たしたもの以降」のリストの組を返す関数。つまり、(=='$')という述語を与えていれば、breakの返り値のペアの後ろ側は'$'からはじまるか、あるいは空文字列かどちらかなはず。
そうすると、"(s,('$':cs))"のパターンにマッチしないのは、(s,"")のケースだけ。それならソースにもそれを反映させるほうがいいのではないだろうか。contが空文字しか有り得ないなら、後半のfill contもいらないことは自明だし。"(s,cont)"と書かれていると、contに空文字列でない文字列が束縛される場合があるのかと思って悩んでしまった。
つまり、こう
fill "" = "" fill tmpl = case break (== '$') tmpl of (s, ('$':cs)) -> s ++ expand var ++ fill cont where (var, cont) = span isAlpha cs (s, "") -> s
ただ、こう書いてしまうと、GHCで-Wオプション(警告あり)をつけた時に
Warning: Pattern match(es) are non-exhaustive In a case alternative: Patterns not matched: (_, (GHC.Base.C# #x) : _) with #x `notElem` ['$']
という警告は出る。リスト構造的にはパターンを列挙しきってないので、これはしょうがない。breakの仕様まで解析させるのは無茶だろうし。
ただ警告を嫌うにしても、
fill "" = "" fill tmpl = case break (== '$') tmpl of (s, ('$':cs)) -> s ++ expand var ++ fill cont where (var, cont) = span isAlpha cs (s, "") -> s _ -> error "unreachable"
みたいに「万が一ここに到達したらエラーで落ちる」ようなデフォルトのパターンを追加しておいて、明示的に"これ以外のパターンはこない"ことをソースに示しておいたほうがいいんじゃないかなあ。haskellでは、こういうケースにどう書くのが一般的なんだろう。
…もしかして、breakの仕様を思い切り読み間違ってたりするだろうか…