Question

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,

Was it helpful?

Solution

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.

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top