如何从 Maybe 中提取 IO () 并执行它?

How can I extract an IO () from Maybe and execute this?

我有这个代码:

trick = Just (putStrLn "Hello?")

我想将这个 IO () 解包到 Maybe 上下文中并调用它。

main = do foo <- trick
          foo

但是,这会引发错误:

Couldn't match type ‘IO’ with ‘Maybe’
Expected type: Maybe ()
  Actual type: IO ()

我该如何解决这个问题?

开始时最简单的解决方案可能是认识到您可以通过模式匹配根据 Maybe (IO ()) 的值来决定要做什么。

maybeDoIO :: Maybe (IO ()) -> IO ()
maybeDoIO (Just io) = io
maybeDoIO Nothing   = return ()

你必须考虑 Nothing,基本上:

main = maybe (putStrLn "Nothing") id foo

您要找的函数是Data.Foldable.sequence_:

>>> Data.Foldable.sequence_ (Just (putStrLn "Hello?"))
Hello?
>>>

如果你的 MaybeNothing,它不会做任何事情:

>>> Data.Foldable.sequence_ Nothing
>>>

之所以有效,是因为 Data.Foldable.sequence_ 的类型是:

Data.Foldable.sequence_
    :: (Foldable t, Monad m) => t (m a) -> m ()

... 如果您将 t 专门化为 Maybe 并将 m 专门化为 IO,您将得到:

Data.Foldable.sequence_ :: Maybe (IO a) -> IO ()

Maybe的具体上下文中,相当于:

sequence_ (Just io) = do
    _ <- io
    return ()
sequence   Nothing  = return ()

你的 main 函数的问题是你在同一个 do 块中混合了两个不同的单子。

foo <- trick 动作对 Maybe monad 来说是 "relative",但之后的 foo 动作是 IO 动作。 >>= 的类型是:

Monad m => m a -> (a -> m b) -> m b

但在您的情况下,您需要以下类型的内容:

Maybe a -> (a -> IO b) -> IO b

有两个不同的单子。

如果要执行 IO 操作,那么 main 的类型必须 IO a 类型,因此 do 符号必须引用 IO 而不是 Maybe。这意味着您 不能 使用 <- 来提取操作,但您必须使用其他东西。例如 Data.Maybe.fromMaybe:

import Data.Maybe

main = do fromMaybe (return ()) trick