LISP error handling gotcha.
[1]> (defun resolve-host-name (addr)
(handler-case (hostent-name (resolve-host-ipaddr addr))
(t ()
addr)))
RESOLVE-HOST-NAME
[2]> (resolve-host-name "1.2.3.4")
"1.2.3.4"
[3]> (resolve-host-name "195.178.208.66")
"tservice.net.ru"
Exception mechanism is a great extension to the whatever language, and I think LISP has one of the best realizations (and the first one actually). I’m not very familiar with the exceptions in C++ as long as with language itself, but iirc it is not (easily) possible return back to the calling point with some value determined by the exception handler. Even in Java with its
finally
section it is still less convenient. But I may be wrong of course :)
Above chunk of the code catches the error (all exceptions) and returns requested address itself, and when no error happend it returns resolved address.
Appartment development: the brick corner completed. Appartment development: opened for myself the right way to saw the tiles.
Comments are currently closed.

Both C++ and Java can catch an exception and then proceed from there.
For instance, in C++:
string resolve_host_name(string ipaddr) { try { return ipaddr_to_hostname_or_throw_exception(ipaddr); } catch (ResolverException &r) { return ipaddr; } }Yup, that simple, especially if used with
catch (...) { }wildcards.
But I meant to show a different thing, so called restarts. I emulated a restart here by returning actual address and not resolved name, but real restart system could allow to make this decision on the higher levels. So effectivel exception handling mechanims provides a way to signal an error, while restart (and in simple cases like I created in LISP and you showed in C++ it can be emulated in the exception block) allows to recover from the given error, and that restart can be handled on higher levels depending on the application design.
What you’ve shown is exactly correspondent to the try-catch of other languages. Here’s a better example of how Common Lisp’s flow is powerful:
(defun division (n d) (flet ((read-divisor () (format t "~&Enter a new divisor: ") (list (read)))) (loop (restart-case (return (/ n d)) (use-value (new-d) :report "Use another divisor." :interactive read-divisor (setf d new-d)) (continue () :report "Return biggest value." (return most-positive-long-float)))))) (defun div-0-to-1 (n d) (handler-bind ((division-by-zero #'(lambda (c) (use-value 1 c)))) (division n d))) (defun div-infinity (n d) (handler-bind ((division-by-zero #'continue)) (division n d))) (defun div-zero-error (n d) (handler-case (division n d) (division-by-zero (c) (error c))))Example interaction:
As you can see in
, the flow goes back to
with the value 1. In fact, we could do much more before, after or instead of
.
In
, it goes back too, but in a simpler way just to signal division to return a pre-determined value.
In
, we simply catch the error and reissue it. This is the common try-catch with a throw inside. Normally, you wouldn’t write a try-catch-throw, because that’s the same as not using it at all, but here the try-catch part has the purpose of discarding the restarts, to make sure that the error is not continuable. This is why it uses
instead of
.
Another thing to notice is that when we use
from the prompt, we get an interactive debug prompt, while
,
and
select the restart automatically. This shows that the restart mechanism is fit for both interaction and automation.
PS:
My first though when I wrote this was: the
in there is ugly, and I might want to do this kind of thing more than once. Here’s Lisp’s power in action (Lisp in general, that is: eg. Common Lisp, Scheme):
(defmacro retry-restart-case (restartable-form &rest clauses) `(loop (restart-case (return ,restartable-form) ,@clauses))) (defun division (n d) (flet ((read-divisor () (format t "~&Enter a new divisor: ") (list (read)))) (retry-restart-case (/ n d) (use-value (new-d) :report "Use another divisor." :interactive read-divisor (setf d new-d)) (continue () :report "Return biggest value." (return most-positive-long-float)))))This way, I hide the
.
PPS:
In fact, the macro implementation doesn’t have to use
, I was just lazily generalizing the structure into a macro. It can be done in terms of
and
for reliability (they are treated specially by the evaluator/compiler) and faster macro expansion (
itself is a rather complicated macro). These operators are mostly useful in macros, where you want to generalize “spaghetti” code (in fact, the conventional exception handling mechanisms are just obfuscated gotos) or add syntatic sugar:
(defmacro retry-restart-case (restartable-form &rest clauses) (let ((retry (gensym))) `(block nil (tagbody ,retry (restart-case (return ,restartable-form) ; same as (return-from nil ,restartable-form) ,@clauses) (go ,retry)))))However, it has the same effect on code.
–
Paulo Madeira
I think I’m starting to forget these things, need to fresh bits out :)