Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

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

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,

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...