返回IO操作会产生什么后果?

时间:2018-01-10 11:16:13

标签: haskell io monads io-monad

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

没有什么可以阻止我们将已经IO的{​​{1}}动作提升到IO版本 - 为我们提供IO (IO a)形式的类型。

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

main = return . print $ "Hello world"

执行时什么都没做。

我的问题是,执行此主程序时会发生什么?

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

2 个答案:

答案 0 :(得分:3)

在幕后,运行时有效地丢弃IO动作main结果,这就是为什么它通常被定义为IO ()。这意味着如果main实际上具有类似IO (IO Int)的类型,则没有真正的问题。执行IO操作,结果(另一个IO操作)被丢弃,未执行。

在您的程序中,您更有可能只是触发类型错误。例如,如果您的意思是fmap doSomething (return . getLine),则fmap doSomething getLine不会进行类型检查。

答案 1 :(得分:2)

IO通常近似于State RealWorld,即State monad,可在"现实世界"上运行。 return State会产生一个不会改变包含状态的动作。因此,通过类比,return任何事物IO都不会做任何事情。不仅仅是return某些IO a,而是a

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

实际上,要构建IO计算并将其传递到其他位置,您可以在do-block中使用let

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 {。}}。