提取 IO 中的 Maybe 值

发布于 2024-12-21 13:13:06 字数 673 浏览 5 评论 0原文

鉴于以下内容:

> (liftM2 fromMaybe) (ioError $ userError "OOPS") (return $ Just "ok")

ghci 给了我

*** Exception: user error (OOPS)

当然,fromMaybe 工作正常:

> (liftM2 fromMaybe) (return $ "not me") (return $ Just "ok")
"ok"

但似乎 IO 操作正在执行,然后被丢弃:

> (liftM2 fromMaybe) (putStrLn "computing.." >> "discarded") (return $ Just "ok")
computing..
"ok"

为什么会发生这种情况?有没有办法让 IO monad 变得更懒惰?

具体来说,给定 value :: IO (Maybe a) 什么是(干净、简洁)的表达方式

result <- (liftM2 fromMaybe) err value

并让它解压结果或相应地抛出 IOError ?

Given the following:

> (liftM2 fromMaybe) (ioError $ userError "OOPS") (return $ Just "ok")

ghci gives me

*** Exception: user error (OOPS)

Of course, fromMaybe is working correctly:

> (liftM2 fromMaybe) (return $ "not me") (return $ Just "ok")
"ok"

But it seems that the IO operation is being carried out and then discarded:

> (liftM2 fromMaybe) (putStrLn "computing.." >> "discarded") (return $ Just "ok")
computing..
"ok"

Why is this happening? Is there any way to make the IO monad lazier?

Specifically, given value :: IO (Maybe a) what's a (clean, concise) way to say

result <- (liftM2 fromMaybe) err value

and have it unpack result or throw an IOError accordingly?

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

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

发布评论

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

评论(3

冷了相思 2024-12-28 13:13:07

我不知道让 IO 更懒惰是正确的方向。您似乎想做的是首先找到“也许”,然后消除它。这可以用多种方式编写,这是一种选择:

test :: IO (Maybe a) -> IO a
test = (>>= maybe (ioError $ userError "oops") return)

I don't know that making IO lazier is the right direction here. What you seem to want to do is first get at the Maybe, then eliminate it. This can be written several ways, here's one option:

test :: IO (Maybe a) -> IO a
test = (>>= maybe (ioError $ userError "oops") return)
我的黑色迷你裙 2024-12-28 13:13:07

如果您从 liftM2 转换为 do-notation,很明显您的代码失败的原因:

do x <- ioError $ userError "OOPS"
   y <- return $ Just "ok"
   return $ fromMaybe x y

这永远不会超过第一行,因为它无条件抛出异常。

Anthony的建议可以正常工作,但是如果你不关心抛出的具体异常,你也可以使用模式匹配:

do Just result <- value

如果模式不匹配,则会调用 fail,在 IO monad 的情况下会引发异常。

> Just x <- return Nothing
*** Exception: user error (Pattern match failure in do expression at <interactive>:1:0-5)

If you translate from liftM2 to do-notation, it's obvious why your code fails:

do x <- ioError $ userError "OOPS"
   y <- return $ Just "ok"
   return $ fromMaybe x y

This will never go past the first line, as it's unconditionally throwing an exception.

Anthony's suggestion will work fine, but if you don't care about the specific exception thrown, you can also use pattern matching:

do Just result <- value

If the pattern doesn't match, this will call fail, which in the case of the IO monad throws an exception.

> Just x <- return Nothing
*** Exception: user error (Pattern match failure in do expression at <interactive>:1:0-5)
挽梦忆笙歌 2024-12-28 13:13:07

什么是(干净、简洁)的方法...解压结果或相应地抛出 IOError?

我建议您避免依赖抛出错误。相反,明确地处理“错误”:

maybeM :: Monad m => m b -> (a -> m b) -> m (Maybe a) -> m b
maybeM err f value = do
  x <- value
  case x of
    Just y  -> f y
    Nothing -> err

-- This can be written simply as:
maybeM err f value = do
  x <- value
  maybe err f x

-- or even shorter! This is starting to look like Anthony's answer :)
maybeM err f value = value >>= maybe err f

函数的输入和类型应该不言而喻。您可以通过给它一个在 Nothing 情况下执行的操作,或在 Just 情况下对内部值执行的函数来使用它。对于您的特定输入,这看起来像:

maybeM (ioError $ userError "OOPS") return (return $ Just "ok")

因此,如果您绝对必须,那么“解压结果或抛出 IOError 的简洁方法”将是:

-- compare to fromJust, a function to be avoided
fromJustIO :: IO (Maybe a) -> IO a
fromJustIO = maybeM (ioError $ userError "OOPS") return

请注意,其类型签名实际上是 也许-> a,这是 magicMonadUnwrap :: Monad m => 的本质妈-> a,这应该会引发一些危险信号。然而,您可以通过简单的方式使用这种暴行:

result <- fromJustIO value

尽管如此,我再次强烈反对在这里使用异常。尝试以比简单爆炸更优雅的方式处理错误,方法是使用 MaybeM 并提供在失败时执行的 IO 操作。

what's a (clean, concise) way to ... unpack [the] result or throw an IOError accordingly?

I recommend you avoid relying on throwing errors. Instead, handle the "error" explicitly:

maybeM :: Monad m => m b -> (a -> m b) -> m (Maybe a) -> m b
maybeM err f value = do
  x <- value
  case x of
    Just y  -> f y
    Nothing -> err

-- This can be written simply as:
maybeM err f value = do
  x <- value
  maybe err f x

-- or even shorter! This is starting to look like Anthony's answer :)
maybeM err f value = value >>= maybe err f

The function's inputs and types should speak for themselves. You use it by giving it an action to perform for the Nothing case, or a function to perform on the value inside for the Just case. For your particular inputs this would look like:

maybeM (ioError $ userError "OOPS") return (return $ Just "ok")

So if you absolutely must, then the "concise way to unpack the result or throw an IOError" would be:

-- compare to fromJust, a function to be avoided
fromJustIO :: IO (Maybe a) -> IO a
fromJustIO = maybeM (ioError $ userError "OOPS") return

Notice how the type signature for this is practically Maybe a -> a, which is the essence of magicMonadUnwrap :: Monad m => m a -> a, which should set off some red flags. However, you can use this atrocity in a simple manner:

result <- fromJustIO value

Although again, I strongly discourage the use of exceptions here. Try handling errors in a more elegant way than simply exploding, by using maybeM and providing an IO action to execute in the event of failure.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文