What Conditions (Exceptions) are Really About

The “condition” (exception) feature of Common Lisp is important, but widely misunderstood, as can be seen by the frequent confusion between “conditions” and “errors”. I’ve been thinking about conditions and exceptions for many years, and here’s how I explain them.

Notes: I’m going to avoid using the word “error”, which has become overloaded. Some of the following applies to Java, but not all; I might write about Java exceptions in the future. I’ll omit the use of explicit catch/throw, for brevity. I’m only talking here about the simple heart of the condition feature, not fancy things like restarts.

Contracts, Bugs, and the Failstop Principle

Every function has a “contract” which defines what the function is supposed to do. If any function call violates the contract, the program must be incorrect: a “bug” has happened. The actual incorrect behavior might have started any time before we detect that there’s a bug.

If a program detects that a bug has happened, it should stop. That’s because if it keeps on going, there’s no way to know what it might do: write the wrong data to a file or database, display wrong answers, hang, etc. This is called the “failstop” principle.

(Exactly what “stop” means depends on the context. An interactive command might return to its event loop. A server thread might go back to its wait-for-input step. These are not perfectly safe, since the program might have corrupted transient state before the bug was detected. A safer way to stop is to kill the entire process. In Erlang, you only have to kill a thread, since each thread has its own transient state.)

Outcomes

The contract of a function specifies, among other things, the possible “outcomes” of calling the function. There is always one “usual” or “straight-line” kind of outcome, and then there can be zero or more “unusual” outcomes.

In Common Lisp, every function call either returns zero of more values, or else signals a condition. The caller discriminates on which kind of outcome this is by scrutinizing the values returned, or scrutinizing the condition that was signaled. The contract specifies the circumstances under which each kind of outcome happens, saying what values are returned or what condition object is signaled (plus what side-effects occurred) for each kind of outcome.

For example, suppose we call (open pathname :if-does-not-exist nil). Possible kinds of outcome are:

  • It returns a stream object. This means that the specified file has been opened for input.
  • It returns nil. This means that there was no file by this name in the file system. There are no side-effects.
  • It signals inappropriate-wildcard. This means that the pathname had was a wildcard pathname; it doesn’t make sense to open one. There are no side-effects.
  • It signals undefined-logical-host, and the instance’s undefined-logical-host-name is the name of the logical host. This means that it was a logical pathname whose host was not found in the set of translations.

(There are many other kinds of outcome. Sadly, Common Lisp does not actually specify what condition classes are signaled. You own contracts always should!)

If the call to open does any of these things, it is working properly and there is no bug. If the call to open returns something other than nil or an open-for-input stream to the specified file, or if it signals any other condition class, a bug has happened and the program should stop.

Conditions and bugs are entirely orthogonal. If you call open (as shown above) with a wildcard pathname, and it signals inappropriate-wildcard, that’s not a bug; that’s exactly what it’s supposed to do. If you call open and it returns a symbol, that’s bug, but no condition is signaled.

Commonly, when a function call ends with an unusual outcome, that’s specified to mean that there were no side-effects. There’s nothing theoretically wrong with specifying in the contract that a certain unusual outcome also has some side effects, but it’s not customary.

Tasteful Contract Design

When you design a function, you should first think of all the possible kinds of (correct) outcome. Then you should decide how each outcome will look to the caller: certain specific returned value(s), or certain specific conditions. This all becomes part of the contract for the function.

The general principle for making this choice is to consider which outcomes are the ones that a programmer is likely to expect and desire. You can’t always know for sure: different programmers might call the same function with different expectations. But it’s usually not hard to guess accurately. The “usual”, “straight-line” outcomes should always be a kind of returned value. The more unusual outcomes seems like it will be expected and important, the more likely you’d be to represent it by a kind of returned value than by a condition. All other unusual outcomes should be indicated by signaling conditions.

The main clue is the appearance of the function call. That’s mainly the function’s name, but it can also include the names of keyword arguments.

For example, (open "/a/b") should be defined to return a value only when it has actually opened a file, in which case it returns a stream. All other outcomes should be signals of conditions. However, (open "/a/b" :if-does-not-exist nil) suggests strongly that some outcomes (there’s no “b” in directory “/a”, or directory “/a” does not exist) should be indicated by returning nil, and conditions should be used for other outcomes.

Why Conditions are Better Than Special Return Values

It’s sometimes tempting to indicate unusual outcomes by having a function return a special value, or by having it return a second value. However, there are two drawbacks to this.

