为什么使用fromMaybe提取IO(Maybe Bool)会执行两个IO操作

时间:2013-07-09 18:27:19

标签: haskell io lazy-evaluation

在此代码中:

 fromMaybe <$> (print "A" >> return True) <*> (print "B" >> (return $ Just False))
 fromMaybe <$> (print "A" >> return True) <*> (print "B" >> (return $ Nothing))

我预计,由于懒惰,会打印“A”或“B”,具体取决于我是提供Just something还是Nothing,而是两者都打印出来,无论如何。有人可以解释一下这里到底发生了什么?和b)我怎样才能达到我想要的效果?

2 个答案:

答案 0 :(得分:6)

关注(print "B" >> (return $ Just False))您使用print return中的IOMonad命令进行排序。由于它是Monad,因此在能够评估print "B"语句之前,需要准确评估return以获得“值”(即使它被忽略)。对于print monad中的IO,这意味着执行副作用。

这个排序发生在两个IO参数中,所以在它们能够传递给纯计算fromMaybe之前,所有的效果都已经执行了。 Applicative总是首先执行所有效果 ,然后在纯值上计算纯计算。

fromMaybe True <$> case thing of
  Just _  -> print "A" >> return thing
  Nothing -> print "B" >> return thing

或者fromMaybe True <$> when (isJust thing) (print "A") >> print "B" >> return thing如果这是更好的行为。

答案 1 :(得分:3)

发生以下情况:

您可以通过IO值映射来自Maye。因此左侧部分

fromMaybe <$> (print "A" >> return True) 

是一个可以重写的IO动作

print "A" >> return (fromMaybe True) :: IO (Maybe Bool -> Bool)

这意味着无论如何都会打印“A”。

请注意,IO monad是关于排序操作的,因此稍后在&gt;&gt; =链中的操作可以从不影响先前的操作是否被执行。

考虑

fromMaybe <$> (getChar >>= return) 

很明显,应用fromMaybe的Char必须来自实际阅读角色。情况并非只在需要时才会被阅读。

如果是这样,以下代码就没有意义了:

do
    a <- getChar
    b <- getChar
    -- at this point, a and b have been actually read from stdin already
    return (a < b)

因为,不知道(&lt;)是否先评估其左或右参数。

相反,在任何情况下,a获取读取的第一个字符的值,并获取第二个字符的值。因此,代码片段的含义是读取两个字符并检查读取的第一个字符是否低于第二个字符。

实际上,如果只有在实际需要其值时才执行IO操作,许多程序将不会打印任何内容。这是因为像

这样的代码
 print "A" >> print "B"

故意忽略第一次印刷的结果。

出于同样的原因,将始终打印“B”。