Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens if you compile a program that takes no input? (Haskell IO purity issues (again))

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?

like image 225
TheIronKnuckle Avatar asked Dec 05 '11 08:12

TheIronKnuckle


1 Answers

I think there are two questions here.

  1. Is IO referentially transparent
  2. Will GHC reduce arbitrary expressions at compile time

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.

like image 158
Nathan Howell Avatar answered Sep 21 '22 18:09

Nathan Howell