为什么导入Control.Applicative允许这个错误的代码键入check?

时间:2012-03-17 20:20:58

标签: haskell applicative

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

import Control.Monad
import Control.Applicative

main = forever putStrLn "Hello, infinity"

那不应该打字,但确实如此。正确的版本显然是:

main = forever $ putStrLn "Hello, infinity"

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

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

  1. 导入Control.Applicative是什么让这种情况发生?
  2. 当它进入无限循环时会发生什么?在这种情况下,Haskell究竟尝试执行什么?
  3. 谢谢,

2 个答案:

答案 0 :(得分:13)

(->) 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基本相同。

为了对读者monad(正如已知)发生的事情有所了解,请查看the documentation,All About Monads section on Reader,并且在整个过程中都有一些提示。 Typeclassopedia这可能会有所帮助。

答案 1 :(得分:6)

Control.Applicative导入Control.Monad.Instances,因此从Control.Monad.Instances重新导出实例。其中包括Functor的{​​{1}}和Monad个实例。