Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> My choice of implementation would be to pass a discriminator function which takes an error and returns true if the error is recoverable.

Conditions are close to exactly that + syntactic sugar.

`handler-bind` lets you establish a dynamic scope, inside of which specific functions are called when specific conditions are signaled. Signaling a condition = call the first handler function in the list that handles the signaled condition. The handler is called before the stack is unwound, which allows it to elect to either `throw`, invoke a restart, or ignore the condition entirely.

This is in contrast to exceptions in (say) Java. `throw new FooException()` does a lot of potentially destructive work before control gets transfered to the exception handler: It allocates an exception object, which fully captures the current thread's stack. (It's not too common, but this can be bypassed, if you don't throw a new exception object...) It unwinds the stack to the frame that has a matching `catch`.

Only after those two things have happened does the exception handler get a chance to decide how to proceed, and at that point, it's too late for many recovery strategies. Conditions help this by making it easier to intercept the process earlier.

Where they hurt is that they add another level of complexity to API design. With Java style exceptions, if the function fails, it fails completely. There's a convenient simplicity to that. With Lisp style conditions and restarts, A function will let you know how things are going as it does its work and then give you the opportunity to change its behavior midstream. It's a lot more work to design that kind of API, and it's probably well past the point of diminishing returns for most kinds of development.



Great answer. I'm left wondering if the solution is more complex than the problem.


There might be two different perspectives on this:

* automated error handling, mostly invisible to the user. In this case the condition system may give an attractive architecture, but it is not widely explored and it is not clear how it would integrate into a language like Java. There could be a benefit, but it is not explored by software architects. Apple's Dylan probably had something to it, but it was not widely used.

* error handling in interactive systems. That's where the Lisp community used/uses it mostly. The largest effort to use the condition system were in the OS of the MIT Lisp machine descendants - especially the one from Symbolics. It was used both in the development environment - which is tightly integrated in the OS. Most of the development tools were using it. But things like the networking code or the file system were also using it. The user interface is generally slightly more complex than current graphical user interfaces and it was expected that the user used this kind of power. When you get a dialog of restarts presented, the user needs more thinking about how to proceed than with a simple cancel/abort dialog box. There were also applications written using it and for that slightly simpler interfaces were used. For example the restarts were presented in dialogs and not in a debugger context.

It takes a bit of re-orientation to imagine a user interface, where the user has the option to repair/retry a failed operation, instead of the usual abort/redo. These benefits are easier to get in an integrated system (-> Lisp, Smalltalk) - a model where several non-integrated tools interact in a condition system is more difficult to imagine.

Today, most of the better Common Lisp systems are using the condition system in their environment. There the power can be used, but at the same time it is also easy to ignore and just use a default mode of using the abort restart - without getting the benefits of thinking a bit more and investing the time to use a more complex restart.


In practice the option to resume is used much less frequently than the option to throw. There are two primitives one can use to signal a condition: SIGNAL allows resumption, but ERROR does not. So if you don't want to deal with the complexities of a possible resumption, simply call ERROR.

Similarly, the client, to establish the handler, can use HANDLER-BIND, which is the fully general form that permits resumption, or HANDLER-CASE, which is simpler to use but does not permit resumption.

In practice, most of the time people use ERROR and HANDLER-CASE, and so the extra complexity does not intrude at all.

OTOH it is occasionally handy to be able to resume.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: