Haskellで引っかかってしまったmain内(do内)での"<-"の自分の勘違いにつていのメモ。
<-
は、てっきり束縛
であるlet
の代わりだと勘違いしていた。
例えば、標準入力から数値を入力して、1から入力した数値までの足し合わせる簡単な関数に数値の引数を渡したいとする。
標準入力からの値はStringなので、Intに変換したい。
そこで下記のようなコードを書いてエラーに。
reduce' i = foldr (+) 0 [1..i]
main = do
s <- getLine
i <- read s :: Int
putStrLn $ show $ reduce' i
エラーは下記の通り。i <- read s :: Int
が悪いらしい。
Couldn't match expected type IO Integer with actual type Int
In a stmt of a 'do' block: i <- read s :: Int
(以下省略)
mainのIOモナドでs <- getLine
としているから、これは束縛
だ…と勝手に思い込んでいた。
私が買ったHaskellの本には<-
が詳しく書いてなく、索引にもない…。そこで、もう一つ買っておいた参考書「すごいH」な本を読むと次のように書いてある。
「I/OアクションgetLineを実行して、それからその結果の値をsに束縛せよ」
その後の説明を読み進めると、つまり、getLine
はString
型ではなくIO String
型で、「<- getLine
」は、「IOから文字列取り出して、左辺の変数に束縛する」という意味らしい。
じゃぁ、read
はというと、これはString -> a
でIO String
ではない。これはただの関数なので<-
を使っても束縛できるわけではない。普通に、束縛はlet
を使う。
なので、正しいコードは次の通り。
reduce' i = foldr (+) 0 [1..i]
main = do
s <- getLine
let i = read s :: Int
putStrLn $ show $ reduce' i
では、getLine
で<-
を使わずlet
を使ったらどうなるかというとエラー。
なぜかというと、let s = getLine
では、getLine
に別名を付けているというコードになってしまうらしい。
実際にGHCiで試してみる。
Prelude> let s = getLine
Prelude> s --("s"は"getLine"の別名なので、"getLine"が実行される)
10 --(入力待ちになるので"10"を入力してEnter)
"10" --(入力結果が表示される)
あぁ、こういうことだったのね…と。盛大に勘違いしてたので、メモメモ。
「すごいH」な本は、やっぱりすごいw