3

在 Haskell 中,类型构造函数IO是一个带有语句的 monad,return它可以将任何表达式提升到它的IO版本。

没有什么能阻止我们将已经是IO动作的东西提升到它的IO版本——给我们一种形式IO (IO a)

所以我可以例如编写以下程序:

main = return . print $ "Hello world"

在执行时什么都不做。

我的问题是,当这个 main 执行时,引擎盖下会发生什么?

return是否存在对IO 操作有意义的情况?

4

2 回答 2

3

在后台,运行时有效地丢弃了action的结果,这就是为什么它通常被定义为. 这意味着如果实际上有一个类似的类型,那么就没有真正的问题。该动作被执行,结果(另一个动作)被丢弃,未执行。IOmainIO ()mainIO (IO Int)IOIO

在程序的更深处,您更有可能只是触发类型错误。例如,fmap doSomething (return . getLine)如果您的意思是fmap doSomething getLine.

于 2018-01-10T13:20:58.983 回答
2

IO通常近似于State RealWorld,即State在“真实世界”上运行的 monad。returnforState产生一个不改变包含状态的动作。因此,通过类比,returning 任何东西IO都不会做任何事情。不仅当你return一些IO a,而且任何a

返回一个IO动作可用于在一个地方建立一个计算(沿途将一些值捕获到一个闭包中)并在其他地方执行它。就像在 C 中传递回调一样。

实际上,要建立IO计算并将其传递到其他地方,您可以使用letdo-block 的内部:

main :: IO ()
main = do
  let act = readFile "somefile" -- action is not performed yet
  foo act -- pass the action as a parameter

foo :: IO () -> IO ()
foo act = do
  .. do something
  result <- act -- actually perform the action
  ..

return在这种情况下,您不需要IO a值。

但是,如果构建该计算本身的过程需要您执行IO操作,那么您就需要这样的东西。在我们的示例中,让我们询问要打开文件名的用户:

main :: IO ()
main = do
  act <- do
    filename <- getLine
    return (readFile filename)
  foo act

在这里,我们需要一个filename来创建我们的动作,但getLine也是IO。这就是额外级别的IO出现。当然,这个例子是合成的,你可以filename <- getLinemain.

于 2018-01-10T11:54:21.053 回答