2

在这段代码中:

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

我预计由于懒惰,将根据我是否提供“A”或“B”来打印,Just something或者Nothing无论如何都打印。有人可以解释a)这里到底发生了什么吗?b) 我怎样才能达到我想要的效果?

4

2 回答 2

6

专注于(print "B" >> (return $ Just False))您正在对print带有return. IO Monad由于它是 a ,因此在能够评估语句之前Monad,它需要print "B"准确地评估以获得“值”(即使它只是被忽略) 。return因为printIOmonad 中,这意味着执行副作用。

这种排序发生在两个 IO 参数中,因此在它们能够传递给纯计算之前fromMaybe,所有效果都已经执行。s 总是先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如果这是更好的行为。

于 2013-07-09T18:47:13.457 回答
3

会发生以下情况:

您将 fromMaybe 映射到一个 IO 值。因此左边部分

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

是一个可以重写的 IO 动作

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

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

请注意,IO monad 都是关于排序操作的,因此 >>= 链中后面的操作永远不会影响是否执行较早的操作。

考虑

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)

因为,不知道 (<) 是先计算它的左参数还是右参数。

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

事实上,如果一个 IO 操作仅在实际需要其值时才执行,那么许多程序就不会打印任何内容。这是因为代码喜欢

 print "A" >> print "B"

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

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

于 2013-07-09T19:00:21.470 回答