[Haskell-cafe] Re: do

Brandon S. Allbery KF8NH allbery at ece.cmu.edu
Mon Oct 15 23:59:25 EDT 2007

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  
   but in fact it is not, as unsafePerformIO uses the magic builtin  
   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  

   (*) 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  

-- 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  
-- 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 at kf8nh.com
system administrator [openafs,heimdal,too many hats] allbery at ece.cmu.edu
electrical and computer engineering, carnegie mellon university    KF8NH

More information about the Haskell-Cafe mailing list