IO 通常近似于 State RealWorld ,即 State monad,它在"real world"上运行 . return for State 生成一个不会改变包含状态的动作 . 因此,通过类比, return 对 IO 的任何内容都没有做任何事情 . 不仅当你 return 某些 IO a ,而且 a .
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
..
在这种情况下,您不需要 returnIO a 值 .
但是,如果构建计算本身的过程需要您执行 IO 动作,那么's where you'd需要这样的事情 . 在我们的示例中,让我们要求用户打开文件名:
main :: IO ()
main = do
act <- do
filename <- getLine
return (readFile filename)
foo act
2 回答
IO
通常近似于State RealWorld
,即State
monad,它在"real world"上运行 .return
forState
生成一个不会改变包含状态的动作 . 因此,通过类比,return
对IO
的任何内容都没有做任何事情 . 不仅当你return
某些IO a
,而且a
.返回
IO
动作可用于在一个地方构建计算(沿途捕获一些值到闭包中)并在其他地方执行 . 就像在C中传递回调一样实际上,要构建
IO
计算并将其传递到其他地方,您可以在do-block中使用let
:在这种情况下,您不需要
return
IO a
值 .但是,如果构建计算本身的过程需要您执行
IO
动作,那么's where you'd需要这样的事情 . 在我们的示例中,让我们要求用户打开文件名:在这里,我们需要一个
filename
来创建我们的动作,但getLine
也在IO
中 . 这就是IO
的额外水平 . 当然,这个例子是合成的,因为你可以在main
中执行filename <- getLine
.在引擎盖下,运行时有效地丢弃了
IO
actionmain
的结果,这就是为什么它通常被定义为IO ()
. 这意味着如果main
实际上具有类似IO (IO Int)
的类型,则没有实际问题 . 执行IO
操作,结果(另一个IO
操作)被丢弃,未执行 .在程序中,您更有可能触发类型错误 . 例如,
fmap doSomething (return . getLine)
将不会进行类型检查,如果您的意思是fmap doSomething getLine
.