LYH 8.3 I/O アクションどうしをまとめる
8.3 I/O アクションどうしをまとめる
main = do putStrLn "Hello, what's your name?" name <- getLine putStrLn $ "Zis is your future: " ++ tellFortune name
8.1と説明が重複するところがあるが、もう一度書く。
上のコードでは、getLineが副作用を扱っているものの、Stringを束縛したnameが関わるいかなる関数においてもI/Oについての情報を知る必要はない。上のtellFortuneという関数はString -> String
という型シグネチャを持っているものとする。確かにI/Oの影響が及ぶ箇所はない。副作用が発生する部分は、getLine
の中だけに留まっている。このIOアクションによって生成された結果が<-
によって取り出されてname
に束縛されてからは、副作用のない世界でname
が処理されるようになる。
IOアクションは、実行されるまで不活性なままである。Haskellではmainという、ただ一つのIOアクションが実行される。mainが1つのIOアクションのみで定義されるなら、
main = some IO action
となるし、doブロックでまとめれば
main = do (IO action 1) (IO action 2) ... do (IO action x) (IO action y) ...
と記述することも出来る。
また、main以外でも、I/Oアクションが実行される場合がある。GHCiにI/Oアクションを入力してENTERキーを押した時である。
ghci> putStrLn "HEEY" HEEY
以下のようなコードは書けるだろうか。
nameTag = "Hello, my name is " ++ getLine
型を考えてみよう。"Hello, my name is "の型は、String
すなわち [Char]
であるが、getLine
の型はIO String
である。
Char
のリストとIO String
とでは型が違うので、当然、リストの連結を行うことは出来ない。よって、このコードは間違いである。
I/Oアクションはどれも結果を生成する。
main = do foo <- putStrLn "Hello" name <- getLine putStrLn ("Hey " ++ name ++ "!")
上のコードは間違いではないが、putStrLn "Hello"
の型はIO ()
であるため、fooに束縛した値は()
であり、意味がない。
また、doブロックの最後の行は束縛を書くことは出来ない。この理由は「13章モナドがいっぱい」で分かるらしい。
以下の文は、getLineをmyGetLineに変えただけで、決してgetLineからStringを取り出してmyGetLineに束縛したものではない。
let myGetLine = getLine
無意味なコードだが、上の文を書いたあとには以下のような記述が可能となる。
name <- myGetLine
8.3 確認問題
- 複数のI/Oアクションを糊付けして1つにするには何を用いればよいか。
- "Hello, what's your name?"と一行に出力し、続いて文字列として名前を読み取らせ、最後に、"Hey (名前), you rock!"と一行に出力せよ。
- 2においてdo構文でまとめられたI/Oアクションの型は何か。
- 3の型になる理由を答えよ。
- mainの型シグネチャはどんな形をしているか答えよ。
- getLineの型を答えよ。
- putStrLnの型を答えよ。
- doブロック内で、getLineをmyGetLineという名前で使えるようにするにはどうすればよいか。
8.3 解答
- do構文
main = do putStrLn "Hello, what's your name?" name <- getLine putStrLn ("Hey " ++ name ++ ", you rock!")
IO ()
- do構文の中の最後のIOアクションであるputStrLnの戻り値の型がIO ()であるため。
main :: IO 具体型
getLine :: IO String
getLineはStringを生成するI/OアクションputStrLn :: String -> IO ()
let myGetLine = getLine