Re: Comparing Lisp conditions to Java Exceptions



Sorry to resurrect a somewhat stale thread, but I came across this which
happens to be pertinent to the problem I am currently experiencing.

> When you require another programmer to declare all of the error
> conditions that may be returned by a function, you are essentially
> saying: exception handling is nice within any given subsystem, just
> don't throw anything unexpected my way across the interface boundary.
> Let's agree on all possible conditions in your code today, and then
> when any layers below you create unexpected problems in the future,
> that is entirely your problem. You must obey the original interface
> contract with respect to me, and ensure that you compensate for any new
> misbehavior of the lower layers in your own domain, so that I don't see
> any problem in mine!

While I am in no way advocating checked exceptions, I do find the above
stance somewhat puzzeling; at least if I am interpreting this correctly.

I have written a lot of Java code, and is therefore used to the way exceptions
are used and documented in the Java world. But am always on the lookup for new
interesting languages to learn. Along the way I fell in love with
Smalltalk, which in turn led me (through discussions on comp.lang.smalltalk)
to both Lisp and Ruby. By far, I am the most excuited about Lisp, and
I have been intending to start writing some 'significant' applications
in Lisp for some time. I find Lisp light years head of Java in terms of
general flexibility and attractiveness.

But 'significant' means real practical stuff. And in real applications,
error handling is very important. A networking server must be able to deal
with sockets errors without terminating; a mail user agent must be able
to differentiate between expected real-life problems (i.e., networking
issues) and internal bugs. Unfortunately, I have personally found the
error handling situation lacking in Lisp (aswell as Ruby and Smalltalk). It
is not that the condition system in and of itself has a problem; it is rather
how it is used and documented.

Specifically, the problem is that the conditions signaled by a function is
almost never documented! I am not talking about every single possible problem
that might occurr as a result of internal library bugs or bugs in calling
code. But the *EXPECTED* conditions that *ARE* going to happen in real life.
In this regard, I must say that I find Java exemplary. Given a function that
operates on a stream, or a file, or any kind of external resource, there is
documentation as to how these error conditions are handled (be it by returning
some special value or by throwing an exception). This is information I, as someone
writing calling code, MUST know in order to write robust and correct code.

Consider this fictional pseudo-Java code that is part of a fictional RDBMS backed
lookup server similar to LDAP (or POP3 or whatever):

private void handleClient(Socket socket) {
try {
BufferedReader bin =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bout =
new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

... authenticate, parse a request

StuffBeingLookedUp stuff;
try {
stuff = myDatabase.getStuff(...);
} catch (SQLException e) {
... log the database problem
bout.write("-ERR internal server error\n");
bout.flush();
return;
}

bout.write("+OK data follows\n");
... write data
bout.flush();
} catch (IOException e) {
... possibly log the problem depending on debuglevel
} finally {
try {
socket.close();
} catch (IOException e) {
/* Assuming the code above has already flush():ed in the successful case,
* just ignore it because the problem is either irrelevant or has already
* been logged.
*/
}
}
}

A couple of things to note:

* The code is able to differentiate between I/O errors having to do with the
communication with the client, from database related errors caused by
the attempt to access the database. In ths context of the server, these are
logically quite different errors and must be treated differently.
* If the SQLException was thrown due to some underlying I/O problem, this information
is not lost; in fact it would be reported as the causal exception in a stacktrace. But
at this level in the calling code, all that is relevant is that it is a database
errors; we don't care whether the database is remote, local or in-memory or what
possible errors the implementation might encounter. Thus we expect the database
API (JDBC and the JDBC driver) to emit only SQLExceptions and subclasses thereof.

Note that someone made a very good point in this thread about how the Common Lisp
condition system allows two components to communicate via conditions, even though
the intervening code has no knowledge or support of said communication. In this case
we could have the calling code (the server) co-operate with the JDBC driver IF this
was desirable. But in cases where it is NOT desirable, we instead run into the problems
below.

How do I do this in Lisp? Consider (and *PLEASE* correct me if I am misstaken on anything):

