[英]How can I perpetually retry an IO until I get a result?
我正在尝试从命令行提示符读取 integer 并且我希望我的程序一直要求输入,直到它可以解析正确的值。
这就是我想出的
import Control.Exception
import System.IO
prompt :: String -> IO String
prompt text = do
putStr text
hFlush stdout
getLine
getInt :: IO Int
getInt = handle recoverError readParse
where recoverError :: SomeException -> IO Int
recoverError _ = getInt
readParse = fmap read $ prompt ">> "
main :: IO ()
main = fmap show getInt >>= putStrLn
我希望handle
function 在出现read
Exception
时递归调用getInt
,但显然情况并非如此。
这是我执行这个程序时看到的
$ ./main
>> 10
10
$ ./main
>> not a number
main: Prelude.read: no parse
我是 haskell 的新手,所以可能在这里遗漏了一些明显的东西。
任何帮助表示赞赏。
问题在于,由于 Haskell 的懒惰, readParse
不会出现read
异常。 直到 putStrLn show
n 字符串为putStrLn
时才会评估read
结果,然后才会发生异常。
您可能可以通过使用来解决此问题evaluate
,但这里的正确解决方案是根本不依赖异常:
如果解析不成功,
read
失败并出现error
,因此不鼓励在实际应用程序中使用它。 使用readMaybe
或readEither
以获得安全的替代方案。
getInt :: IO Int
getInt = maybe getInt return =<< readMaybe <$> prompt ">> "
-- alternatively written as
getInt = do
input <- prompt ">> "
case readMaybe input of
Just v -> return v
Nothing -> getInt
使用readIO
而不是read
,如:
readParse = prompt ">> " >>= readIO
不同之处在于fmap read
创建了一个 IO 操作,该操作始终成功并产生带有嵌入异常的纯结果,而readIO
创建一个 IO 操作,该操作可能会引发 IO 异常,但其纯值始终成功您也可以将getLine
和readIO
结合使用,改为使用readLn
,如下所示:
prompt :: Read a => String -> IO a
prompt text = do
...
readLn
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.