
I actually got this done several hours ago, but my DSL is being annoying tonight... Anyway, here's a simple example of how to do explicit/non-monadic I/O in GHC. (It *only* works in GHC; other compilers have different internal implementations of IO.) I specifically modeled it to highlight its resemblance to State. {-# OPTIONS_GHC -fno-implicit-prelude -fglasgow-exts #-} import GHC.Base import GHC.IOBase import GHC.IO import GHC.Handle (stdout) {- This is horrible evil to demonstrate how to do I/O without the help of the IO monad. And yes, it is very much a help. The trick here is that the type IO is a state-like type: a value constructor which wraps a function. Thus, working with it manually requires that we have a runIO.(*) Naively, this looks like unsafePerformIO; but in fact it is not, as unsafePerformIO uses the magic builtin RealWorld# to create a new State# RealWorld on the fly, but in fact we are passing on the one we get from somewhere else (ultimately, the initial state for main). (Additionally, we don't unwrap the resulting tuple; we return it.) This is why runIO is really *safePerformIO* (i.e. entirely normal I/O). (*) Well, not absolutely. GHC.IOBase uses unIO instead: unIO (IO f) = f I think this is a little cleaner, and better demonstrates how IO is really not all that special, but simply a way to pass state around. -} -- treat IO like State, for demonstration purposes runIO :: IO a -> State# RealWorld -> (# State# RealWorld,a #) runIO (IO f) s = f s -- And here's our simple "hello, world" demo program main :: IO () main = IO (\s -> runIO (putStrLn' "hello, world") s) -- this is just to demonstrate how to compose I/O actions. we could just -- call the real putStrLn above instead; it is operationally identical. -- write a string followed by newline to stdout -- this is completely normal! putStrLn' :: String -> IO () putStrLn' = hPutStrLn' stdout -- write a string followed by newline to a Handle hPutStrLn' :: Handle -> String -> IO () hPutStrLn' h str = IO (\s -> let (# s',_ #) = runIO (hPutStr' h str) s in runIO (hPutChar h '\n') s') -- write a string, iteratively, to a Handle hPutStr' :: Handle -> String -> IO () hPutStr' _ [] = IO (\s -> (# s,() #)) hPutStr' h (c:cs) = IO (\s -> let (# s',_ #) = runIO (hPutChar h c) s in runIO (hPutStr' h cs) s') -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH