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:
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 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.
Control.Applicative
imports Control.Monad.Instances
, and therefore re-exports the instances from Control.Monad.Instances
. This includes Functor
and Monad
instances for ((->) r)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With