11

我正在帮助一个朋友学习 Haskell,他最近创建了这样的代码,该代码在运行时检查并产生 CPU 燃烧循环。我对此完全感到困惑。

import Control.Monad
import Control.Applicative

main = forever putStrLn "Hello, infinity"

那不应该类型检查,但确实如此。正确的版本显然是:

main = forever $ putStrLn "Hello, infinity"

令我感到奇怪和惊讶的是,无论是否导入 Control.Applicative,您都会得到不同的结果。如果不导入它,它不会输入检查:

Prelude Control.Monad> forever putStrLn "Hello, infinity"

<interactive>:1:1:
    No instance for (Monad ((->) String))
      arising from a use of `forever'
    Possible fix: add an instance declaration for (Monad ((->) String))
    In the expression: forever putStrLn "Hello, infinity"
    In an equation for `it': it = forever putStrLn "Hello, infinity"

我在 Control.Applicative 的源代码中没有看到 Monad 实例((->) String,所以我猜测由于使用 Control.Category 或 Control.Arrow 会发生一些奇怪的事情,但我不知道。所以我想我有两个问题:

  1. 导入 Control.Applicative 的原因是什么?
  2. 当它进入无限循环时会发生什么?在这种情况下,Haskell 实际上试图执行什么?

谢谢,

4

2 回答 2

14

没有 的实例(->) String,但有(->) e... 的实例,并且该实例在许多情况下非常非常有用。对于第二个问题,我们必须看一下forever函数的类实例:

instance Monad ((->) e) where
    return x = \e -> x
    m >>= f  = \e -> f (m e) e

forever m = m >> forever m = m >>= \_ -> forever m

现在,forever putStrLn做什么?

forever putStrLn
    = putStrLn >>= \_ -> forever putStrLn
    = \e -> (\_ -> forever putStrLn) (putStrLn e) e
    = \e -> (forever putStrLn) e
    = forever putStrLn

...这只是一个纯粹的无限循环,基本上与loop = loop.

要了解 reader monad 的情况(众所周知),请查看文档、Reader 上的 All About Monads部分,并且在Typeclassopedia中散布一些提示可能会有所帮助。

于 2012-03-17T20:30:31.130 回答
6

Control.Applicative导入Control.Monad.Instances,因此从 重新导出实例Control.Monad.Instances。这包括. Functor_Monad((->) r)

于 2012-03-17T20:30:49.920 回答