如何从Haskell中的读取功能中捕获没有解析的例外?

发布于 2025-01-24 19:05:32 字数 215 浏览 2 评论 0原文

在我的Haskell程序中,我想使用getline函数来阅读用户给出的值。然后,我想使用读取函数将此值从字符串转换为适当的Haskell类型。如何通过读取函数引发的解析错误并要求用户重新输入该值?

我认为这不是“ IO错误”是正确的,因为这不是IO系统无法正常运行的错误?这是语义错误,所以我无法使用IO错误处理机制?

In my Haskell program, I want to read in a value given by the user using the getLine function. I then want to use the read function to convert this value from a string to the appropriate Haskell type. How can I catch parse errors thrown by the read function and ask the user to reenter the value?

Am I right in thinking that this is not an "IO Error" because it is not an error caused by the IO system not functioning correctly? It is a semantic error, so I can't use IO error handling mechanisms?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

醉南桥 2025-01-31 19:05:32

你不想。您要使用

maybeRead = fmap fst . listToMaybe . reads

相反

: em>原因为什么要使用读取而不是捕获错误例外是纯代码中的异常是邪恶的,因为它是非常易于尝试捕获它们在错误的位置:请注意,它们只有在被迫而不是之前飞行。定位在哪里可以是非平凡的练习。这就是Haskell程序员喜欢保持其代码总数的原因(原因之一),即终止和例外。

您可能需要查看适当的解析框架(例如 parsec )和 haskeline 也是如此。

You don't want to. You want to use reads instead, possibly like that:

maybeRead = fmap fst . listToMaybe . reads

(though you might want to error out if the second element of the tuple is not "", that is, if there's a remaining string, too)

The reason why you want to use reads instead of catching error exceptions is that exceptions in pure code are evil, because it's very easy to attempt to catch them in the wrong place: Note that they only fly when they are forced, not before. Locating where that is can be a non-trivial exercise. That's (one of the reasons) why Haskell programmers like to keep their code total, that is, terminating and exception-free.

You might want to have a look at a proper parsing framework (e.g. parsec) and haskeline, too.

信仰 2025-01-31 19:05:32

ReadMaybeReadeither满足您的期望。您可以在text.read.read软件包中找到此功能。

There are readMaybe and readEither that satisfy your expectation. You find this functions in Text.Read package.

眼趣 2025-01-31 19:05:32

这是 @barsoap的答案的附录,而不是其他任何东西。

Haskell例外可能会在任何地方抛出,包括纯代码,但可能只会从IO Monad中捕获。为了捕获纯代码抛出的异常,您需要在io语句上使用catch 尝试,该语句将迫使要评估纯代码。

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

您应该使用比someSception更具体的东西,因为那会抓住任何东西。在上面的代码中,尝试将返回few excepre如果read> read无法解析字符串,但它也将返回左例异常如果试图打印值时有IO错误,或其他可能出错的其他因素(内存等)。

现在,这就是为什么纯代码的例外是邪恶的原因。如果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

如果运行此操作,您会发现您总是最终进入案例语句的分支,无论用户输入了什么。这是因为IO操作传递给尝试从未尝试读取输入的字符串。它打印列表的第一个值data,它是恒定的,从不触摸列表的尾巴。因此,在案例语句的第一个分支中,编码器认为数据已评估,但没有评估,并且读取仍然可能会引发异常。

读取是为了不进行简报的数据,而不是解析用户输入的输入。使用读取,或切换到真实的解析器组合库。我喜欢 uu-parsinglib ,但是,还有许多其他人也很好。无论如何,您很可能需要额外的力量。

This is an addendum to @barsoap's answer more than anything else.

Haskell exceptions may be thrown anywhere, including in pure code, but they may only be caught from within the IO monad. In order to catch exceptions thrown by pure code, you need to use a catch or try on the IO statement that would force the pure code to be evaluated.

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

You should use something more specific than SomeException because that will catch anything. In the above code, the try will return a Left exception if read can't parse the string, but it will also return a Left exception if there's an IO error when trying to print the value, or any number of other things that could possibly go wrong (out of memory, etc.).

Now, here's why exceptions from pure code are evil. What if the IO code doesn't actually force the result to be evaluated?

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

If you run this, you'll find that you always end up in the Right branch of the case statement, no matter what the user entered. This is because the IO action passed to try doesn't ever try to read the entered string. It prints the first value of the list data, which is constant, and never touches the tail of the list. So in the first branch of the case statement, the coder thinks the data is evaluated but it isn't, and read may still throw an exception.

read is meant for unserializing data, not parsing user-entered input. Use reads, or switch to a real parser combinator library. I like uu-parsinglib, but parsec, polyparse, and many others are good too. You'll very likely need the extra power before long anyway.

許願樹丅啲祈禱 2025-01-31 19:05:32

这是一个改进的Mayberead,仅允许尾随的空间,但没有其他:

import Data.Maybe
import Data.Char

maybeRead2 :: Read a => String -> Maybe a
maybeRead2 = fmap fst . listToMaybe . filter (null . dropWhile isSpace . snd) . reads

Here's an improved maybeRead which allows only for trailing whitespaces, but nothing else:

import Data.Maybe
import Data.Char

maybeRead2 :: Read a => String -> Maybe a
maybeRead2 = fmap fst . listToMaybe . filter (null . dropWhile isSpace . snd) . reads
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文