LoginSignup
13
10

More than 5 years have passed since last update.

Haskellでmain(do)内での"<-"と"let"の違い

Last updated at Posted at 2016-08-14

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に束縛せよ」

その後の説明を読み進めると、つまり、getLineString型ではなくIO String型で、「<- getLine」は、「IOから文字列取り出して、左辺の変数に束縛する」という意味らしい。

じゃぁ、readはというと、これはString -> aIO 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

13
10
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
10