Wikiエンジンの開発

13.3 CGIの処理


昨日は"sequence"関数を追跡しました。
"sequence"関数は、たとえば

[Just 1, Just 2, Just 3]    -- → Just [1, 2, 3]
[IO "ab", IO "cd", IO "ef"]    -- → IO ["ab, "cd", "ef"]


という変換をすることが分かりました。
本日は"mapM"関数を見ていきます。
定義を再度掲載します。

mapM             :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f as        =  sequence (map f as)


"mapM"関数の第1引数"f"には"mGetEnvPair"関数が束縛されます。
"mapM"関数の第2引数"as"には

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" ]


が束縛されます。
"mGetEnvPair"関数の型はもちろん

mGetEnvPair :: String -> IO String


です。そして"as"の型も当然

names :: [String]


です。
つまり"mapM"関数の型変数"a"、"b"はいずれもStringなわけですね。
"mapM mGetEnvPair"合成関数によって、"names"の各要素はStringからIO Stringに変換され、リストとなります。
この時点では[IO String]型です。
この値に対して"sequence"関数を適用すると、IO [String]型になります。
では"mGetEnvPair"関数を見ていきましょう。
定義はこんな感じ。

mGetEnvPair name = catch (return . Just . (,) name =<< getEnv name)
                             (const $ return Nothing)


なんかお初なものがたくさんありますね。
まず"mGetEnvPair"関数の引数は上で書いた"names"変数の各要素に束縛されます。
ちょっと分解して考えてみよう。

catch (return . Just . (,) name =<< getEnv name)
      (const $ return Nothing)


まず"catch"関数。なんとなく例外のcatchを連想します。
定義はこちら。

catch      ::  IO a -> (IOError -> IO a) -> IO a 
catch      =   primCatch


う〜ん、どうも本当に例外をキャッチしているようですね。
"primCatch"関数の定義はどこにもありません。
括弧の中のどれかが例外をスルーするのでしょう。


次に"getEnv"関数(これが例外をスルーしそう)。
"getEnv"関数の説明を引用しておきます。
定義はありませんでした。環境依存なところがありますからね。

getEnv var は環境変数 var の 値を返す。
もし、変数 var が定義されていなければ、 isDoesNotExistError 例外を発生する。

http://www.sampou.org/haskell/report-revised-j/system.html


やっぱり例外をスルーしますね。
先の"catch"関数はこれを補足していたんですね。


ちなみに"getEnv"関数の型は

Prelude System> :i getEnv
getEnv :: String -> IO String 	-- Defined in System.Environment


です。単なる環境変数の値(文字列)ではなく、IOモナドの文字列を返します。
このIOモナドの文字列が、"<<="演算子で、左辺に渡ります。
左辺はこんな感じです。

return . Just . (,) name


まず"(,)"はタプルを作るための演算子を関数として使うための表現ですね。
つまり、環境変数の名前と値のタプルをここで作っているわけです。
つまり、

(,) name    -- → (環境変数の値, 環境変数名)
            -- (=<<)演算子によって、「IOモナドの環境変数の値」は「環境変数の値」に変換されます。


となります。
次に"Just"です。
これは関数じゃなくってMaybe型の値コンストラクタです。
単に

Just (環境変数の値, 環境変数名)


というMaybe型の値を作りだします。
そして最後に"return"関数。
Maybe型の"return"関数の定義はこんな感じ。

return           =  Just


なので、結果として

Just Just (環境変数の値, 環境変数名)
Just (環境変数の値, 環境変数名)


となります。簡単な例で確認してみます。

return . Just . (,) "abc" =<< (return "def")
return . Just . (,) "abc" (IO "def")
return . Just . ("abc", IO "def")
return . Just ("abc", IO "def")
Just ("abc", IO "def")


環境変数の名前と値(IOモナド)のタプルを作っているわけです。
"mGetEnvPair"の途中ですが、今日はここまで!