为什么嵌套在其他monad中的IO不能执行?有没有办法迫使他们去?

时间:2017-02-22 23:27:45

标签: haskell monads

这是我上一个问题的后续跟进。 IO action nested in other monads not executing

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

为什么我需要取消monad?有没有办法在不去除的情况下执行IO?

注意:这是 what-if ,而不是关于好的或坏的做法的问题。

3 个答案:

答案 0 :(得分:9)

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之前它无法执行任何操作对象

这意味着我们构建的任何IO操作都不会通过main进行操作。

因此,使用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"

希望这有帮助。

答案 1 :(得分:4)

让我们从略有不同的例子开始。如您所知,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值与任何其他值一样,因此适用类似的注意事项。为了给出具体的(and realistic)示例,假设我们有一个这种类型的函数......

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

...在GUI中为按键事件注册事件处理程序。我们的想法是,您使用KeyCode -> IO ()参数调用该函数,该参数指定响应按键时应发生的情况,并运行生成的IO (IO ())以便您选择的KeyCode -> IO ()处理程序变得活跃。然而,由 IO ()操作生成的内部 IO (IO ()) 用于不同的目的:它取消注册事件处理程序,它应该是您可以自行决定在申请的稍后时间使用 - 也许永远不会。在这种情况下,你绝对不希望在外部动作之后立即运行内部动作!

总结一下,IO (IO a)是一个IO操作,在运行时会产生另一个IO操作,您可能也可能不想这样做。

P.S。:正如她提到in the other Q&Ajoin可以用来展平嵌套的IO动作或任何其他嵌套的monadic值。顺便提一下,列表也有Monad个实例。您认为join (group "Mississippi")会做什么?

答案 2 :(得分:0)

嗯......你问这个问题:

  

为什么我需要取消monad?有没有办法在不去除的情况下执行IO?

好吧,让我尽可能直言不讳: Unnesting join 我们在Haskell-land中如何称呼它,不仅仅是另一个monad combinator,一个圣洁的特殊事物,将MonadFunctorApplicative区分开来!

是的,这完全意味着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的事实。

请记住>>=行为 es(双关语):

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

我们知道amba -> m b类型的函数,我们的想法是使用fmap (c -> d) -> m c -> m d,如果我们fmap {{1我们得到一个表达式amb,然后fmap amb变为ca变为dm b因此变为m c -> m d!< / p>

现在我们很激动: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

你去。