在IO中提取Maybe值

时间:2011-12-16 22:42:39

标签: haskell io monads maybe

鉴于以下内容:

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

3 个答案:

答案 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动作以便在发生故障时执行。