モナド

11.6 本章のまとめ


ようやくモナドの章が終わります。
Maybe、[]、IOと見てきましたが、(合っているかどうか微妙なところですが)
どれも難しいですね。
油断すると見失ってしまう感じです。


今の理解で第3部に進んで、理解を頭に馴染ませる必要があります。
どこかのサイトで、HaskellモナドとCのポインタを初学者の最初の壁だと表現してましたが、
私にはHaskellモナドの方が100倍難しく感じました。
すっかりノイマン型な頭になってしまっている証拠でしょうか。


さて練習問題に進みます。

11.7 練習問題

head.hsを(>>=)を使って書き直してみる

こんな感じになりました。

main = do cs <- getContents
          putStr $ tfirstNLines 10 cs

tfirstNLines :: Int -> String -> String
tfirstNLines n x = return (return (lines x) >>= take n) >>= unlines


returnがネストしていて気持ち悪いですが、一応動きました。
これが今の私の実力ですね。。。
…と思い、解答例を見てみると、

main = getContents >>= putStr $ firstNLines 10


えぇ?何??(>>=)を適用しているところが違うじゃん。
IOモナドを使えって問題だったのね。
難しく考えてしまったようです。
まぁ、でもリストモナドの理解は深まりました♪


一応自分の解をひねり出した時は、こんな感じで考えました。


まず最初のlinesですが、この型は

Prelude> :t lines
lines :: String -> [String]


これを(>>=)を使って右辺に渡したいわけですが、
右辺は右辺で型は

Prelude> :t take 1
take 1 :: [a] -> [a]


今回の文脈ではaはStringになります。
ということはlinesの結果をそのまま(>>=)で右辺に渡すことは出来ません。
(>>=)の型は

class  Monad m  where
    (>>=)  :: m a -> (a -> m b) -> m b


なので、右辺に渡る時にm aがaに、つまり[String]がStringになってしまい、
linesの型と合わなくなります。
なので(安直ですが)returnを使ってモナド化してから(>>=)で右辺に渡しています。
※「モナド化」という言葉は私が勝手に使っています。間違ってるかも知れません(汗)。


これで、まず

return (lines x) >>= take n


までが出来上がりました。
型をチェックしてみるとこんな感じ。

Prelude> :t return (lines "abcd\nefg") >>= take 3
return (lines "abcd\nefg") >>= take 3 :: [String]


最後にこの結果をunlinesに渡したいわけですが、
unlinesの型は

Prelude> :t unlines
unlines :: [String] -> String


あちゃぁ、これもそのまま(>>=)で渡すことが出来ません。
同じようにreturnでモナド化しちゃいます。
最終的にはこんな感じ。

return (return (lines x) >>= take n) >>= unlines


イケテないっちゃイケテませんけどね。
もっと格好良く作れるのかなぁ?

tail.hsを(>>=)を使って書き直してみる

IOモナドの部分を書き換えるのであれば、機械的に出来ちゃいます。
こんな感じ。

{--
main = do cs <- getContents
          putStr $ lastNLines 10 cs
--}
main = do getContents >>= putStr . lastNLines 10


前の定義も載せておきました。

expand.hsを(>>=)を使って書き直してみます

これも機械的に。こんな感じ。

{--
main = do cs <- getContents
          putStr $ expand cs
--}
main = getContents >>= putStr . expand


簡単ですね♪