15

鉴于以下情况:

> (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 ?

4

3 回答 3

12

我不知道让IO懒惰是正确的方向。您似乎想要做的是首先获得Maybe,然后消除它。这可以写成几种方式,这里有一个选项:

test :: IO (Maybe a) -> IO a
test = (>>= maybe (ioError $ userError "oops") return)
于 2011-12-16T23:02:40.090 回答
10

如果您将 from 转换liftM2为 do-notation,那么您的代码失败的原因就很明显了:

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

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

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

do Just result <- value

如果模式不匹配,这将调用fail,在IOmonad 的情况下会抛出异常。

> Just x <- return Nothing
*** Exception: user error (Pattern match failure in do expression at <interactive>:1:0-5)
于 2011-12-16T23:37:53.110 回答
5

什么是(干净,简洁)的方式来...解压缩 [the] 结果或相应地抛出 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

注意这个的类型签名实际上Maybe a -> a是 的,这是 的本质magicMonadUnwrap :: Monad m => m a -> a,应该引起一些危险信号。但是,您可以以简单的方式使用这种暴行:

result <- fromJustIO value

尽管如此,我还是强烈反对在这里使用异常。尝试以一种比简单爆炸更优雅的方式处理错误,方法是使用maybeM并提供一个 IO 操作以在发生故障时执行。

于 2011-12-20T07:23:19.330 回答