モナド
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
簡単ですね♪