IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

コンパイル時に型推論が効かない例

下記のように対話的なコードを書いてみました。コンパイルエラーとなりました。

[work06]$ cat test07.hs
import System.IO

main :: IO ()
main = do putStrLn "Waht is your name?"
      a <- readLn
      putStrLn "How old are you?"
      b <- readLn
      return ()


[work06]$ runghc test07.hs

test07.hs:5:16:
    No instance for (Read t0) arising from a use of `readLn'
    The type variable `t0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Read () -- Defined in `GHC.Read'
      instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
      instance (Read a, Read b, Read c) => Read (a, b, c)
        -- Defined in `GHC.Read'
      ...plus 25 others
    In a stmt of a 'do' block: a <- readLn
    In the expression:
      do { putStrLn "Waht is your name?";
           a <- readLn;
           putStrLn "How old are you?";
           b <- readLn;
           .... }
    In an equation for `main':
        main
          = do { putStrLn "Waht is your name?";
                 a <- readLn;
                 putStrLn "How old are you?";
                 .... }

test07.hs:7:16:
    No instance for (Read t1) arising from a use of `readLn'
    The type variable `t1' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Read () -- Defined in `GHC.Read'
      instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
      instance (Read a, Read b, Read c) => Read (a, b, c)
        -- Defined in `GHC.Read'
      ...plus 25 others
    In a stmt of a 'do' block: b <- readLn
    In the expression:
      do { putStrLn "Waht is your name?";
           a <- readLn;
           putStrLn "How old are you?";
           b <- readLn;
           .... }
    In an equation for `main':
        main
          = do { putStrLn "Waht is your name?";
                 a <- readLn;
                 putStrLn "How old are you?";
                 .... }

ambiguousと出てきたので、明示的に型を指定する必要があります。 readLn :: Read a => IO aなので、IO Stringを追加しました。これを試したところ、コンパイルは通るが、実行時エラーとなってしまいました。

[work06]$ cat test08.hs
import System.IO

main :: IO ()
main = do putStrLn "Waht is your name?"
      a <- readLn :: IO String 
      putStrLn "How old are you?"
      b <- readLn :: IO String
      return ()


[work06]$ runghc test08.hs
Waht is your name?
foo bar
test08.hs: user error (Prelude.readIO: no parse)

エラーメッセージを調べてみると、次の情報がありました。 http://stackoverflow.com/questions/7801407/read-from-stdin-in-haskell-using-io-readln 型があってないとのことのようです。

原因が分からず、しばし悩みました。基本に立ち返り、 readLn :: Read a => IO a なのだから、 Read String => IO String となるはずです。 Readのインスタンスをみたところ、 http://hackage.haskell.org/package/base-4.7.0.0/docs/Prelude.html#t:Read Read Charはあっても、Read Stringはありませんでした。

ちょっと疑問に思ったのは、なんでコンパイル時にエラーにならないのかという点です。推測になりますが、Read aのaは実行時にならないと判明しないからなのかもしれません。

getLineの型はIO Stringなので、期待した通りに動きました。

[work06]$ cat test09.hs
import System.IO

main :: IO ()
main = do putStrLn "Waht is your name?"
      a <- getLine
      putStrLn "How old are you?"
      b <- getLine
      return ()


[work06]$ runghc test09.hs
Waht is your name?
foo bar
How old are you?
30