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

Dan*_*ons 10 haskell applicative

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

import Control.Monad
import Control.Applicative

main = forever putStrLn "Hello, infinity"
Run Code Online (Sandbox Code Playgroud)

这不应该键入检查,但确实如此.正确的版本显然是:

main = forever $ putStrLn "Hello, infinity"
Run Code Online (Sandbox Code Playgroud)

令我感到奇怪和惊讶的是,无论是否导入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"
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢,

Dan*_*ner 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
Run Code Online (Sandbox Code Playgroud)

现在,做forever putStrLn什么?

forever putStrLn
    = putStrLn >>= \_ -> forever putStrLn
    = \e -> (\_ -> forever putStrLn) (putStrLn e) e
    = \e -> (forever putStrLn) e
    = forever putStrLn
Run Code Online (Sandbox Code Playgroud)

......它只是一个纯粹的无限循环,与...完全相同loop = loop.

为了得到一些直觉这是怎么回事与读者单子(因为它是已知的),看看文件,所有关于单子节的读者,并有整个洒一些提示Typeclassopedia这可能会有帮助.

  • @DanielLyons当您拥有许多需要访问某些共享配置信息的函数时,它非常有用.然后你可以写(例如)`foo >> = bar >> = baz`而不是像`\ e - >让x = foo e那样重复环境.y = bar xe; z = baz ye in y`. (4认同)

dav*_*420 6

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