Wikiエンジンの開発
13.3 CGIの処理
今日は"cgiEnvs"関数を見ていきます。
定義はこんな感じ。
cgiEnvs = return . catMaybes =<< mapM mGetEnvPair names where mGetEnvPair :: String -> IO (Maybe (String, String)) mGetEnvPair name = catch (return . Just . (,) name =<< getEnv name) (const $ return Nothing) names = [ "SERVER_NAME", "SERVER_PORT", "SERVER_SOFTWARE", "SERVER_PROTOCOL", "GATEWAY_INTERFACE", "SCRIPT_NAME", "REQUEST_METHOD", "PATH_INFO", "PATH_TRANSLATED", "CONTENT_TYPE", "CONTENT_LENGTH", "QUERY_STRING", "HTTP_COOKIE", "HTTP_ACCEPT", "REMOTE_HOST", "REMOTE_ADDR", "REMOTE_USER", "AUTH_TYPE", "HTTPS" ]
何となくですが、各種環境変数を読み込んでいるように見えます。
それでは一つ一つ見ていこう。
まずは"names"。
これは関数じゃありません。変数です。
where節内で以下のように束縛されています。
names = [ "SERVER_NAME", "SERVER_PORT", "SERVER_SOFTWARE", "SERVER_PROTOCOL", "GATEWAY_INTERFACE", "SCRIPT_NAME", "REQUEST_METHOD", "PATH_INFO", "PATH_TRANSLATED", "CONTENT_TYPE", "CONTENT_LENGTH", "QUERY_STRING", "HTTP_COOKIE", "HTTP_ACCEPT", "REMOTE_HOST", "REMOTE_ADDR", "REMOTE_USER", "AUTH_TYPE", "HTTPS" ]
[String]型の変数ですね。
"mGetEnvPair"関数とこの変数に対して"mapM"関数が適用されます。
"mapM"関数はお初ですので、ちょっと見てみます。
mapM :: Monad m => (a -> m b) -> [a] -> m [b] mapM f as = sequence (map f as)
なるほど、"mapM"の"M"はモナドの"M"なんですね。
でも"sequence"って何だろう?
sequence :: Monad m => [m a] -> m [a] sequence = foldr mcons (return []) where mcons p q = p >>= \x -> q >>= \y -> return (x:y)
込み入って来ました。
丁寧に見ていこう。
型だけをみると、モナドクラス型の値のリストをモナドクラスのリストに変換しているようです。
"foldr"関数は既出ですね。
"mcons"関数はwhere節にて定義されています。
mcons p q = p >>= \x -> q >>= \y -> return (x:y)
うわぁ、モナドだらけ(汗)。
"mcons"関数の第一引数は"sequence"関数に与えられたモナドクラス型リストの各要素です。
"mcons"関数の第二引数も、型としては第一引数と同じです。
括弧をつけるともうちょっと分かりやすくなるかな?
mcons p q = p >>= (\x -> q) >>= (\y -> return (x:y)) mcons p q = do p (\x -> q) (\y -> return (x:y))
do式に展開もしてみました。
なるほど、"mcons"関数はモナドを外してリストにした上でモナド化していますね。
具体的な値を使って確認してみよう。
mcons (Just 1) Nothing do (Just 1) (\x -> Nothing) (\y -> return (x:y))
ラムダ式の引数"x"と"y"は">>="演算子でモナドじゃなくなった値ですから、
上の場合だと、1、Nothingです。
Prelude> Just 1 >>= (\x -> return x) Just 1 Prelude> Nothing >>= (\x -> return x) Nothing
あぁ、今日は時間切れです。
続きはまた明日。