putStrLn
when called with any arguments will always return a value of type IO ()
. I agree that's pure, I can handle that. But is it referentially transparent? I think so, because for any given input you could replace the function call with an IO ()
which would throw the correct string at stdout.
So I'm cool with putStrLn
, but getLine
when called with no arguments could return any number of things provided they are of type IO String
. That is neither pure nor referentially transparent right?
Silly pedantic question and it's probably not going to change how I write my code, but I really want to nail this once and for all. (I understand that the IO monad will sequence things correctly, that's not my issue)
This raises another question for me. Is the compiler smart enough to recognise a program that takes no input? For example say I compile
main = putStrLn . show $ map (+1) [1..10]
Is GHC smart enough to reduce that program to the IO ()
that causes [2,3,4,5,6,7,8,9,10,11] to be printed out? Or does it still work it out and evaluate/execute everything at runtime? Same goes for arbitrary programs where Input is not required. Does GHC employ the fact that the entire program is referentially transparent and can simply be replaced with it's value?
I think there are two questions here.
Looking at the type of IO, you can imagine that it emulates referential transparency by relying on mysterious value RealWorld
that does not have a data constructor, and by making each statement depend on the last (in a single threaded world). In the case of an IO String
, this is a newtype wrapper around RealWorld -> (RealWorld, String)
... which is a function, not a value. Using IO
without the Monad
instance makes this particularly, and painfully, obvious.
Prelude GHC.Types> :info IO
newtype IO a
= IO (GHC.Prim.State# GHC.Prim.RealWorld
-> (# GHC.Prim.State# GHC.Prim.RealWorld, a #))
As for GHC's optimization, in this case it does not reduce the list to a string at compile time. The optimized code produced by GHC 7.2.1 lazily generates a list, maps (+1) over the results, converts the list to a string, and finally prints it to the console. Pretty much exactly as it reads in your example.
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