使用IO.readLn从Haskell中的stdin读取

时间:2011-10-18 01:11:05

标签: haskell io ghc

此代码无法在GHC 7.0.3中编译:

import System.IO

main = do
    z <- readLn
    print z

我的目的是从stdin读取一行并将其存储在z中,以便稍后使用它来执行更高级的操作。错误消息如下所示:

test.hs:5:9:
    Ambiguous type variable `a0' in the constraints:
      (Show a0) arising from a use of `print' at test.hs:5:9-13
      (Read a0) arising from a use of `readLn' at test.hs:4:14-19
    Probable fix: add a type signature that fixes these type variable(s)
    In a stmt of a 'do' expression: print z
    In the expression:
      do { z <- readLn;
           print z;
           return () }
    In an equation for `main':
        main
          = do { z <- readLn;
                 print z;
                 return () }

显然,我还有一些基本的东西尚未被理解;请向我解释为什么它不起作用以及如何解决它。

EDIT1 :我通过将print z更改为putStrLn z来修复编译错误,因此GHC理解我想要读取字符串。但是当我运行程序时,我得到一个我无法理解的运行时错误:

$ ./test
hello!
test: user error (Prelude.readIO: no parse)
$

我刚输入“你好!”然后输入。请注意,我在OS X上运行x86_64 GHC,这被认为是不稳定的。

EDIT2 :我将readLn更改为getLine,它无缘无故地神奇地工作。我想知道为什么,但我很高兴它有效。

最终代码:

import System.IO

main = do
    z <- getLine
    print z

3 个答案:

答案 0 :(得分:27)

readLn作为类型:Read a => IO a。它从用户读取一行,然后将该字符串解析为类型a。什么是a类型?这是你想要的任何东西(只要它是Read的一个实例)。例如:

readAInt :: IO Int
readAInt = readLn

readABool :: IO Bool
readABool = readLn

print的类型为Show a => a -> IO ()。它采用的类型是Show的实例,并将其打印出来。例如,要打印True,您可以使用print True。要打印Int 42,您可以使用print 42


在您的示例中,您一起使用print和readLn。这不起作用,因为haskell无法确定应返回的类型readLnprint可以采用任何可显示的类型,因此它不会限制返回哪种类型。这使得readLn的返回类型不明确,因为haskell无法弄清楚类型。这是错误消息所说的内容。


您可能只需要存储用户输入的字符串,而不是将其读入您自己的类型。您可以使用类型为getLine :: IO String的getLine执行此操作。类似地,您可以使用putStrLn而不是print来打印字符串。 putStrLn的类型为String -> IO ()

答案 1 :(得分:16)

这是您将代码更改为的内容,对吧?

import System.IO

main = do
    z <- readLn
    putStrLn z

putStrLnString写入stdout,因此zString。因此readLn将从stdin中读取String

但是...... readLn期望从stdin读取Haskell格式的值。即,不要期望你键入类似This is a string的东西,而是用引号来预测它:"This is a string"

要修复,请将readLn替换为getLine,其中读取文字文本,而不是Haskell格式的字符串。

import System.IO

main = do
    z <- getLine
    putStrLn z

答案 2 :(得分:10)

readLn读回您指定的类型,因此不能以这种方式使用:它需要在指定其类型的函数中使用。另一方面,getLine总是返回一个String,所以它可以做你想要的。

值得注意的是,您可能还想使用putStrLn而不是print; print会添加引号。

因此,GHCi中的do { z <- getLine; putStrLn z; }应该做你想做的事。