Hacker News new | ask | show | jobs
by lobster_johnson 3948 days ago
Thanks. I get that part. After digging a bit, it seems the implementation of IO is in fact a bit magical [1] (and not possible to implement in pure Haskell). GHC's IO is implemented using a GHC-internal pseudo-state monad that uses a magical "world" value to enforce linear ordering — all of which, I'm guessing, would prevent the compiler from accidentally optimizing away or memoizing side-effectful functions?

Edit: I find it interesting that Haskell tutorials and books generally don't try to peel away some of Haskell's abstractions. Many texts will tell you that "IO a" represents an "action" and expect you to take this at face value, intead of explaining how this is distinguishable from a mere function, and how it's implemented internally. For example, I would say one of the big eureka moments for any Haskell learner is to realize how lazy-evaluated graphs of functions can turn into linearly-ordered programs thanks to the transparent "baton passing" of monad chaining (IO in particular). But I've yet to find a text which articulates this idea very well.

[1] http://stackoverflow.com/a/9244715/632555

1 comments

GHC's implementation of IO isn't the only one possible. For example, if Haskell had only two available IO operations (read a character or print a character), the following would be a workable definition of the IO type:

    data IO a = PutChar (Char, IO a)
              | GetChar (Char -> IO a)
              | Return a
Programs can work by constructing a value of type IO () and defining it as main, just like in regular Haskell. Note that IO is no longer a wrapped impure function, and there are no magical tokens in sight. In fact, the above implementation doesn't even have to be opaque, it can be completely exposed to programmers. Nevertheless, it's just as easy for the runtime to interpret in a sequential way, and there won't be any problems with accidentally optimizing away or memoizing anything.