* Reading the CLHS, the spec is only concearned with type errors when it comes to the
I/O functions (read-line, read-byte, write-byte, etc). The exact condition signaled in
case of a problem is entirely implementation dependent. Thus, if I am to be portable
I *cannot* do what the Java code does above, unless i go out and investigate the
situation for each and every Common Lisp I might want to support.
* Taking sbcl as an example, I have uncovered no documentation as to what conditions
are signaled by the various functions in sb-bsd-sockets (except nothing to the effect
of "might signal some kind of condition"). Looking at the source code I can figure it
out, but even then (if I remember correctly, and I might not because this was a while
ago) the exception hierarchy is mostly concerned with mirroring the possible return
values of underlying syscalls, rather than offering a categorical hierarchy (i.e.,
differentiating "connection failed" from "some error happened during normal I/O
on an open connection", etc - although in THIS case this is not offered by Java
either).
* If after having looked at the sb-bsd-sockets source code I make my code correct
for the case of sbcl, (1) I am still only supporting sbcl, and (2) I have no clue
how "official" the set of signalled exceptions is, or whether it might randomly
change in a future release.

I have experienced the same thing with other Lisp variants (I've looked at a number of
Scheme:s for example). In the case of Scheme the error handling *mechanisms* aren't even
all that useful in some cases; and in the cases where the *mechanism* is powerful "enough"
for my purposes, the documentation offers *no* clue as to actual error conditions signalled
by the library. I haven't gone so far as to inspect the source code of all these Schemes
I've looked at. The same goes for portability API:s like CLOCC and such - no "official"
error signaling seems to exist, and who knows what might happen on different implementations.
A cursory look at some of the source gave me the impression that's it is not just a
documentation issue, but that the issue really isn't tackeled, and code will result in
different conditions on different platforms (CL implementations).

Am I the only one who feel this is a problem? Have I missed something? How do people
solve this problem when writing real programs in Lisp?

For a while I have toyed with the idea of making my first "real" project a "Common Lisp
Common Library" of sorts; which would be a portable library with various functionality
that is often very practical, and which would have properly documented error conditions.
It would also try to present a "Lisp:ish" API rather than "superficial" wrappers around
various POSIX API:s. In short, my goeal would be to be able to write code similar to
the following and have it be portable (excuse any broken indentation and the fact that
it probably wouldn't even compile):

(defun listen-loop (socket)
(handler-case
(loop (start-thread #'handle-client (accept socket)))
(io-error (ioe) (log-error ioe)))

(defun handle-client (stream)
(unwind-protect
(handler-case
(progn
... authenticate and parse request
(handler-case
(let ((stuff (get-stuff client-request)))
(write-line stream "+OK data follows")
... write data)
(database-error
(dbe)
(progn
... log the problem
(write-line stream "-ERR internal server error"))))
(flush stream))
(io-error (ioe) (log-error ioe)))
(handler-case (close stream) (io-error () nil))))

(Things like (with-open-stream ...) to guarantee that the stream is
closed would clean that up a bit)

Does anybode agree with me, even a little? Should I just not bothering doing
something like this because nobody would be interested anyway?

--
/ Peter Schuller, InfiDyne Technologies HB

PGP userID: 0xE9758B7D or 'Peter Schuller <peter.schuller@xxxxxxxxxxxx>'
Key retrieval: Send an E-Mail to getpgpkey@xxxxxxxxx
E-Mail: peter.schuller@xxxxxxxxxxxx Web: http://www.scode.org

.



Relevant Pages

  • Re: Whats wring with this try-catch block?
    ... If you look at the exception which is being ... I susect you'll find it's a ThreadAbortException. ... documentation for Response.Redirect for more information. ... I thought I'd convert the code into a method called before logging in or any other attempt to access the database. ...
    (microsoft.public.dotnet.general)
  • Re: The origins of CL conditions system
    ... Machine experience explaining the Lisp Machine error handling. ... languages with continuable exceptions (including Mary Fontana from TI ... Why can't I resume after catching an exception? ... exception handling chapter of The Design and Evolution of C++. ...
    (comp.lang.lisp)
  • Re: instant Lisp web application publishing
    ... flow of control (e,g. first log in the user, then show page x, than ... of Lisp if they want) ... flexible database that can be easily mapped into OOP terminology (so you ... Also I don't have a good candidate server to deploy it too. ...
    (comp.lang.lisp)
  • Re: Comparing Lisp conditions to Java Exceptions
    ... That should be well documented by the Lisp that you are using. ... Maybe you should consider using a Lisp system that is better ... I find their documentation to be adequate, ... Common Lisp implementations and libraries: ...
    (comp.lang.lisp)
  • Re: Statically AND Dynamically Typed Language ??
    ... right that such `interfaces' make a good way to structure documentation. ... In Lisp, for the purposes of writing manuals, one usually documents ... Python largely follows the language-supported division of code into ...
    (comp.lang.misc)