693 lines
28 KiB
HTML
693 lines
28 KiB
HTML
<!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> – 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> – 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=LISPY-XMAS2023" 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 our contributor's Lisp course with this Christmas 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">
|
||
📢 New videos: <a href="https://www.youtube.com/watch?v=h_noB1sI_e8">web dev demo part 1</a>, <a href="https://www.youtube.com/watch?v=xnwc7irnc8k">dynamic page with HTMX</a>, <a href="https://www.youtube.com/watch?v=Zpn86AQRVN8">Weblocks demo</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” 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
|
||
#<DIVISION-BY-ZERO {1008FF5F13}>
|
||
</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.~&")
|
||
(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
|
||
#<DIVISION-BY-ZERO {1004846AE3}>
|
||
</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) ;; <-- 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” 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)
|
||
;; #<MY-DIVISION-BY-ZERO {1005A5FE43}>
|
||
</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)) ;; <-- 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” or “throw” 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)
|
||
;; #<MY-DIVISION-BY-ZERO {1005C18653}>
|
||
</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”
|
||
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)
|
||
;; #<MY-DIVISION-BY-ZERO {1005C18653}>
|
||
(dividend *)
|
||
;; 3
|
||
</code></pre>
|
||
|
||
<p>an “:accessor” 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” 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.~&"
|
||
(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” 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. <--- 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 () ;; <-- 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”:</p>
|
||
|
||
<pre><code class="language-lisp">(defun divide-with-restarts (x y)
|
||
(restart-case (/ x y)
|
||
(return-zero ()
|
||
:report "Return 0" ;; <-- 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~&")
|
||
(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>If the handler returns normally (it declines to handle the condition),
|
||
the condition continues to bubble up, searching for another handler,
|
||
and it will find the interactive debugger (when it’s an error, not
|
||
when it’s a simple condition).</p>
|
||
|
||
<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)) ;; <-- 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”) (unwind-protect)</h2>
|
||
|
||
<p>The “finally” 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.~&"))
|
||
</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”</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/>
|
||
© 2002–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">our contributor's Common Lisp video 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>
|