这是我上一个问题的后续跟进. IO动作嵌套在未执行的其他monad中
该问题的解决方案是删除一些monad,并允许执行IO操作.
为什么我需要取消monad?有没有办法在没有取消的情况下执行IO?
注意:这是一个假设,而不是关于好的或坏的做法的问题.
hao*_*hao 10
也许这会有助于将其IO视为type IO a = World -> (a, World); 也就是说,一个函数将其唯一参数作为计算机的当前状态,并返回一个新状态和一些值a.这IO与GHC内部的实际实现并没有太大的不同,所以希望我们可以原谅这种类比的(卑鄙)交流方法.
所以readFile :: FilePath -> IO String,例如,成为readFile :: FilePath -> World -> (a, World).
而且main :: IO ()是真的main :: World -> ((), World).
然而,这意味着具有类型的值IO _是惰性的.它们只是功能!在给定值之前,函数不能执行任何操作; 在我们的例子中,函数想要的值是一个World对象,我们无法构造它.这就是Haskell中IO的美妙之处:我们可以IO通过使用我们熟悉和喜欢的monadic运算符(return,bind)来构建一个动作,但是在运行时传入World对象之前它不能做任何事情.
这意味着IO我们构建的任何未通过线程的操作main都不会被执行.
所以,有了foobar :: [Char] -> IO [IO ()],我们当然可以观察到返回值:
main :: IO ()
main = do
ios <- foobar "string"
print "goodbye"
Run Code Online (Sandbox Code Playgroud)
但是,直到我们解构ios并约束IO那些行动所接受的内在价值World:
main :: IO ()
main = do
ios <- foobar
ios !! 0
ios !! 1
ios !! 2
...
print "goodbye"
Run Code Online (Sandbox Code Playgroud)
或者,简而言之,
main = do
ios <- foobar
sequence ios
print "goodbye"
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助.
让我们从一个稍微不同的例子开始。如您所知, aString是以下列表Char:
GHCi> :set +t
GHCi> "Mississippi"
"Mississippi"
it :: [Char]
Run Code Online (Sandbox Code Playgroud)
的列表Strings是 的列表的列表Char;这是一个[[Char]]:
GHCi> group "Mississippi"
["M","i","ss","i","ss","i","pp","i"]
it :: [[Char]]
Run Code Online (Sandbox Code Playgroud)
group "Mississippi"是 a [[Char]],我不希望将其作为 a 处理[Char]- 这会破坏使用的意义group。
IO a对于大多数用途来说,值与任何其他值一样,因此也适用类似的考虑。为了给出一个具体的(和现实的)例子,假设我们有一个这种类型的函数......
(KeyCode -> IO ()) -> IO (IO ())
Run Code Online (Sandbox Code Playgroud)
...它在 GUI 中注册按键事件的事件处理程序。这个想法是,您使用参数调用该函数KeyCode -> IO (),该参数指定响应按键时应该发生的情况,并运行结果,IO (IO ())以便您选择的KeyCode -> IO ()处理程序变为活动状态。然而,该操作生成的内部函数 有不同的用途:它取消注册事件处理程序,并且您可以自行决定在应用程序的稍后位置使用它 - 也许永远不会。在这种情况下,您绝对不想在外部操作之后立即运行内部操作!IO () IO (IO ())
总而言之,一个IO (IO a)动作IO在运行时会产生另一个IO动作,您可能也想运行也可能不想运行。
PS:正如 sheyll在其他问答中提到的,join可用于展平嵌套IO操作或任何其他嵌套单子值。顺便说一句,列表也有一个Monad实例。你认为join (group "Mississippi")会做什么?