C is also pure. After all, C code is just data until it gets compiled and executed. When and how often this happens is irrelevant. You could write a C interpreter in Haskell, and then your C code would be as pure as anything else in Haskell!
Looks like a reimplementation of Wouter Swiestra's work on a functional model of IO,<p><a href="http://www.staff.science.uu.nl/~swier004/Publications/BeautyInTheBeast.pdf" rel="nofollow">http://www.staff.science.uu.nl/~swier004/Publications/Beauty...</a><p>Author = {Wouter Swierstra and Thorsten Altenkirch},
Booktitle = {Haskell '07: Proceedings of the ACM SIGPLAN Workshop on Haskell},
Title = {Beauty in the Beast: A Functional Semantics of the Awkward Squad},
Pages = {25--36},
Location = {Freiburg, Germany},
Year = {2007}}
So basically, “main” is not an impure function. It’s a pure value representing an impure function. A monad is essentially a data type that represents computations in some domain-specific language, using data (return) and code (>>=) from the host language. The <i>only</i> thing special about the IO datatype is that the impure Haskell runtime can evaluate it.
If you want to do this in practice rather than as a specialised example, I would suggest using something like Conduit, rather than reinventing the wheel.<p>Conduit is used widely in the Yesod web framework (for example, to implement the HTTP server), and could easily be hooked up to stdin and stdout for an interactive pure program.<p>Conduit internally looks like this: <a href="http://hackage.haskell.org/packages/archive/conduit/0.5.6/doc/html/Data-Conduit-Internal.html" rel="nofollow">http://hackage.haskell.org/packages/archive/conduit/0.5.6/do...</a>
- note that you get to choose an underlying monad m and run things in it using PipeM, but you could just make this the Identity monad.