Hacker News new | past | comments | ask | show | jobs | submit login
(Ab)Using Language Features: The Common Lisp Condition System (dawnofthedata.com)
81 points by jmdavis on May 16, 2014 | hide | past | favorite | 24 comments



This was something I really loved during my time in Common Lisp. This was a gem that I was hoping would make it into Clojure, but I think around version 1.3 they decided to abandon the idea and add a library to "make anything throwable" which totally missed the point in my opinion.

Are there any other languages that allow the calling scope to specify how lower-level functions handle errors without unwinding the stack?


> Are there any other languages that allow the calling scope to specify how lower-level functions handle errors without unwinding the stack?

While it isn't a mainstream language by any means, a scheme dialect called muSE [1] that I developed for writing logic for automatic music video construction supports such resumes and retries. [2]

[1] https://code.google.com/p/muvee-symbolic-expressions/

[2] https://code.google.com/p/muvee-symbolic-expressions/wiki/Ex...

(Apologies for shameless plug)

-- edit: I just saw that the clojure library "ribol" has an api design similar to muSE's in principle. A couple of design comments though -

1. The "on blah blah" is too much syntax for this. It prevents direct sharing of handlers between similar situations. Function argument pattern matching ought to be enough.

2. The retry options mechanism is somewhat limited - the options seem to apply only at the raise point itself (disclaimer: I've only had a quick glance at ribol). It is more useful to have multiple retry options at different scopes so you can partially unwind, try something, if that doesn't work unwind some more (perhaps involving cleanup), try something else, etc.

I think muSE's design is better on both these fronts.


Rust has 'Conditions', which basically allow the current function to run a closure when an error happens which tells it what to do on the error, and then continue the function call. I don't know much Rust or Common Lisp though, so I'm not sure if they're the same thing. Rust's condition functionality definitely doesn't unwind the stack when calling back though, because in Rust unwinding the stack isn't ever allowed because of memory safety issues.


Rust's conditions were actually removed. [1] They don't need language support, though, so you could re-add them as a library. When they existed, they were very close to Common Lisp conditions.

1: https://github.com/mozilla/rust/pull/12039


Perl 6 allows for handling exception in a same-scope CATCH block, so you can try to recover. It can probably be mixed with their phasers[1] in interesting ways. Also of interest might be control exceptions[2].

1: http://perlcabal.org/syn/S04.html#Phasers

2: http://perlcabal.org/syn/S04.html#Control_Exceptions


Here is an interesting perl6 example which works in Rakudo:

  $ perl6

  > $*PERL<name>;
  rakudo

  > $*PERL<compiler><ver>;
  2014.03.01

  > { CONTROL { print 1; $_.resume }; print 2; warn; say 3 }
  213
ref: https://news.ycombinator.com/item?id=7192294

For a perl5 Condition System see this implementation - https://metacpan.org/pod/ConditionSystem


That one's still exception based. You wanted

http://p3rl.org/Worlogog::Incident http://p3rl.org/Worlogog::Restart

which uses Return::MultiLevel to do stack unwinds that can pass through try/catch constructs.


perl6: super hyperdimensional prototype future programming language

"It's not Lisp!"


> Are there any other languages that allow the calling scope to specify how lower-level functions handle errors without unwinding the stack?

Any language with continuations can have it added as a library. Of course, not a lot of popular, non-Lisp languages these days have continuations... (Ruby kinda does -- it was a language-level feature in 1.8 but has been sidelined since as non-MRI implementations generally don't support them.)


Not really the same thing, but Eiffel does have retries.

https://docs.eiffel.com/book/platform-specifics/exception-me...


For conditions in Clojure check out Ribol: http://docs.caudate.me/ribol/


also here: https://github.com/bwo/conditions

though honestly Ribol is probably a better bet---just wanted to show that this kind of thing can be done as a library without too much complication.


> Are there any other languages that allow the calling scope to specify how lower-level functions handle errors without unwinding the stack?

Dylan (http://opendylan.org/) has them, but that's because some of the same people who standardized Common Lisp went on to design Dylan.


> Are there any other languages that allow the calling scope to specify how lower-level functions handle errors without unwinding the stack?

another possible answer - Erlang.

While the look of the condition/restart scheme may be very different, such a condition/restart protocol can be implemented as message passing with a known global "condition handler" process I think.

A "raise" function can be implemented as a message-send to the condition handler process followed by an immediate wait-for-response. The handler process may terminate the raising process, replace its value with something, etc. depending on the installed handlers.


Interesting abuse of the Lisp Condition system.

If you'd like to read more, an excellent introduction to the Common Lisp condition system is given in Practical Common Lisp: http://www.gigamonkeys.com/book/beyond-exception-handling-co...

And I wrote a tutorial on Common Lisp restarts, using them in a non-trivial real world example: http://chaitanyagupta.com/lisp/restarts.html


Here[0] is another example of extreme abuse of the condition system in order to construct "dynamic data pipelines" in Lisp. Roughly, it provides the ability of sending data to different parts of the call stack, and then returning back to continue processing. It can do all of this without any explicit use of variables, or without passing state around.

[0] PDF: https://bitbucket.org/tarballs_are_good/dynamic-collect/src/...


The post probably should've mentioned reader macros, since this is how you do this sort of stuff "normally".


I originally had a section about reader macros but I ended up cutting it since the article was running a little long.

Reader macros are great, but they work best when a token can unambiguously be recognized by the reader as using some "special syntax" (usually involves a '#' and then another dispatch character preceding the new syntax).

This is great when you're coming up with a brand-new syntax. But if you're trying to match something that already exists, it's not easy


For those wondering about reader macros in Common Lisp, I wrote a tutorial a couple of months back that allows the Lisp reader to understand JSON: https://gist.github.com/chaitanyagupta/9324402


It's nice seeing Common Lisp posts on the front page.


Yes, even if the industry usage is not big, learning about other languages is always a good way to improve our skills.


If anything we tend to hear more about Scheme(s) on HN than Common Lisp, which is even less associated with 'industry' (to my knowledge).


This reminds me of using doesNotRecognizeToSelector:/methodForSelector: in Objective-C to do similar tricks. For example, you can wire up a color class to respond to any hexColor selector by catching the unimplemented ones:

    NSColor * color = [Colors h00FF00];
You can then parse the selector string when the runtime doesn't find an appropriate method for it. As with all these tricks, it is of course incredibly slower than just doing GetHexColor(char * hexString), but where's the fun in that right.


This reminds me of Perl's AUTOLOAD, only for variable binding instead of functions.




Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: