[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

A few random I/O proposals



Well let me add my two cents worth to the debate.  It seems that everyone
agrees that something must be done to handle I/O errors.  Three proposals
for this:

1) Special return value: return #f on any error
2) Failure continuation: pass an extra argument to be called on error
3) Error system: on error, call implicit error handler

I think that it is best to look at the more general problem of
handling arbitrary errors, not just I/O.  Sooner or later we will have
to do so and it would be ashame if there was a different error
handling mechanism just for I/O.  For this reason I'm in favor of 3.
However, it does not mean that at this point we have to define all the
details of the error system.  As a first pass, only I/O related error
situations could be defined.  To keep it simple, error conditions could
be represented as symbols (e.g. FILE-NOT-FOUND) and later a new
datatype could be added if symbols are not sufficient.

A versatile error system can be added to Scheme with minimal extra machinery.
All that is needed is

1) a dynamic binding mechanism (there is a proposal for this already)
2) a name for the dynamic variable denoting the error handler procedure
   (let's say we use ERROR-HANDLER)
3) a convention for passing information from the error point to the error
   handler.  I suggest that the error handler accepts a single argument
   which is the error descriptor (a symbol for now).

A sample use of this system is:

(define (read-foo)
  (call-with-current-continuation
    (lambda (exit)
      (dyn-let ((error-handler
                 (lambda (err-descr)
                   (case err-descr
                     ((FILE-NOT-FOUND) (exit "file \"foo\" does not exist"))
                     ((READ-ERROR)     (exit "file \"foo\" can not be read"))
                     (else             (exit "I/O error"))))))
        (let ((port (open-input-file "foo")))
          (let ((content (read port)))
            (close-input-port port)
            content))))))

For those who like the #f solution, you can always write:

(define old-open-input-file open-input-file)

(define (open-input-file filename)
  (call-with-current-continuation
    (lambda (exit)
      (dyn-let ((error-handler (lambda (err-descr) (exit #f))))
        (old-open-input-file filename)))))


While I'm at it, dynamic binding can also be used to remove the
procedures CURRENT-INPUT-PORT, CURRENT-OUTPUT-PORT,
WITH-INPUT-FROM-FILE and WITH-OUTPUT-TO-FILE because they can be written as:

(define (CURRENT-INPUT-PORT) (dyn-ref CURRENT-INPUT-PORT))

(define (WITH-INPUT-FROM-FILE filename thunk)
  (dyn-let ((CURRENT-INPUT-PORT (open-input-file filename)))
    (let ((result (thunk)))
      (close-input-port (CURRENT-INPUT-PORT))
      result)))


Marc