Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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,

like image 441
Daniel Lyons Avatar asked Mar 17 '12 20:03

Daniel Lyons


2 Answers

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.

like image 142
Daniel Wagner Avatar answered Nov 24 '22 12:11

Daniel Wagner


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

like image 40
dave4420 Avatar answered Nov 24 '22 13:11

dave4420