Wikiエンジンの開発
13.3 CGIの開発
今日は"mGetEnvPair"関数の続きです。
昨日は"mGetEnvPair"関数の前半戦では、環境変数の名前をもらって、
IO (Maybe (環境変数名, 環境変数の値))
というIO型のMaybeタプル(アクション)を生成しました。
※昨日の記事は間違っていました。
今日は後半戦です。
…と、その前にこの構文が理解出来ていませんでした。
catch (...) (...)
予測ですけど、JavaとかC#における、try 〜 catch(もしくはfinally)...にあたる構文なのかなぁ。
ほんのちょっと調べてみました。
ここにいろいろ載ってました。
やっぱりそうですね、"catch"関数の第1引数がtryブロック、第2引数がcatchブロックにあたるわけですね。
catch (例外が発生する可能性があるアクション) (例外が発生した時に適用される関数(例外ハンドラ))
"mGetEnvPair"関数の場合、"getEnv"アクションで例外が発生した場合、第2引数が適用されるわけです。
つまり、指定した環境変数が定義されていない場合、第2引数の関数が適用されます。
では第2引数の例外ハンドラを見ていきます。
(const $ return Nothing)
"const"関数がお初ですね。
調べてみましょう。
- Preludeモジュール
const :: a -> b -> a const x _ = x
"const"関数の第2引数が何であれ、第1引数を返す関数ですね。
あぁ、そうか。
例外ハンドラには"IOError"型の値が引数に指定されるわけですね。
それを無視して、"const"関数の第1引数を返す。
簡単な例でみてみます。
Prelude> :m + System Prelude System> catch (getEnv "yohpapa") (const $ return "catch exception!") "catch exception!"
つまり、環境変数が設定されていない場合、
return Nothing
というアクションが返るわけです。
これでなぜタプルがMaybe型になっていたか分かりました。
環境変数が設定されていない場合、Nothingを返したいからなんですね。
これで"mGetEnvPair"関数の理解が出来ました。
そして"cgiEnvs"関数に戻ります。
cgiEnvs = return . catMaybes =<< mapM mGetEnvPair names
(=<<)演算子の右辺で、環境変数名と環境変数値のタプルがアクションとして取得出来ます。
これを左辺に渡します。
最後に左辺の"catMaybes"関数です。
定義はこんな感じ。
- Data.Maybeモジュール
catMaybes :: [Maybe a] -> [a] catMaybes ms = [ m | Just m <- ms ]
おっと、本書で学習したリスト内包表記が現れました。
引数のリストの各要素に対して、非モナド化して、再度リストを作ります。
"cat"は"catenate"の略で「連結する」の意味です。
非モナド化して、連結する、という意味でしょうか。
簡単な例で示すとこんな感じ。
Prelude> :m + Data.Maybe Prelude Data.Maybe> [m | Just m <- [Nothing, Just 1, Just 2, Just 3]] [1,2,3]
なお、"catMaybes"関数のリストの要素にNothingがある場合、
Just mのパターンマッチに引っかからないので破棄されます。
さて"cgiEnvs"関数に戻ると、(=<<)の右辺(アクション)は(=<<)演算子によって、
Maybe型タプルのリストとなり、左辺のアクションに適用されます。
Maybe型タプルのリストはタプルのリストとなります。
やっと"cgiEnvs"関数の追跡が終わりました。
"cgiEnvs"関数は、"names"に束縛された各環境変数を
(環境変数名, 環境変数の値)
というタプルのリストにします。
最後に"return"関数を適用して、上記タプルのリストのアクションとなるわけです。
結果だけみるととてもシンプルな機能の関数でしたね。
今日はここまで!