
Internally GHC does have to enforce the ordering on IO operations somehow.
If there actually was a RealWorld value being passed around you could
use some version of seq the guarantees sequential evaluation.
But GHC doesn't even pass a RealWorld around, the sequencing is
enforced by different means.
It's uninteresting for this discussion how GHC enforces the sequencing
internally, the important part is that it is part the sequencing is
part of the IO monad semantics and this is what should be used to
guarantee the sequencing of IO operations in a program.
-- Lennart
On Thu, Aug 20, 2009 at 10:41 PM, Peter Verswyvelen
But how does GHC implement the RealWorld internally? I guess this can't be done using standard Haskell stuff? It feels to me that if I would implement it, I would need seq again, or a strict field, or some incrementing "time" value that is a strict argument of each of the IO primitives. In any case, I would need strictness to control the dependencies no? I might be wrong (again) but this is all very interesting ;-)
On Thu, Aug 20, 2009 at 10:25 PM, David Menendez
wrote: On Thu, Aug 20, 2009 at 3:43 PM, Peter Verswyvelen
wrote: Also doesn't Haskell's IO system uses a hidden RealWorld type that has no value but which is passed from between monadics binds in a strict way to make the ordering work?
Haskell only describes how the IO monad behaves. GHC's implementation uses a RealWorld type, but other implementations are possible.
A quick sketch of an alternative implementation,
data Trace = Done | Get (Char -> Trace) | Put Char Trace
newtype IO a = IO { unIO :: (a -> Trace) -> Trace }
instance Monad IO where return a = IO (\k -> k a) m >>= f = IO (\k -> unIO m (\a -> unIO (f a) k))
getChar :: IO Char getChar = IO Get
putChar :: Char -> IO () putChar c = IO (\k -> Put c (k ()))
The run-time system is responsible for interpreting the Trace and inputting/outputting characters as needed. All of IO can be implemented in this manner.
So IO in Haskell is a horrible hack then? :-) If it would be done nicely, in the FRP way, then RealWorld IO would need time stamps to get rid of the hack?
Again, no. GHC's IO type uses the RealWorld value to create data dependencies. For example, putChar 'x' >> getChar, the getChar depends on the RealWorld returned by putChar 'x'.
This is why it's dangerous to open up GHC's IO type unless you know what you're doing. If you aren't careful, you may accidentally duplicate or destroy the RealWorld, at which point you risk losing purity and referential transparency.
I suppose you could consider the fact that GHC's IO is implemented using impure primitive operations a hack, but the whole point of the IO monad is to hide that impurity from the rest of the program.
-- Dave Menendez
http://www.eyrie.org/~zednenem/