Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!think.com!hsdndev!husc6!carlton From: carlton@scws1.harvard.edu (david carlton) Newsgroups: comp.lang.scheme Subject: Re: open-input-file [topics from hell, part 2] Message-ID: Date: 23 Apr 91 19:51:55 GMT References: <9104211907.AA25632@august> Sender: news@husc6.harvard.edu Organization: Citizens for Boysenberry Jam Lines: 107 In-reply-to: Alan@lcs.mit.EDU's message of 21 Apr 91 19:07:37 GMT In article <9104211907.AA25632@august> Alan@lcs.mit.EDU (Alan Bawden) writes: From: david carlton 3) Have open-{input,output}-file return #f if the file doesn't exist. [ I presume you don't really want open-output-file to return #f if the file doesn't exist! ] You presume quite correctly, of course. Currently, implementations are forced to signal an error in such a situation, which is ridiculous, since it makes it impossible to handle that situation, which is a fairly common one. I agree that it is ridiculous that there isn't a way to handle that situation. But having open-input-file return #F isn't the solution. For one thing, inexperienced programmers would frequently forget to check the return value. I'm not sure that that's any worse than the current solution - the program simply crashes in a different place. Also, you really do need finer control of exceptions. People write programs that need to distinguish between: o The file does not exist in the specified directory. o Some directory in the specified path does not exist. o The file exists, but you aren't allowed to read it. o You aren't allowed to read some directory in the specified path. o The filesystem containing the file is unavailable (perhaps due to network lossage). And for open-output-file things are worse because it involves possible mutation of the filesystem. I think ultimate solution is to have a condition system that can handle exceptional conditions. This may be another one of those things that you can't really do until you have a coherent story to tell about dynamic binding. So forget the ultimate solution. So how about giving open-input-file some additional arguments that specify how it should behave in the exceptional cases? Like an a-list of "exceptions" and associated thunks -- if any of the exceptions occurs, the associated thunk is (tail-recursively) invoked. This isn't a serious proposal, but something like it is clearly preferable to the oft-suggested horror of simply having open-...-file return #F if some ill-specified set of things go wrong. Horror? Good grief... But I do agree that finer control over such matters is preferable. I don't think that taking care of it with an exception mechanism is the right way to do this - I would much rather write code that looks like (let ((port (open-input-port filename))) (if (not port) (do-something) (proceed-with-one's-business))) rather than (let ((port (begin (install-exception-handler-to-do-something)) (open-input-port filename))) (proceed-with-one's-business)) Matter of taste, I suppose, but I would rather reserve exception handlers for unexpected sorts of circumstances that programs normally would die on, such as functions being applied to arguments of the wrong type or the user hitting the interrupt key, rather than what I think is probably a fairly common circumstance and one which any program that does I/O should be prepared to handle, namely the non-existence of files. So what would I propose? In its context, I think that the C/UNIX way of doing things, with functions that return some sort of distinctive value, typically 0 or -1, to signal an error, and then set the value of the global variable errno to be equal to a more informative message for the use of the programs that care about those details. But that doesn't strike me as particularly Schemey. Another suggestion would be to have the functions return one of a set of distinctive objects if the open failed. What those distinctive objects should be is another matter - I think that I would lean towards creating a whole new type of error-objects, but doubtless many people would disagree with me. Symbols would probably work - easy to check for equality (not that speed is of primary importance in error handling, but it doesn't hurt), and it's easier to have the symbol store useful information than it would be if the error return value were a number. Yet another possibility would be to have the file return two arguments in an error condition, the first being #f and the second being something more informative. Of course, this would only work if multiple return values were implemented a la Common Lisp, something which I don't really like, but this would seem (to me, at any rate) to be a natural situation to use that. That way, people could ignore the second argument if they didn't care about why their open-input-file didn't work, but would have the extra information there if they wanted it. To be sure, none of my suggestions solve your complaint that people don't check the return value. As I said above, I don't think that this is a big problem - the program will simply crash in a different place, and while it may produce a less intuitive error message the result is still the same. And I'm not a big fan of designing languages to help the "inexperienced programmers" who "would frequently forget to check the return value" - they can learn. In this case, admittedly, things are different since code written to follow the current standard couldn't check the return value if it wanted to, but, after all, introducing incompatibilities in Scheme standards is a time-honoured tradition :-)