鉴于以下内容:
> (liftM2 fromMaybe) (ioError $ userError "OOPS") (return $ Just "ok")
ghci给了我
*** Exception: user error (OOPS)
当然,来自Maybe的工作正常:
> (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?
答案 0 :(得分:12)
我不知道让IO
懒惰是正确的方向。你似乎想要做的是先到Maybe
,然后消除它。这可以用几种方式写出来,这里有一个选项:
test :: IO (Maybe a) -> IO a
test = (>>= maybe (ioError $ userError "oops") return)
答案 1 :(得分:10)
如果您从liftM2
翻译为记号,那么您的代码失败就很明显了:
do x <- ioError $ userError "OOPS"
y <- return $ Just "ok"
return $ fromMaybe x y
这永远不会越过第一行,因为它无条件地抛出异常。
Anthony's suggestion可以正常工作,但如果你不关心抛出的特定异常,你也可以使用模式匹配:
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)
答案 2 :(得分:5)
什么是(干净,简洁)的方式来解压[结果]或相应地抛出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动作以便在发生故障时执行。