Это дополнение к ответу @ barsoap больше, чем что-либо еще.
Исключения в Haskell могут создаваться где угодно, в том числе в чистом коде, но они могут быть перехвачены только из монады ввода-вывода.Чтобы перехватывать исключения, генерируемые чистым кодом, вам нужно использовать catch
или try
в операторе ввода-вывода, который заставит чистый код быть оцененным.
str2Int :: String -> Int -- shortcut so I don't need to add type annotations everywhere
str2Int = read
main = do
print (str2Int "3") -- ok
-- print (str2Int "a") -- raises exception
eVal <- try (print (str2Int "a")) :: IO (Either SomeException ())
case eVal of
Left e -> do -- couldn't parse input, try again
Right n -> do -- could parse the number, go ahead
Вам следует использовать что-то большееконкретнее, чем SomeException
, потому что это поймает что-нибудь.В приведенном выше коде try
вернет Left exception
, если read
не сможет проанализировать строку, но также вернет Left exception
, если при попытке напечатать значение, произошла ошибка ввода-вывода, или любое числодругих вещей, которые могли бы пойти не так (из памяти и т. д.).
Теперь вот почему исключения из чистого кода являются злом.Что, если код IO на самом деле не заставляет результат оцениваться?
main2 = do
inputStr <- getLine
let data = [0,1,read inputStr] :: [Int]
eVal <- try (print (head data)) :: IO (Either SomeException ())
case eVal of
Right () -> do -- No exception thrown, so the user entered a number ?!
Left e -> do -- got an exception, probably couldn't read user input
Если вы запустите это, вы обнаружите, что вы всегда окажетесь в ветви Right
оператора case,независимо от того, что пользователь вошел.Это связано с тем, что действие ввода-вывода, переданное try
, никогда не пытается read
введенной строки.Он печатает первое значение списка data
, которое является постоянным, и никогда не касается конца списка.Таким образом, в первой ветви оператора case кодировщик думает, что данные оцениваются, но это не так, и read
может по-прежнему выдавать исключение.
read
предназначено для десериализации данных, а не для анализавведенный пользователем ввод.Используйте reads
или переключитесь на настоящую библиотеку синтаксического анализатора.Мне нравится uu-parsinglib , но parsec , polyparse , и многие другие тоже хороши.В любом случае, очень скоро вам понадобится дополнительная мощность.