1
0
Fork 0
cl-sites/lispcookbook.github.io/cl-cookbook/error_handling.html
2023-10-25 11:23:21 +02:00

681 lines
27 KiB
HTML
Raw Blame History

<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 for Linux version 5.2.0">
<title>Error and exception handling</title>
<meta charset="utf-8">
<meta name="description" content="A collection of examples of using Common Lisp">
<meta name="viewport" content=
"width=device-width, initial-scale=1">
<link rel="icon" href=
"assets/cl-logo-blue.png"/>
<link rel="stylesheet" href=
"assets/style.css">
<script type="text/javascript" src=
"assets/highlight-lisp.js">
</script>
<script type="text/javascript" src=
"assets/jquery-3.2.1.min.js">
</script>
<script type="text/javascript" src=
"assets/jquery.toc/jquery.toc.min.js">
</script>
<script type="text/javascript" src=
"assets/toggle-toc.js">
</script>
<link rel="stylesheet" href=
"assets/github.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1 id="title-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Error and exception handling</h1>
<div id="logo-container">
<a href="index.html">
<img id="logo" src="assets/cl-logo-blue.png"/>
</a>
<div id="searchform-container">
<form onsubmit="duckSearch()" action="javascript:void(0)">
<input id="searchField" type="text" value="" placeholder="Search...">
</form>
</div>
<div id="toc-container" class="toc-close">
<div id="toc-title">Table of Contents</div>
<ul id="toc" class="list-unstyled"></ul>
</div>
</div>
<div id="content-container">
<h1 id="title-non-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Error and exception handling</h1>
<!-- Announcement we can keep for 1 month or more. I remove it and re-add it from time to time. -->
<p class="announce">
📹 💻
<a style="font-size: 120%" href="https://www.udemy.com/course/common-lisp-programming/?couponCode=LISPMACROSPOWER" title="This course is under a paywall on the Udemy platform. Several videos are freely available so you can judge before diving in. vindarel is (I am) the main contributor to this Cookbook."> Discover vindarel's Lisp course in videos with this September coupon.</a>
<strong>
Recently added: 18 videos on MACROS.
</strong>
<a style="font-size: 90%" href="https://github.com/vindarel/common-lisp-course-in-videos/">Learn more</a>.
</p>
<p class="announce-neutral">
📕 <a href="index.html#download-in-epub">Get the EPUB and PDF</a>
</p>
<div id="content"
<p>Common Lisp has mechanisms for error and condition handling as found
in other languages, and can do more.</p>
<p>What is a condition ?</p>
<blockquote>
<p>Just like in languages that support exception handling (Java, C++,
Python, etc.), a condition represents, for the most part, an
“exceptionalâ€<EFBFBD> situation. However, even more so than those languages,
<em>a condition in Common Lisp can represent a general situation where
some branching in program logic needs to take place</em>, not
necessarily due to some error condition. Due to the highly
interactive nature of Lisp development (the Lisp image in
conjunction with the REPL), this makes perfect sense in a language
like Lisp rather than say, a language like Java or even Python,
which has a very primitive REPL. In most cases, however, we may not
need (or even allow) the interactivity that this system offers
us. Thankfully, the same system works just as well even in
non-interactive mode.</p>
<p><a href="https://z0ltan.wordpress.com/2016/08/06/conditions-and-restarts-in-common-lisp/">z0ltan</a></p>
</blockquote>
<p>Let’s dive into it step by step. More resources are given afterwards.</p>
<h2 id="ignoring-all-errors-returning-nil">Ignoring all errors, returning nil</h2>
<p>Sometimes you know that a function can fail and you just want to
ignore it: use <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_ignore.htm">ignore-errors</a>:</p>
<pre><code class="language-lisp">(ignore-errors
(/ 3 0))
; in: IGNORE-ERRORS (/ 3 0)
; (/ 3 0)
;
; caught STYLE-WARNING:
; Lisp error during constant folding:
; arithmetic error DIVISION-BY-ZERO signalled
; Operation was (/ 3 0).
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
NIL
#&lt;DIVISION-BY-ZERO {1008FF5F13}&gt;
</code></pre>
<p>We get a welcome <code>division-by-zero</code> warning but the code runs well and
it returns two things: <code>nil</code> and the condition that was signaled. We
could not choose what to return.</p>
<p>Remember that we can <code>inspect</code> the condition with a right click in Slime.</p>
<h2 id="catching-any-condition-handler-case">Catching any condition (handler-case)</h2>
<!-- we will say "handling" for handler-bind -->
<p><code>ignore-errors</code> is built from <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_hand_1.htm">handler-case</a>. We can write the previous
example by catching the general <code>error</code> but now we can return whatever
we want:</p>
<pre><code class="language-lisp">(handler-case (/ 3 0)
(error (c)
(format t "We caught a condition.~&amp;")
(values 0 c)))
; in: HANDLER-CASE (/ 3 0)
; (/ 3 0)
;
; caught STYLE-WARNING:
; Lisp error during constant folding:
; Condition DIVISION-BY-ZERO was signalled.
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
We caught a condition.
0
#&lt;DIVISION-BY-ZERO {1004846AE3}&gt;
</code></pre>
<p>We also returned two values, 0 and the signaled condition.</p>
<p>The general form of <code>handler-case</code> is</p>
<pre><code class="language-lisp">(handler-case (code that errors out)
(condition-type (the-condition) ;; &lt;-- optional argument
(code))
(another-condition (the-condition)
...))
</code></pre>
<h2 id="catching-a-specific-condition">Catching a specific condition</h2>
<p>We can specify what condition to handle:</p>
<pre><code class="language-lisp">(handler-case (/ 3 0)
(division-by-zero (c)
(format t "Caught division by zero: ~a~%" c)))
;; …
;; Caught division by zero: arithmetic error DIVISION-BY-ZERO signalled
;; Operation was (/ 3 0).
;; NIL
</code></pre>
<p>This workflow is similar to a try/catch as found in other languages, but we can do more.</p>
<h2 id="handler-case-vs-handler-bind">handler-case VS handler-bind</h2>
<p><code>handler-case</code> is similar to the <code>try/catch</code> forms that we find in
other languages.</p>
<p><a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_handle.htm">handler-bind</a> (see the next examples), is what to use
when we need absolute control over what happens when a signal is
raised. It allows us to use the debugger and restarts, either
interactively or programmatically.</p>
<p>If some library doesn’t catch all conditions and lets some bubble out
to us, we can see the restarts (established by <code>restart-case</code>)
anywhere deep in the stack, including restarts established by other
libraries that this library called. And <em>we can see the stack
trace</em>, with every frame that was called and, in some lisps, even see
local variables and such. Once we <code>handler-case</code>, we “forgetâ€<C3A2> about
this, everything is unwound. <code>handler-bind</code> does <em>not</em> rewind the
stack.</p>
<p>Before we properly see <code>handler-bind</code>, let’s study conditions and restarts.</p>
<h2 id="defining-and-making-conditions">Defining and making conditions</h2>
<p>We define conditions with <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_defi_5.htm">define-condition</a> and we make (initialize) them with <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_mk_cnd.htm">make-condition</a>.</p>
<pre><code class="language-lisp">(define-condition my-division-by-zero (error)
())
(make-condition 'my-division-by-zero)
;; #&lt;MY-DIVISION-BY-ZERO {1005A5FE43}&gt;
</code></pre>
<p>It’s better if we give more information to it when we create a condition, so let’s use slots:</p>
<pre><code class="language-lisp">(define-condition my-division-by-zero (error)
((dividend :initarg :dividend
:initform nil
:reader dividend)) ;; &lt;-- we'll get the dividend with (dividend condition). See the CLOS tutorial if needed.
(:documentation "Custom error when we encounter a division by zero.")) ;; good practice ;)
</code></pre>
<p>Now when we’ll “signalâ€<C3A2> or “throwâ€<C3A2> the condition in our code we’ll be
able to populate it with information to be consumed later:</p>
<pre><code class="language-lisp">(make-condition 'my-division-by-zero :dividend 3)
;; #&lt;MY-DIVISION-BY-ZERO {1005C18653}&gt;
</code></pre>
<div class="info-box info">
<p>
<strong>Note:</strong> here's a quick reminder on classes, if you are not fully operational
on the <a href="clos.html">Common Lisp Object System</a>.
</p>
</div>
<pre><code class="language-lisp">(make-condition 'my-division-by-zero :dividend 3)
;; ^^ this is the ":initarg"
</code></pre>
<p>and <code>:reader dividend</code> created a <em>generic function</em> that is a “getterâ€<C3A2>
for the dividend of a <code>my-division-by-zero</code> object:</p>
<pre><code class="language-lisp">(make-condition 'my-division-by-zero :dividend 3)
;; #&lt;MY-DIVISION-BY-ZERO {1005C18653}&gt;
(dividend *)
;; 3
</code></pre>
<p>an “:accessorâ€<C3A2> would be both a getter and a setter.</p>
<p>So, the general form of <code>define-condition</code> looks and feels like a
regular class definition, but despite the similarities, conditions are
not standard objects.</p>
<p>A difference is that we can’t use <code>slot-value</code> on slots.</p>
<h2 id="signaling-throwing-conditions-error-warn-signal">Signaling (throwing) conditions: error, warn, signal</h2>
<p>We can use <a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_error.htm#error">error</a> in two ways:</p>
<ul>
<li><code>(error "some text")</code>: signals a condition of type <a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_smp_er.htm">simple-error</a>, and opens-up the interactive debugger.</li>
<li><code>(error 'my-error :message "We did this and that and it didn't work.")</code>: creates and throws a custom condition with its slot “messageâ€<C3A2> and opens-up the interactive debugger.</li>
</ul>
<p>With our own condition we can do:</p>
<pre><code class="language-lisp">(error 'my-division-by-zero :dividend 3)
;; which is a shortcut for
(error (make-condition 'my-division-by-zero :dividend 3))
</code></pre>
<p>Throwing these conditions will enter the interactive debugger, where
the user may select a restart.</p>
<p><code>warn</code> will not enter the debugger (create warning conditions by subclassing <a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_smp_wa.htm">simple-warning</a>).</p>
<p>Use <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_signal.htm">signal</a> if you do not want to enter the debugger, but you still want to signal to the upper levels that something <em>exceptional</em> happened.</p>
<p>And that can be anything. For example, it can be used to track
progress during an operation. You would create a condition with a
<code>percent</code> slot, signal one when progress is made, and the
higher level code would handle it and display it to the user. See the
resources below for more.</p>
<h3 id="conditions-hierarchy">Conditions hierarchy</h3>
<p>The class precedence list of <code>simple-error</code> is <code>simple-error, simple-condition, error, serious-condition, condition, t</code>.</p>
<p>The class precedence list of <code>simple-warning</code> is <code>simple-warning, simple-condition, warning, condition, t</code>.</p>
<h3 id="custom-error-messages-report">Custom error messages (:report)</h3>
<p>So far, when throwing our error, we saw this default text in the
debugger:</p>
<pre><code>Condition COMMON-LISP-USER::MY-DIVISION-BY-ZERO was signalled.
[Condition of type MY-DIVISION-BY-ZERO]
</code></pre>
<p>We can do better by giving a <code>:report</code> function in our condition declaration:</p>
<pre><code class="language-lisp">(define-condition my-division-by-zero (error)
((dividend :initarg :dividend
:initform nil
:accessor dividend))
;; the :report is the message into the debugger:
(:report (lambda (condition stream)
(format stream "You were going to divide ~a by zero.~&amp;" (dividend condition)))))
</code></pre>
<p>Now:</p>
<pre><code class="language-lisp">(error 'my-division-by-zero :dividend 3)
;; Debugger:
;;
;; You were going to divide 3 by zero.
;; [Condition of type MY-DIVISION-BY-ZERO]
</code></pre>
<h2 id="inspecting-the-stacktrace">Inspecting the stacktrace</h2>
<p>That’s another quick reminder, not a Slime tutorial. In the debugger,
you can inspect the stacktrace, the arguments to the function calls,
go to the erroneous source line (with <code>v</code> in Slime), execute code in
the context (<code>e</code>), etc.</p>
<p>Often, you can edit a buggy function, compile it (with the <code>C-c C-c</code>
shortcut in Slime), choose the “RETRYâ€<C3A2> restart and see your code pass.</p>
<p>All this depends on compiler options, wether it is optimized for
debugging, speed or security.</p>
<p>See our <a href="debugging.html">debugging section</a>.</p>
<h2 id="restarts-interactive-choices-in-the-debugger">Restarts, interactive choices in the debugger</h2>
<p>Restarts are the choices we get in the debugger, which always has the
<code>RETRY</code> and <code>ABORT</code> ones.</p>
<p>By <em>handling</em> restarts we can start over the operation as if the error
didn’t occur (as seen in the stack).</p>
<h3 id="using-asserts-optional-restart">Using assert’s optional restart</h3>
<p>In its simple form <code>assert</code> does what we know:</p>
<pre><code class="language-lisp">(assert (realp 3))
;; NIL = passed
</code></pre>
<p>When the assertion fails, we are prompted into the debugger:</p>
<pre><code class="language-lisp">(defun divide (x y)
(assert (not (zerop y)))
(/ x y))
(divide 3 0)
;; The assertion (NOT #1=(ZEROP Y)) failed with #1# = T.
;; [Condition of type SIMPLE-ERROR]
;;
;; Restarts:
;; 0: [CONTINUE] Retry assertion.
;; 1: [RETRY] Retry SLIME REPL evaluation request.
;; …
</code></pre>
<p>It also accepts an optional parameter to offer to change values:</p>
<pre><code class="language-lisp">(defun divide (x y)
(assert (not (zerop y))
(y) ;; list of values that we can change.
"Y can not be zero. Please change it") ;; custom error message.
(/ x y))
</code></pre>
<p>Now we get a new restart that offers to change the value of Y:</p>
<pre><code class="language-lisp">(divide 3 0)
;; Y can not be zero. Please change it
;; [Condition of type SIMPLE-ERROR]
;;
;; Restarts:
;; 0: [CONTINUE] Retry assertion with new value for Y. &lt;--- new restart
;; 1: [RETRY] Retry SLIME REPL evaluation request.
;; …
</code></pre>
<p>and when we choose it, we are prompted for a new value in the REPL:</p>
<pre><code>The old value of Y is 0.
Do you want to supply a new value? (y or n) y
Type a form to be evaluated:
2
3/2 ;; and our result.
</code></pre>
<h3 id="defining-restarts-restart-case">Defining restarts (restart-case)</h3>
<p>All this is good but we might want more custom choices. We can add
restarts on the top of the list by wrapping our function call inside
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_rst_ca.htm">restart-case</a>.</p>
<pre><code class="language-lisp">(defun divide-with-restarts (x y)
(restart-case (/ x y)
(return-zero () ;; &lt;-- creates a new restart called "RETURN-ZERO"
0)
(divide-by-one ()
(/ x 1))))
(divide-with-restarts 3 0)
</code></pre>
<p>In case of <em>any error</em> (we’ll improve on that with <code>handler-bind</code>),
we’ll get those two new choices at the top of the debugger:</p>
<p><img src="simple-restarts.png" alt="" /></p>
<p>That’s allright but let’s just write more human-friendy “reportsâ€<C3A2>:</p>
<pre><code class="language-lisp">(defun divide-with-restarts (x y)
(restart-case (/ x y)
(return-zero ()
:report "Return 0" ;; &lt;-- added
0)
(divide-by-one ()
:report "Divide by 1"
(/ x 1))))
(divide-with-restarts 3 0)
;; Nicer restarts:
;; 0: [RETURN-ZERO] Return 0
;; 1: [DIVIDE-BY-ONE] Divide by 1
</code></pre>
<p>That’s better, but we lack the ability to change an operand, as we did
with the <code>assert</code> example above.</p>
<h3 id="changing-a-variable-with-restarts">Changing a variable with restarts</h3>
<p>The two restarts we defined didn’t ask for a new value. To do this, we
add an <code>:interactive</code> lambda function to the restart, that asks for
the user a new value with the input method of its choice. Here, we’ll
use the regular <code>read</code>.</p>
<pre><code class="language-lisp">(defun divide-with-restarts (x y)
(restart-case (/ x y)
(return-zero ()
:report "Return 0"
0)
(divide-by-one ()
:report "Divide by 1"
(/ x 1))
(set-new-divisor (value)
:report "Enter a new divisor"
;;
;; Ask the user for a new value:
:interactive (lambda () (prompt-new-value "Please enter a new divisor: "))
;;
;; and call the divide function with the new value…
;; … possibly catching bad input again!
(divide-with-restarts x value))))
(defun prompt-new-value (prompt)
(format *query-io* prompt) ;; *query-io*: the special stream to make user queries.
(force-output *query-io*) ;; Ensure the user sees what he types.
(list (read *query-io*))) ;; We must return a list.
(divide-with-restarts 3 0)
</code></pre>
<p>When calling it, we are offered a new restart, we enter a new value,
and we get our result:</p>
<pre><code>(divide-with-restarts 3 0)
;; Debugger:
;;
;; 2: [SET-NEW-DIVISOR] Enter a new divisor
;;
;; Please enter a new divisor: 10
;;
;; 3/10
</code></pre>
<p>Oh, you prefer a graphical user interface? We can use the <code>zenity</code>
command line interface on GNU/Linux.</p>
<pre><code class="language-lisp">(defun prompt-new-value (prompt)
(list
(let ((input
;; We capture the program's output to a string.
(with-output-to-string (s)
(let* ((*standard-output* s))
(uiop:run-program `("zenity"
"--forms"
,(format nil "--add-entry=~a" prompt))
:output s)))))
;; We get a string and we want a number.
;; We could also use parse-integer, the parse-number library, etc.
(read-from-string input))))
</code></pre>
<p>Now try again and you should get a little window asking for a new number:</p>
<p><img src="assets/zenity-prompt.png" alt="" /></p>
<p>That’s fun, but that’s not all. Choosing restarts manually is not always (or
often?) satisfactory. And by <em>handling</em> restarts we can start over the operation
as if the error didn’t occur, as seen in the stack.</p>
<h3 id="calling-restarts-programmatically-handler-bind-invoke-restart">Calling restarts programmatically (handler-bind, invoke-restart)</h3>
<p>We have a piece of code that we know can throw conditions. Here,
<code>divide-with-restarts</code> can signal an error about a division by
zero. What we want to do, is our higher-level code to automatically
handle it and call the appropriate restart.</p>
<p>We can do this with <code>handler-bind</code> and <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_invo_1.htm#invoke-restart">invoke-restart</a>:</p>
<pre><code class="language-lisp">(defun divide-and-handle-error (x y)
(handler-bind
((division-by-zero (lambda (c)
(format t "Got error: ~a~%" c) ;; error-message
(format t "and will divide by 1~&amp;")
(invoke-restart 'divide-by-one))))
(divide-with-restarts x y)))
(divide-and-handle-error 3 0)
;; Got error: arithmetic error DIVISION-BY-ZERO signalled
;; Operation was (/ 3 0).
;; and will divide by 1
;; 3
</code></pre>
<h3 id="using-other-restarts-find-restart">Using other restarts (find-restart)</h3>
<p>Use <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_find_r.htm#find-restart">find-restart</a>.</p>
<p><code>find-restart 'name-of-restart</code> will return the most recent bound
restart with the given name, or <code>nil</code>.</p>
<h3 id="hiding-and-showing-restarts">Hiding and showing restarts</h3>
<p>Restarts can be hidden. In <code>restart-case</code>, in addition to <code>:report</code>
and <code>:interactive</code>, they also accept a <code>:test</code> key:</p>
<pre><code class="language-lisp">(restart-case
(return-zero ()
:test (lambda ()
(some-test))
...
</code></pre>
<h2 id="handling-conditions-handler-bind">Handling conditions (handler-bind)</h2>
<p>We just saw a use for <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_handle.htm">handler-bind</a>.</p>
<p>Its general form is:</p>
<pre><code class="language-lisp">(handler-bind ((a-condition #'function-to-handle-it)
(another-one #'another-function))
(code that can...)
(...error out))
</code></pre>
<p>We can study a real example with the
<a href="https://github.com/mrkkrp/unix-opts"><code>unix-opts</code></a> library, that
parses command line arguments. It defined some conditions:
<code>unknown-option</code>, <code>missing-arg</code> and <code>arg-parser-failed</code>, and it is up
to us to write what to do in these cases.</p>
<pre><code class="language-lisp">(handler-bind ((opts:unknown-option #'unknown-option)
(opts:missing-arg #'missing-arg)
(opts:arg-parser-failed #'arg-parser-failed))
(opts:get-opts))
</code></pre>
<p>Our <code>unknown-option</code> function is simple and looks like this:</p>
<pre><code class="language-lisp">(defun unknown-option (condition)
(format t "~s option is unknown.~%" (opts:option condition))
(opts:describe)
(exit)) ;; &lt;-- we return to the command line, no debugger.
</code></pre>
<p>it takes the condition as parameter, so we can read information from
it if needed. Here we get the name of the erroneous option with the
condition’s reader <code>(opts:option condition)</code>.</p>
<h2 id="running-some-code-condition-or-not-finally-unwind-protect">Running some code, condition or not (“finallyâ€<C3A2>) (unwind-protect)</h2>
<p>The “finallyâ€<C3A2> part of others <code>try/catch/finally</code> forms is done with <a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_unwind.htm">unwind-protect</a>.</p>
<p>It is the construct used in “with-“ macros, like <code>with-open-file</code>,
which always closes the file after it.</p>
<p>With this example:</p>
<pre><code class="language-lisp">(unwind-protect (/ 3 0)
(format t "This place is safe.~&amp;"))
</code></pre>
<p>We <em>do</em> get the interactive debugger (we didn’t use handler-bind or
anything), but our message is printed afterwards anyway.</p>
<h2 id="conclusion">Conclusion</h2>
<p>You’re now more than ready to write some code and to dive into other resources!</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="http://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html">Practical Common Lisp: “Beyond Exception Handling: Conditions and Restartsâ€<C3A2></a> - the go-to tutorial, more explanations and primitives.</li>
<li>Common Lisp Recipes, chap. 12, by E. Weitz</li>
<li><a href="https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node317.html">language reference</a></li>
<li><a href="http://nklein.com/2011/03/tutorial-introduction-to-conditions-and-restarts/">Video tutorial: introduction on conditions and restarts</a>, by Patrick Stein.</li>
<li><a href="http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html">Condition Handling in the Lisp family of languages</a></li>
<li><a href="https://z0ltan.wordpress.com/2016/08/06/conditions-and-restarts-in-common-lisp/">z0ltan.wordpress.com</a> (the article this recipe is heavily based upon)</li>
</ul>
<h2 id="see-also">See also</h2>
<ul>
<li><a href="http://jacek.zlydach.pl/blog/2019-07-24-algebraic-effects-you-can-touch-this.html">Algebraic effects - You can touch this !</a> - how to use conditions and restarts to implement progress reporting and aborting of a long-running calculation, possibly in an interactive or GUI context.</li>
<li><a href="https://github.com/stylewarning/lisp-random/blob/master/talks/4may19/root.lisp">A tutorial on conditions and restarts</a>, based around computing the roots of a real function. It was presented by the author at a Bay Area Julia meetup on may 2019 (<a href="https://github.com/stylewarning/talks/blob/master/4may19-julia-meetup/Bay%20Area%20Julia%20Users%20Meetup%20-%204%20May%202019.pdf">talk slides here</a>).</li>
<li><a href="https://lisper.in/restarts#signaling-validation-errors">lisper.in</a> - example with parsing a csv file and using restarts with success, <a href="https://www.reddit.com/r/lisp/comments/7k85sf/a_tutorial_on_conditions_and_restarts/drceozm/">in a flight travel company</a>.</li>
<li><a href="https://github.com/svetlyak40wt/python-cl-conditions">https://github.com/svetlyak40wt/python-cl-conditions</a> - implementation of the CL conditions system in Python.</li>
</ul>
<p class="page-source">
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/error_handling.md">error_handling.md</a>
</p>
</div>
<script type="text/javascript">
// Don't write the TOC on the index.
if (window.location.pathname != "/cl-cookbook/") {
$("#toc").toc({
content: "#content", // will ignore the first h1 with the site+page title.
headings: "h1,h2,h3,h4"});
}
$("#two-cols + ul").css({
"column-count": "2",
});
$("#contributors + ul").css({
"column-count": "4",
});
</script>
<div>
<footer class="footer">
<hr/>
&copy; 2002&ndash;2023 the Common Lisp Cookbook Project
<div>
📹 Discover <a style="color: darkgrey; text-decoration: underline", href="https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358">vindarel's Lisp course on Udemy</a>
</div>
</footer>
</div>
<div id="toc-btn">T<br>O<br>C</div>
</div>
<script text="javascript">
HighlightLisp.highlight_auto({className: null});
</script>
<script type="text/javascript">
function duckSearch() {
var searchField = document.getElementById("searchField");
if (searchField && searchField.value) {
var query = escape("site:lispcookbook.github.io/cl-cookbook/ " + searchField.value);
window.location.href = "https://duckduckgo.com/?kj=b2&kf=-1&ko=1&q=" + query;
// https://duckduckgo.com/params
// kj=b2: blue header in results page
// kf=-1: no favicons
}
}
</script>
<script async defer data-domain="lispcookbook.github.io/cl-cookbook" src="https://plausible.io/js/plausible.js"></script>
</body>
</html>