Чтение из стандартного ввода в Haskell с использованием IO.readLn - PullRequest
13 голосов
/ 18 октября 2011

Этот код не компилируется в 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)
$

Я только что набрал "привет!" а затем введите. Обратите внимание, что я использую x86_64 GHC на OS X, что считается нестабильным.

EDIT2 : я изменил readLn на getLine, и он волшебным образом работает без причины. Я хотел бы знать почему, но я счастлив, что это работает.

Финальный код:

import System.IO

main = do
    z <- getLine
    print z

Ответы [ 3 ]

27 голосов
/ 18 октября 2011

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 не может понять, какой тип readLn должен возвращать.print может принимать любой отображаемый тип, поэтому он не ограничивается тем, какой тип будет возвращен.Это делает тип возвращаемого значения readLn неоднозначным, поскольку haskell не может определить тип.Это то, что говорит сообщение об ошибке.


То, что вы, вероятно, в чем-то сохраняете, только строку, вводимую пользователем, а не считывание ее в свой собственный тип.Вы можете сделать это с помощью getLine, которая имеет тип getLine :: IO String.Точно так же вы можете использовать putStrLn вместо print, чтобы просто напечатать строку.putStrLn имеет тип String -> IO ().

16 голосов
/ 18 октября 2011

Это то, на что вы изменили свой код, верно?

import System.IO

main = do
    z <- readLn
    putStrLn z

putStrLn записывает String в стандартный вывод, поэтому z является String. Поэтому readLn будет читать String из стандартного ввода.

НО ... readLn ожидает чтения значения в формате Haskell из стандартного ввода. т.е. вместо того, чтобы ожидать, что вы наберете что-то вроде This is a string, он ожидает это в кавычках: "This is a string".

Чтобы исправить, замените readLn на getLine, который читает буквальный текст, а не строку в формате Haskell.

import System.IO

main = do
    z <- getLine
    putStrLn z
10 голосов
/ 18 октября 2011

readLn считывает заданный вами тип и поэтому не может использоваться таким образом: его необходимо использовать в функции, которая указывает его тип.С другой стороны, getLine всегда возвращает String, поэтому он делает то, что вам нужно.

Стоит отметить, что вы можете использовать putStrLn вместо print;print добавит кавычки.

Таким образом, do { z <- getLine; putStrLn z; } в GHCi должен делать то, что вы хотите.

...