Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!samsung!uunet!comp.vuw.ac.nz!cc-server4.massey.ac.nz!E.Ireland From: E.Ireland@massey.ac.nz (Evan Ireland) Newsgroups: comp.lang.scheme Subject: I/O is a special case (long). Message-ID: <559sis-b@massey.ac.nz> Date: 29 Apr 91 21:32:29 GMT Reply-To: E.Ireland@massey.ac.nz Organization: Information Sciences, Massey University, New Zealand Lines: 118 >It seems to me that the simplest and most "Scheme-like" approach is to add > (open-input-file filename [handler]) > (open-output-file filename [handler]) > -- a system-dependent code indicating the nature of the problem > -- the filename after (system-dependent) expansion >and if the handler returns normally its value would become the value of >the call to open-input-stream. The handler not being supplied, the >default would be to signal an error. Someone who -wants- #f to be >returned has only to do > (open-input-file filename (lambda (e n) #f)) I support this proposal. Let me summarise the good points as I see them. (1) No changes are required to existing code. (Chris Hanson points out that "a CL-like condition system can be added without changing any code, and is totally compatible with the standard.", but this approach is simpler). (2) Novice users, and expert users who *want* their programs to terminate with error conditions for these cases, don't have to provide "success" continuations. (Of course a condition system would allow this too, if you simply fail to handle the condition). (3) The idea of optional failure continuations for input/output procedures is "easier to understand" than a condition system. >I prefer the current situation of being able to say that a certain >procedure accepts arguments of a certain sort and signals an error if >the arguments are not valid. As Chris said, we can build a condition >system consistant with this. You are then free to handle the >conditions as you see fit (e.g. return some special error object). If you are programming in a mainly functional style, you will probably (1) Functions, such as sqrt, which are often partial. It is often tidier to check, before applying a function, whether the arguments you intend to pass are valid, and the tools for doing this are normal function applications e.g. (if (< x 0) (do-something-with-negative-number) (* 5 (sqrt x)) (2) Side-effecting procedures, mostly for I/O. Here the validity of an argument often depends on factors external to the program, e.g. the current state of the file system, which cannot be checked by the program, unless it resorts to other I/O procedures! (if (file-exists? filename) (open...) (do-something-about-error)) In this case checking beforehand is not feasible, because the state of the file system may change between the file-exists? and open... calls. Also the file-exists? call may fail! >At this juncture, seems to me, we could make the following change to the >language: for each builtin function, define another version of it with a >funny-looking name ("%CAR" or whatever) that takes explicit continuation >arguments for each of its various failure cases. (if (null? x) (do-somethinhg-with-empty-list) (do-something-with (car x))) For case (2), however, since we cannot check in this way, we need an --- Error values: these require changes to existing code (where the user *wants* termination with an error condition), and if error values are (mistakenly) not checked for, they will simply cause problems later on, possibly making some problems more difficult to trace. --- Condition systems: would solve the problem, but are more cumbersome than simple failure continuations. Also the error handlers are further removed from potential-error-causing procedure calls, making things more difficult to follow. --- Failure continuations: could be added as optional parameters to I/O procedures. Easy to understand, use, and implement, and requiring minimal changes to the language. Note that these are "plain" old continuations, not requiring call/cc. Anyway my view is biased since I am working on I/O systems for purely functional languages, where a continuation-passing style is adopted for I/O. In my scheme, every I/O operation has an error continuation, but a simple infix operator <> can be used to get the default "error" continuation, e.g. greet str = put str "Please enter your name: " <> get str <> \ name -> put str ("Hello " ++ name ++ ".\n") <> ... with (a <> b) defined as (a error b); error takes a string as argument and terminates execution. Now if I want to add error handling, I greet str = put str "Please enter your name: " (\ s -> handle_put_error s) $ get str (\ s -> handle_get_error s) $ \ name -> put str (\ s -> handle_put2_error s) ("Hello " ++ name ++ ".\n") <> ... -- Note: f $ a = f a ($ is useful for avoiding parentheses) (This is Haskell syntax). Now in Scheme, a full continuation-passing style may not be appropriate for the standard I/O, but optional failure continuations provide a similar effect to the <> operator above, with minimal changes required to the language standard. As I have noted, error handling for "functions" might be more appropriately handled by explicit checking. _______________________________________________________________________________ E.Ireland@massey.ac.nz Evan Ireland, School of Information Sciences, +64 63 69099 x8541 Massey University, Palmerston North, New Zealand.