First, experience over many long years has shown that programmers often forget to check for the special values. Coding is hard and demands a lot of concentration. When a programmer is hard at work figuring out how to write an algorithm, it can be difficult to keep in mind all the possible outcomes of every call. There’s no excuse for it, but in real life, this is a common bug.

Bruce Eckel, in Thinking in Java, 2nd edition, correctly says:

In C and other earlier languages, there could be several of these formalities, and they were generally established by convention and not as part of the programming language. Typically, you returned a special value or set a flag, and the recipient was supposed to look at the value or the flag and determine that something was amiss. However, as the years passed, it was discovered that programmers who use a library tend to think of themselves as invincible — as in, “Yes, errors might happen to others, but not in my code.” So, not too surprisingly, they wouldn’t check for the error conditions (and sometimes the error conditions were too silly to check for [such as all the error values from printf]). If you were thorough enough to check for an error every time you called a method, your code could turn into an unreadable nightmare. Because programmers could still coax systems out of these languages they were resistant to admitting the truth: This approach to handling errors was a major limitation to creating large, robust, maintainable programs.

If an algorithm forgets to check for the special values, it will proceed as if the usual outcome happened. This means that the program is malfunctioning. A bug has happened but it has not been detected.

But if that unusual outcome is expressed as a signal of a condition, and the programmer forgets to handle it, the program will stop. This is what we want: failstop behavior.

(Exactly what “stop” means depends on context. In a server, there would probably be a handler-bind near the base of the stack that handles all conditions. This “ultimate handler” is called when a bug has been detected. It might write a stack trace to a log file, and then cause the thread to be restarted, for example.)

Second, even if you do remember to check for the special value, it often makes the program cluttered and harder to read. This is particularly annoying in Lisp, where it’s customary to write applicative forms where arguments to one form are themselves non-trivial forms.

I only have room here for a short example. The problems discussed above come up more often, and are harder to deal with, in much larger programs.

Suppose we have a configuration module that associates keys with URL’s. Looking up a key has two possible outcomes: the URL is found (usual) and no URL is found (unusual). The function url-host-name extracts the host name from an URL. If the URL does not specify a host name, that’s an unusual outcome. Finally, make-host creates and returns a host object, with the given host name.

We want to write a new function, get-host-from-configuration, which takes a configuration and key, and returns the host name of the specified configuration entry. There are two possible outcomes: the host, or an indication that we could not obtain it.

Version 1 disregards unusual outcomes:

(defun get-host-from-configuration (configuration key)
  "Returns the host associated with the key and the configuration."
  (make-host :name (url-host-name (read-url configuration key))))

Version 2 indicates unusual outcomes by returning nil:

(defun get-host-from-configuration (configuration key)
  "Returns the host associated with the key and the configuration,
or nil if it cannot be obtained."
  (let ((url (read-url configuration key)))
    (when url
      (let ((host-name (url-host-name url)))
        (when host-name
          (make-host :name host-name))))))

Version 3 uses conditions:

