Чтение из stdin в Haskell с использованием IO.readLn

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

Ответ 1

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 ().

Ответ 2

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

import System.IO

main = do
    z <- readLn
    putStrLn z

putStrLn записывает a String в stdout, поэтому z является String. Поэтому readLn будет читать a String из stdin.

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

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

import System.IO

main = do
    z <- getLine
    putStrLn z

Ответ 3

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

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

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