为什么嵌套在其他 monads 中的 IO 不会执行?有没有办法强迫他们这样做



这是我最后一个问题的后续。 嵌套在其他未执行的单元中的 IO 操作

该问题的解决方案是删除一些monads,这允许IO操作执行。

为什么我需要解开单子的巢穴?有没有办法在不取消嵌套的情况下执行 IO?

注意:这是一个假设,而不是一个关于好或坏做法的问题。

也许将IO视为type IO a = World -> (a, World)会有所帮助; 也就是说,一个函数将计算机的当前状态作为其唯一参数,并返回新状态以及一些值a。这与 GHC 内部中IO的实际实现没有太大区别,所以希望我们可以原谅这里通过类比进行通信的(卑鄙)方法。

所以readFile :: FilePath -> IO String,例如,变得readFile :: FilePath -> World -> (a, World).

main :: IO ()真的很main :: World -> ((), World).

但是,这意味着类型为IO _的值是惰性的。它们只是功能!函数在被赋予值之前无法执行任何操作;在我们的例子中,函数想要的值是一个World对象,我们无法构造它。这就是 Haskell 中 IO 的美妙之处:我们可以通过使用我们熟悉和喜爱的 monadic 运算符(return、bind)来构建一个IO操作,但在运行时传入World对象之前,它不能做任何事情。

这意味着我们构建的任何未通过线程mainIO操作都不会被执行。

因此,使用foobar :: [Char] -> IO [IO ()],我们当然可以观察到返回值:

main :: IO ()
main = do
ios <- foobar "string"
print "goodbye"

但直到我们解构ios并绑定内部IO值,这些操作才能获得它们想要的World

main :: IO ()
main = do
ios <- foobar
ios !! 0
ios !! 1
ios !! 2
...
print "goodbye"

或者,简称,

main = do
ios <- foobar
sequence ios
print "goodbye"

希望这有帮助。

让我们从一个稍微不同的例子开始。如您所知,StringChar的列表:

GHCi> :set +t
GHCi> "Mississippi"
"Mississippi"
it :: [Char]

Strings列表是Char列表的列表;也就是说,[[Char]]

GHCi> group "Mississippi"
["M","i","ss","i","ss","i","pp","i"]
it :: [[Char]]

group "Mississippi"是一个[[Char]],我不希望它作为一个[Char]来处理——那会破坏使用group的意义。

在大多数情况下,IO a值与其他任何值一样,因此适用类似的注意事项。举一个具体(和现实)的例子,假设我们有一个这种类型的函数......

(KeyCode -> IO ()) -> IO (IO ())

。在 GUI 中注册按键事件的事件处理程序。这个想法是使用KeyCode -> IO ()参数调用函数,该参数指定响应按键时应发生的情况,并运行生成的IO (IO ()),以便您选择的KeyCode -> IO ()处理程序变为活动状态。但是,IO (IO ())操作生成的内部IO ()具有不同的用途:它取消注册事件处理程序,并且由您自行决定在应用程序的稍后点使用(也许永远不会)。在这种情况下,您绝对不想在外部操作之后立即运行内部操作!

总而言之,IO (IO a)是一个IO操作,运行时会产生另一个IO操作,您可能也希望也可能不想运行该操作。

PS:正如Sheyll在其他问答中提到的,join可用于扁平化嵌套IO操作或任何其他嵌套的monadic值。顺便说一下,列表也有一个Monad实例。你认为join (group "Mississippi")会怎么做?

嗯...你问这个问题:

为什么我需要解开单子的巢穴?有没有办法在不取消嵌套的情况下执行 IO?

好吧,让我直截了当地说:哈斯克尔土地上,我们怎么称呼它,不做嵌套或join,不仅仅是另一个单子组合器,它是一个神圣的特殊哼哼,Monad区别于FunctorApplicative

是的,这完全意味着Monad类型类可以使用join方法而不是>>=进行设计。

实际上,解嵌套绑定是同一事物的两种不同视角

让我剧透这篇文章的其余部分:

join = (>>= id)                  

。和:

(ma >>= amb) = join (amb <$> ma)

让我们通过证明我们可以对>>=进行join证明它们是平等的,反之亦然。

>>=制作join

好的,现在更详细地join = (>>= id)

join mmx = do mx <- mmx
x <- mx
return x

然后:

join mmx = do mx <- mmx
mx

现在有了bind又名>>=

join mmx = mmx >>= id

和无积分使用部分

join = (>>= id)

join制作>>=

现在反过来,这更难,我们需要这样一个事实,即每个Monad也是一个Functor

记住>>=实际上做了什么(双关语):

ma >>= amb = do a <- ma
amb a

我们知道amba -> m b类型的函数,这个想法是使用fmap它有(c -> d) -> m c -> m d,如果我们fmapamb我们得到一个表达式fmap amb那么c变得ad变得m b,因此m c -> m d变得m a -> m (m b)

现在我们很激动:m (m b)只是尖叫join,我们只需要输入正常应用程序可以做的m a

ma >>= amb = do mb <- fmap amb ma
mb

这是我们在上一节中看到的:

join mmb = do mb <- mmb
mb

mmb == fmap amb ma

ma >>= amb == do mb <- fmap amb ma == join (fmap amb ma)
mb

给你。

相关内容

最新更新