(defun get-host-from-configuration (configuration key)
  "Returns the host associated with the key and the configuration,
  signal host-not-in-config if the host cannot be found."
  (handler-case
        (make-host :name (url-host-name (read-url configuration key)))
    ((configuration-entry-not-found url-has-no-host) ()
        (error 'cannot-make-host-from-key :key key))))

Version 1 is nice and simple, but it doesn’t take into account the possibility of the unusual outcomes of its callees. Its contract cannot possibly be fulfilled.

Version 2 works, but it loses the applicative form. Every time we call a function, we have to stop, give the result a name, and check it before we can go on.

Version 3 keeps the applicative form. As long as everything has the usual outcome, it’s just like the simple code in Version 1. The “straight-line” code path is all in one place and easy to see. The infrequent unusual condition handlers are out of the way.

Conditions at the Right Level of Abstraction

You may be thinking: why not fix Version 1 by keeping the code, and just changing its contract to say

“Returns the host associated with the key and the configuration, signals configuration-entry-not-found if the URL was not found in the configuration, and signals url-has-no-host if the URL doesn’t have a host.”

In other words, we could make the callees use conditions, as with version 3, but just let the conditions propagate to the caller.

The problem with this is that it’s a modularity violation. The caller of get-host-from-configuration has no business knowing that there are URL’s involved at all. That’s an underlying implementation detail. Instead, get-host-from-configuration should indicate the unusual outcome, that it can’t make the host object, by signaling the cannot-make-host-from-key condition. It’s OK for the condition object to contain the key, since our caller clearly knows about the concept of keys since that’s an argument to get-host-from-configuration.

Similarly, it’s good for the read-url function applied to a configuration to indicate that it can’t find an entry by signaling configuration-entry-not-found rather than, say, file-not-found if the whole configuration file was missing. The caller of read-url has no business knowing whether the configuration is stored in a file or a database. We might even have two subclasses of configuration, file-configuration and database-configuration, but this would be hidden from the caller of get-host-from-configuration. Whether the configuration is stored in a file or a database is an internal implementation detail.

condition, serious-condition, and error Are Meaningless

Common Lisp defines three base condition classes named condition, serious-condition, and error. This is based on the misconception that you can tell whether the signaling of a condition is an “error” (bug) simply by knowing the class. But you can’t. Whether the signaling of a condition is a bug or not depends entirely on whether the function signaling it is defined to do so, or not. If I were designing a new dialect of Lisp, I would omit the classes serious-condition and error.

Why This Philosophy is Unconventional

Most explanations of conditions put little or no emphasis on functions having contracts that specify conditions. Few other explanations refer to the propensity of programmers to neglect to check special “error codes”.

Major Lisp texts, such as “Practical Common Lisp” and “Common Lisp: The Language, 2nd Edition” start off by acknowledging that signaling does not always mean that there’s an “error”, but they soon give up on that distinction. The word “error” is often used to sometimes mean what I call an “unusual outcome” and other times used to mean what I call a “bug”. I see these as extremely different phenomena that must be carefully distinguished.

The fact that the usual function for signaling a condition is called error greatly amplifies the confusion. If I were designing a new Lisp dialect, I would not call it that.

Bruce Eckel’s book says:

With an exceptional condition, you cannot continue processing because you don’t have the information necessary to deal with the problem in the current context. All you can do is jump out of the current context and relegate that problem to a higher context. This is what happens when you throw an exception.

As you see, that’s not how I would explain it at all. An unusual outcomes isn’t even necessarily a “problem”. It doesn’t mean you “cannot continue processing” any more than returning from the function means that.

Joel Spolsky doesn’t like exceptions at all. He considers them like “goto” statements, which everybody “considers harmful”, whereas I think that structured non-local exits do not have the problems cited in the “considered harmful” paper. He objects that “there is no way to see which exceptions might be thrown and from where”. But how are you supposed to program with functions whose contracts you do not know, exceptions or no exceptions? He says “they create too many possible exit points”; but whether you express unusual outcomes with exceptions or with special returned values, there are just the same number of them. He advocates using error codes, even though he admits that it makes programs far bulkier and makes it impossible to nest function calls.

Implementation and Portability Considerations

The Common Lisp specification makes tradeoffs between clean contracts and speed. For example, the addition function “+” ideally ought to be defined to signal a condition when either argument is a symbol. But, in order to allow generation of fast code on non-specialized hardware, its contract says that given a symbol, it may either signal, or return some integer value.

Some contracts in Common Lisp are deliberately incomplete in order to allow some implementations to add non-standard extensions.

Many contracts in Common Lisp do not specify particular condition classes to be signaled, but rather erely say that some outcome’s behavior is “a condition is signaled” without specifying a particular condition class nor instance variable values.

Topics For the Future

unwind-protect, unhandled conditions in cleanup handlers, chained conditions, Java exceptions, debugging, handler-bind, handling all condition classes, *break-on-signal*, polymorphism, with-error-context, condition names should say what happened, not where it happened.

19 Responses to “What Conditions (Exceptions) are Really About”

  1. anon Says:

    With the right semantics, special values can be wonderful. IEEE NaN values are one example. They pass through a sequence of computations, so you need only check before you rely upon a value. And a NaN need not be an outright error but simply an indication that the computation fell out of its expected domain and that you need to try another.

    The idea of a special, out-of-domain condition value has been picked up in the functional language community as well. Then the data and control flow does not change, but you still have a “Maybe” or “Error” condition when there is a sensible place to check.

    Now none of that helps you find the issue’s origin. IEEE-754 actually recommended a system much like CL conditions (some cross-polenation existed between groups). No one implemented it sanely, alas, and now most CPUs make resuming from a floating-point condition practically impossible. Thus language systems have punted, etc. It’s pretty sad, really. Software could be far more reliable. Oh well; it’s only bridges and planes and stuff, right?

  2. Scott L. Burson Says:

    Good essay. I pretty much agree. I might use multiple values more often than you do, though. It’s sometimes a judgment call just how unexpected a given outcome is likely to be.

    I’d like to comment on a strange phenomenon I ran into a few years ago: people who believed in using exceptions (I’ll use that term since this was in the context of C++), but who believed there should be only one exception class. This position astounded me, but these were smart people and they held this quite fervently. Their stated reason was that they had more than once seen exception hierarchies that grew like kudzu, and that they felt that the plethora of subclasses contributed nothing.

    They didn’t begin to persuade me, but since then I think I may have glimpsed the problem. I think that good exception/condition hierarchy design is a little bit of an art. You touch on one of the issues, I suspect, when you say “condition names should say what happened, not where it happened”. Well, I don’t know for sure exactly why the particular exception hierarchies these people had encountered had gotten so large, but I can see the temptation, particularly for inexperienced programmers, to include all the information they think might be useful — which would tend to mean there would be one exception class per `throw’ site, and I can certainly imagine the name of the throwing routine getting in there too. The result, I can well imagine, would be a greater tendency for exception hierarchies to get out of hand than would apply to other kinds of hierarchies.

    I think it takes some experience (and maybe some good examples) for people to get a feel for what kinds of information a handler is really going to be able to make use of. It also takes some restraint not to multiply entities beyond necessity, and some real thought as to what really are the important categories of exceptional situation.

    All of which is to encourage you to address this topic yourself in a future installment. I’m also curious as to what you’re going to say about Java exceptions.

  3. Mike Sperber Says:

    Excellent essay. The aspect of distinguishing between bugs and “environmental errors” is especially important, and has informed the process for R6RS. R6RS condition objects distinguish bugs in the program (called “violations” ;) from “environmental errors” (called “errors”), and the specifications say what kinds of condition objects are provided to the exception handlers.

    Thus, the distinction that you call “meaningless” in Common Lisp, is meaningful in Scheme, and the standard never gives up on making the distinction. The distinction is sometimes (but not often) hard to make, but the condition system allows expression this ambiguity through compound condition objects.

    I’ll note that we started out with an exception system only for errors, and added bugs only when we determined that it was appropriate. Both approaches to design make sense, I conjecture, but consistency is crucial.

  4. dlweinreb Says:

    Anon: Yes, I agree. IEEE floating point special values such as Nan are a special case that makes sense in its context, but it can be hard to find the root cause of the problem.

    Scott: I partially agree with them: to decide just how deep and specific to make an exception hierarchy requires taste, which is to say that you have to make an educated guess about what your callers are going to care about. It is indeed easy to go hog-wild and make too many exception classes, making fine distinctions that no caller ever ends up caring out. This results in an overly-complicated contract. You’re absolutely right that it’s a bit of an art, since you can never know for sure what future callers of your function will want, exactly. It’s too bad that these folks took a reactionary view about it; there’s no reason to react by saying that there should only be one class! Haven’t they ever heard of “moderation”?

    Mike: Thank you. I did use the word “meaningless” to be provocative, I admit. Certainly sometimes you know for sure that you have found a bug: an assert statement signals an exception that always means that there is a bug, and no contract should ever specify that exception. And there are some exceptional situations that are like assertion violations from the underlying system, e.g. this stuff that the compiler produced is not in a valid format, or the free-memory area is not in a valid format. Running out of heap space is pretty much like that too; technically anything that ever makes new objects, or calls something that does, has “out of memory” as part of its contract, but there’s not much point in really writing that into the documentation of the function in most contexts (although you can imagine programs where you’d really do that, in some kind of program intended to run in a low-memory situation and keep track of how much memory it uses, conceivably.) So if you’re talking about this kind of thing, I have no problem with it in practice.

  5. Thom Says:

    Thanks for posting this. On the list of things I wanted to steal from ITA when I left, your document on conditions was at the top of my list. So this essay is most welcome.

  6. nick caruso Says:

    Dan, have you been keeping an eye on Paul Graham’s Arc? Care to comment?

  7. Dave Dyer Says:

    I would add two considerations to your excellent exposition.

    (1) no matter how bad the underlying substrate, C/Windows for example, it’s
    possible to wrap the trash with a function that has a reliable contract along
    the given outlines. Do it.

    (2) A corollary to the fail/stop principle is that the failure/stoppage has
    to be visible, and preferably transmitted back toward to the programmer.
    This ought to be true for production as well as development.

  8. Mikael Jansson » 2008 » March » 24 Says:

    [...] Conditions in Lisp explained No Comments » Web analytics var gaJsHost = ((”https:” == document.location.protocol) ? “https://ssl.” : “http://www.”); document.write(unescape(”%3Cscript src=’” + gaJsHost + “google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E”)); var pageTracker = _gat._getTracker(”UA-3946114-1″); pageTracker._initData(); pageTracker._trackPageview(); [...]

  9. Sp3w » Blog Archive » Linkage 2008.03.24 Says:

    [...] What Conditions are Really About [...]

  10. Dave Moon Says:

    I will have more to say later, maybe in a few days, but I wanted to comment on two points.

    First, when you say “condition, serious-condition, and error Are Meaningless” I think you are misunderstanding Common Lisp. You are trying to fit it into the framework of your excellent distinction between bugs and unusual outcomes. But that isn’t what Common Lisp’s distinction between condition and error is about. I believe the distinction was intended to be that the function that signals an error is incapable of continuing execution, while the function that signals a non-error condition is capable of continuing execution if the exception handler does not unwind the stack. This depends on Common Lisp’s separation of exception signalling from stack unwinding, two distinct concepts that other languages have conflated. However, the Common Lisp standard is not entirely consistent about this, so it is easy to become confused about the intended model. But I think the distinction between errors, which require the computation to be unwound, and non-errors, which permit continuing the computation, makes some sense. Both “bugs” and “unusual outcomes” would be in the “not capable of continuing execution” category. Another way to put it is that the condition mechanism is fundamentally a dynamic non-local communication mechanism, and it can be used to communicate other things besides unexpected function results.

    Second, where you say “But, in order to allow generation of fast code on non-specialized hardware, [plus sign's] contract says that given a symbol, it may either signal, or return some integer value.” you made an interesting mistake. What CLtL II actually says is “requires that its arguments all be numbers; to call one with a non-number is an error.” and “is an error” is defined as “the effects and results are completely undefined.” So Common Lisp does not say that + is allowed to return the wrong integer value given wrong arguments. It gives the implementation much more freedom: + is allowed to do absolutely anything if given wrong arguments. It could collapse the universe, if it knows how to do that. My point is that Common Lisp is not saying that the contract of + is to signal or return an integer when given wrong arguments, which would fit into your framework. Instead there is no contract at all for that case! Of course depending on their target market particular implementations may provide stronger error checking than that!

  11. Top Posts « WordPress.com Says:

    [...] What Conditions (Exceptions) are Really About The “condition” (exception) feature of Common Lisp is important, but widely misunderstood, as can be seen […] [...]

  12. dlweinreb Says:

    I will provide an accurate answer to Dave Moon, after making very sure what the right reply is. There are some subtle issues here, possibly depending on the differences between three dialects of Lisp: Zetalisp, Common Lisp the Language 2nd Edition, and ANSI Standard X3J13 Common Lisp. Stand by.

  13. Karl Fogel Says:

    Excellent and enlightening essay. It would have been a bit easier to read if the quotes from Bruce Eckel had been formatted as quotes (I couldn’t tell where they ended, and could only tell where they began because you introduced them with a separate paragraph), and if the code examples had been screen-formatted and monospace, so that indentation and line-breaks were preserved.

  14. Will Watts Says:

    I was interested by your challenge of Eckel’s description of exceptional conditions:

    “With an exceptional condition, you cannot continue processing because you don’t have the information necessary to deal with the problem in the current context. All you can do is jump out of the current context and relegate that problem to a higher context. This is what happens when you throw an exception.”

    Borland’s Delphi Pascal compiler had a well-established exception model, not unlike Java’s but without exception specifications for functions. When Borland ported its compiler from Windows to Linux (the Kylix compiler), it was found that Linux’s model of exception handling was very different from Windows. In particular it was about two orders of magnitude slower in execution.

    When Windows programs were recompiled under Linux - and this included Borland’s own core Delphi libraries - they were unsurprisngly found to be much slower. At the time, the advice from Borland (or at least their then chief designer Danny Thorpe at a talk he gave) was that this indicated that programmers had been abusing the exception mechanism by using them for ‘non exceptional circumstances’. An exception (said Thorpe) should only be something that happens rarely; Borland felt entitled to incorporate this assumption into the design of its compiler. You, the Kylix customer, should recode where exceptions were frequently thrown, to avoid them.

    Of course, Kylix has not been a very successful product… ;-) but I wondered what you thought of this point of view. It has certainly guided the way I have coded things ever since, and seems to be a fairly useful rule of thumb.

  15. dlweinreb Says:

    Will: When I was first learning to program in PL/I in 1974, standard advice was that it was a bad idea to use procedures (functions), because they were much too slow.

    If you are forced to code in a language or operating environment in which the implementation of conditions/exceptions is very slow, then, of course, you have good reason to avoid using them in any performance-critical area.

    Most software obeys the old 80/20 rule, or even 90/10 or 95/5: 5% of the code occupies 95% of the execution time. In such a situation, you’d have to go back to using error codes or special returned values or multiple values, rather than exceptions, to denote unusual outcomes, in that part of the code. With any luck, you can use exceptions in the rest of the code.

    One of the things I was going to talk about in the future was performance, especially the different characteristics of Lisp vs. Java performance. But I thought I should run some benchmarks to make sure I know what I’m talking about. I think I know what I’ll find, but one thing I’ve learned over decades of software development is that benchmark results are very often surprising. Measure, don’t guess.

  16. dlweinreb Says:

    I have consulted with Kent Pitman, who knows more than anybody else about Lisp conditions and the official language definitions.

    David Moon: I believe that the problem here lies in the differences between Zetalisp and X3J13 Common Lisp. In the latter, which is what I was talking about (and I should have made that clearer — sorry!), there really isn’t any semantic distinction between the classes error, serious-condition, and condition. They are useful only as conventions/mnemonics for people who think that you can say something about whether a particular condition class is, in and of itself, an “error”. That, of course, depends on what you mean by “error”, a word that I have intentionally avoided.

    There are some conditions that, for all practical purposes, always indicate a bug, such as trying to compute the car of an array. While it is remotely conceivable that a program might do that on purpose, including in its contract the condition that that would signal, it would almost certainly be poor programming style. However, in my opinion, indexing off the end of an array is not, per se, a bug. You can write a linear search that’s slightly faster by letting Lisp itself check for the “I’ve reached the end of the array” circumstance, rather than coding it yourself.

    (Note: if the array is easy to modify, a better trick is to store a copy of the desired value at the end of the array, so that you’re guaranteed to find it, and then you can omit the length check. But in many cases that’s not possible, and it’s not thread safe, and so on. But I digress.)

    What you’re definitely right about is that I have not yet considered Lisp’s ability do do “handler-bind”, which is very important. I’ll talk about that in a future post. “signal” can also be interesting, but I don’t think I’ve ever actually used it.

    You are right about the contract of +. Thanks for the correction.

    The HyperSpec is annoyingly unspecific sometimes about unusual behaviors. Just recently I was asked whether the :junk-allowed t option of parse-integer means that if you give parse-integer a string, it should return nil rather than signaling. The spec is not clear on that, although it tends to suggest that it should signal, which is what I would choose if I were the spec writer. But I wish these things were spelled out more clearly; and it would be even nicer if it specified exactly what condition class were signaled in such cases.

    If we ever get to define a new Lisp, and we have enough time and resources to do all this (X3J13 did not), and we don’t have to subject ourselves to a design-by-huge-committee process (such as X3 requires), I hope we’d get this all right. Java’s standard does much better job in this respect. But writing good standards is very difficult and time-consuming. I helped a with the Java spec and it was clear that Guy Steele and Bill Joy worked very long and hard on it. They also had the advantage of having Guy Steele, a great writer, and particularly a clear and deep thinker when it comes to programming languages.

  17. Mikael Jansson » Blog Archive » Links for 2008-03-24 Says:

    [...] Conditions in Lisp explained reddit_url=’http://mikael.jansson.be/journal/2008/03/24-links’; reddit_title=’Links for 2008-03-24′; digg_url=’http://mikael.jansson.be/journal/2008/03/24-links’; //digg_skin=’compact’; digg_title = ‘Links for 2008-03-24′; ShareThis [...]

  18. unClog » On speaking conditionally Says:

    [...] if you haven’t read what Dan Weinreb has to say about the Common Lisp Condition System then, well, you should [...]

  19. Ascription is an Anathema to any Enthusiasm » Blog Archive » Conditions in Common Lisp Says:

    [...] Weinreb wrote a long blog post recently outlining what Common Lisp’s “Conditions (exceptions) are really about.”  He argues, and I won’t disagree, that they provide a clean, even elegant, way to [...]

Leave a Reply