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:
- What is it about importing Control.Applicative that lets this happen?
- What’s happening when it enters the infinite loop? What is Haskell actually trying to execute in that case?
Thanks,
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 atforeverand the class instance for functions:Now, what does
forever putStrLndo?…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.