dav*_*gan 22 io haskell exception-handling
在我的Haskell程序中,我想读取用户使用该getLine函数给出的值.然后我想使用该read函数将此值从字符串转换为适当的Haskell类型.如何捕获read函数抛出的解析错误并要求用户重新输入值?
我是否正确地认为这不是"IO错误",因为它不是由IO系统无法正常运行引起的错误?这是一个语义错误,所以我不能使用IO错误处理机制?
bar*_*oap 28
你不想.你想要使用read,可能是这样的:
maybeRead = fmap fst . listToMaybe . reads
Run Code Online (Sandbox Code Playgroud)
(虽然如果元组的第二个元素不是"",你可能想要错误,也就是说,如果剩下的字符串也是如此)
的原因,为什么要使用读取,而不是捕捉error例外的是,在纯代码的例外是邪恶的,因为它是非常容易的,试图抓住他们在错误的地方:请注意,当他们被迫,而不是之前他们只能飞.找到那里可能是一个非平凡的练习.这是(原因之一)为什么Haskell程序员喜欢保持他们的代码总数,即终止和无异常.
您可能想要查看正确的解析框架(例如parsec)和haskeline.
这是对@ barsoap的答案的补充.
Haskell异常可能会抛出任何地方,包括纯代码,但它们只能从IO monad中捕获.为了捕获纯代码抛出的异常,您需要在IO语句中使用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
Run Code Online (Sandbox Code Playgroud)
你应该使用更具体的东西,SomeException因为它会捕获任何东西.在上面的代码中,try将返回一个Left exceptionif read无法解析字符串,但Left exception如果在尝试打印该值时出现IO错误,或者可能出错的任何其他数量的内容(内存不足),它也将返回a 等).
现在,这就是纯代码中的异常是邪恶的原因.如果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
Run Code Online (Sandbox Code Playgroud)
如果你运行它,你会发现Right无论用户输入什么,你总是会在case语句的分支中结束.这是因为传递给的IO动作try不会尝试read输入的字符串.它打印列表的第一个值data,它是常量,永远不会触及列表的尾部.因此,在case语句的第一个分支中,编码器认为数据已经过评估但不是,并且read可能仍会抛出异常.
read用于反序列化数据,而不是解析用户输入的输入.使用reads,或切换到真正的解析器组合库.我喜欢uu-parsinglib,但parsec,polyparse和许多其他人也很好.不管怎样,你很可能不久需要额外的电力.