Why does importing Control.Applicative allow this bad code to type check?

China☆狼群 提交于 2019-11-28 03:20:46

问题


I'm helping a friend learn Haskell and he recently created code like this, which type checks and produces a CPU-burning loop at runtime. I'm completely baffled by this.

import Control.Monad
import Control.Applicative

main = forever putStrLn "Hello, infinity"

That shouldn't type check, but does. The correct version would clearly be:

main = forever $ putStrLn "Hello, infinity"

What's weird and surprising to me is that you get different results with and without importing Control.Applicative. Without importing it, it doesn't type 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"

I don't see a Monad instance for ((->) String in the source for Control.Applicative, so I'm guessing something weird is happening due to its use of Control.Category or Control.Arrow, but I don't know. So I guess I have two questions:

  1. What is it about importing Control.Applicative that lets this happen?
  2. What's happening when it enters the infinite loop? What is Haskell actually trying to execute in that case?

Thanks,


回答1:


There isn't an instance for (->) String, but there is an instance for (->) e... and that instance is very, very useful in many situations. For the second question, we must take a look at forever and the class instance for functions:

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

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

Now, what does forever putStrLn do?

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

...it's just a pure infinite loop, basically identical to loop = loop.

To get some intuition for what's going on with the reader monad (as it is known), take a look at the documentation, the All About Monads section on Reader, and there are some hints sprinkled throughout the Typeclassopedia which might help.




回答2:


Control.Applicative imports Control.Monad.Instances, and therefore re-exports the instances from Control.Monad.Instances. This includes Functor and Monad instances for ((->) r).



来源:https://stackoverflow.com/questions/9753389/why-does-importing-control-applicative-allow-this-bad-code-to-type-check

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!