I've been away - sorry this response is being made so long after the
original message.


The Haskell IO System

Andy Gill's message about IO points out the fact the monadic IO has
not yet been well integrated into the Haskell IO system.  Indeed, to
do this right will require a redesign of the Haskell IO system.  This
should probably be addressed in a new version of the report as the
implementation of IO has diverged in the different Haskell systems.

Andy writes:

  As you know, Glasgow is trying to push the IO monad as a general
  mechanism for expressing I/O in Haskell. Unfortunately we can't do
  some simple things right now.

  For example, to read stdin then write the input to stdout in Haskell is.

  > main = readChan stdin exit (\ str -> appendChan stdout str exit done)

  In the IO world we have no standard way of directly doing this.

This means only that the Glasgow system does not directly provide
monadic versions of readChan and appendChan.  Such functions are
easily defined (as Andy later goes on to do) but this brings up the
important issue of exactly how the standard Haskell IO system should
be defined.  Right now there are three different ways to do IO:
  Streams (implemented internally by the system)
  Continuations (layered on streams by PreludeIO)
  An IO Monad (not in the report, provided by the Glasgow & latest
               Yale systems)

The root of the Andy's problem is that the functionality of stream IO
may not be predefined directly in the monad.  In fact, the Glasgow
system allows monadic and stream IO to be intermixed using
DialogueToIO (hope I got the name right ...), a stream interpreter
written using the IO monad.  Functions such as readChanIO are
necessarily part of this interpreter and could easily be made visible.


Stream IO Considered Harmful!!

Since there are two fundamentally different ways of doing IO (streams
and monads), the question of which is better should be addressed.
Continuation IO as defined in the Haskell report is just sugaring on
top of stream IO and need not be considered as an IO style in itself
(although Paul Hudak's MADT work could be applied to make this a primitive
IO style too!).  Anyway, there is a very serious difference between
streams and monads: extensibility.  New IO requests or responses
within the stream framework require a change in the Request and
maybe Response data type.  This prevents modular expansion of the IO system.
The monad, however, does not require that all IO functions be mirrored
by a data type and can be extended easily.  The C interface in the
Glasgow system (as well as the Lisp interface in Yale Haskell) depend
on the ability to add new functions to the IO monad.


Eliminating Stream IO

Although stream and monadic IO are (almost) equally expressive, this
extensibility problem (an engineering issue!) suggests that there is
a good reason to eliminate streams completely.  The Yale system has
in fact done so: the Request and Response types have been removed and
continuation IO is defined directly on the monad.  Thus only monadic
IO is supported but the continuation style sugaring can be used
unchanged.  In our system we define

type Dialogue = IO ()

The type signatures of the continuation IO functions remain unchanged;
indeed most programs are totally unaffected by the elimination of
streams since they exclusively use continuations for IO.  We have not
provided monadic primitives for operations such as readChan but these
could easily be made available to the user.  However, continuation IO
can be freely mixed with monadic IO without conversions such as
DialogueToIO.

In retrospect, stream IO seems to be a historical artifact in Haskell.
Eliminating it will simplify the language without losing
expressiveness.  Compatibility is perhaps the only reason to support
streams but since most programs use only continuation IO this
does not seem to be a compelling reason for their retention.  


Processing IO Requests

Getting back to Andy's message, he suggests using a function to
process individual IO requests:

  To allow us to specify the IO, we need a hook into the
  current runtime i/o system.  lets call it
  
  > processRequestIO :: Request -> IO Response

  Glasgow Haskell (as of version 0.15) provides this as part of its
  extensions.

Unfortunately, using this as a basis for building the IO system
unnecessarily perpetuates the stream style of IO involving Request and
Response.  By providing monadic functions directly as primitives such
`universal' IO functions can be happily eliminated.


A New Haskell IO System

Redesigning the Haskell IO system from scratch without streams will
take some work.  All we have done in our latest release is to remove
streams while supporting continuations.  We also provide the same
basic monad functions that Glasgow does.  More could be done to
make the IO system more attractive and convenient.  Much of what Andy
suggests in terms of error handling is quite useful and would be a
good addition to the standard prelude (always filling in error
continuations with abort is tiresome!).  Other parts of his proposal
simply supply monad analogues to existing stream / continuation
functions (print vs printIO, for example); these would not be
necessary if streams would be eliminated.  However, the problem of
having two ways (continuation and monadic) to do the same thing
remains.  The prelude could also supply more IO utility functions,
such as extra monadic combinators.

I would like to thank Andy for starting discussion on a very important
topic that ought to be addressed as soon as possible before the
various Haskell implementations go their separate ways.

Let the debate begin!!

   John Peterson
   [EMAIL PROTECTED]
   Yale Haskell Project





Reply via email to