Чтение из 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 должен делать то, что вы хотите.