The title is pretty self-descriptive, but there\'s one part that caught my attention:
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
It's probably best not to think too deeply about GHC's implementation of IO
, because that implementation is weird and shady and works most of the time by compiler magic and luck. The broken model that GHC uses is that an IO
action is a function from the state of the entire real world to a value paired with a new state of the entire real world. For humorous proof that this is a strange model, see the acme-realworld package.
The way this "works": Unless you import weird modules whose names start with GHC.
, you can't ever touch any of these State#
things. You're only given access to functions that deal in IO
or ST
and that ensure the State#
can't be duplicated or ignored. This State#
is threaded through the program, which ensures that the I/O primitives actually get called in the proper order. Since this is all for pretend, the State#
is not a normal value at all—it has a width of 0, taking 0 bits.
Why does State#
take a type argument? That's a much prettier bit of magic. ST
uses that to force polymorphism needed to keep state threads separate. For IO
, it's used with the special magic RealWorld
type argument.