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] [EMAIL PROTECTED]
system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED]
electrical and computer engineering, carnegie mellon university    KF8NH


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to