1
0
Fork 0
cl-sites/lispcookbook.github.io/cl-cookbook/type.html

683 lines
26 KiB
HTML
Raw Normal View History

2023-10-25 11:23:21 +02:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 for Linux version 5.2.0">
<title>Type System</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; Type System</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; Type System</h1>
<!-- Announcement we can keep for 1 month or more. I remove it and re-add it from time to time. -->
2024-05-15 18:18:38 +02:00
<!-- <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>
2023-10-25 11:23:21 +02:00
<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 a complete and flexible type system and corresponding
tools to inspect, check and manipulate types. It allows creating
custom types, adding type declarations to variables and functions and
thus to get compile-time warnings and errors.</p>
<h2 id="values-have-types-not-variables">Values Have Types, Not Variables</h2>
<p>Being different from some languages such as C/C++, variables in Lisp are just
<em>placeholders</em> for objects<sup id="fnref:1" role="doc-noteref"><a href="type.html#fn:1" class="footnote" rel="footnote">1</a></sup>. When you <a href="http://www.lispworks.com/documentation/lw50/CLHS/Body/m_setf_.htm"><code>setf</code></a> a variable, an object
is “placed” in it. You can place another value to the same variable later, as
you wish.</p>
<p>This implies a fact that in Common Lisp <strong>objects have types</strong>, while
variables do not. This might be surprising at first if you come from a C/C++
background.</p>
<p>For example:</p>
<pre><code class="language-lisp">(defvar *var* 1234)
*VAR*
(type-of *var*)
(INTEGER 0 4611686018427387903)
</code></pre>
<p>The function <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_tp_of.htm"><code>type-of</code></a> returns the type of the given object. The
returned result is a <a href="http://www.lispworks.com/documentation/lw51/CLHS/Body/04_bc.htm">type-specifier</a>. In this case the first
element is the type and the remaining part is extra information (lower and
upper bound) of that type. You can safely ignore it for now. Also remember
that integers in Lisp have no limit!</p>
<p>Now lets try to <a href="http://www.lispworks.com/documentation/lw50/CLHS/Body/m_setf_.htm"><code>setf</code></a> the variable:</p>
<pre><code class="language-lisp">* (setf *var* "hello")
"hello"
* (type-of *var*)
(SIMPLE-ARRAY CHARACTER (5))
</code></pre>
<p>You see, <code>type-of</code> returns a different result: <a href="http://www.lispworks.com/documentation/lw70/CLHS/Body/t_smp_ar.htm"><code>simple-array</code></a>
of length 5 with contents of type <a href="http://www.lispworks.com/documentation/lcl50/ics/ics-14.html"><code>character</code></a>. This is because
<code>*var*</code> is evaluated to string <code>"hello"</code> and the function <code>type-of</code> actually
returns the type of object <code>"hello"</code> instead of variable <code>*var*</code>.</p>
<h2 id="type-hierarchy">Type Hierarchy</h2>
<p>The inheritance relationship of Lisp types consists a type graph and the root
of all types is <code>T</code>. For example:</p>
<pre><code class="language-lisp">* (describe 'integer)
COMMON-LISP:INTEGER
[symbol]
INTEGER names the built-in-class #&lt;BUILT-IN-CLASS COMMON-LISP:INTEGER&gt;:
Class precedence-list: INTEGER, RATIONAL, REAL, NUMBER, T
Direct superclasses: RATIONAL
Direct subclasses: FIXNUM, BIGNUM
No direct slots.
INTEGER names a primitive type-specifier:
Lambda-list: (&amp;OPTIONAL (SB-KERNEL::LOW '*) (SB-KERNEL::HIGH '*))
</code></pre>
<p>The function <a href="http://www.lispworks.com/documentation/lw51/CLHS/Body/f_descri.htm"><code>describe</code></a> shows that the symbol <a href="http://www.lispworks.com/documentation/lw71/CLHS/Body/t_intege.htm"><code>integer</code></a>
is a primitive type-specifier that has optional information lower bound and
upper bound. Meanwhile, it is a built-in class. But why?</p>
<p>Most common Lisp types are implemented as CLOS classes. Some types are simply
“wrappers” of other types. Each CLOS class maps to a corresponding type. In
Lisp types are referred to indirectly by the use of <a href="http://www.lispworks.com/documentation/lw51/CLHS/Body/04_bc.htm"><code>type
specifiers</code></a>.</p>
<p>There are some differences between the function <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_tp_of.htm"><code>type-of</code></a> and
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_clas_1.htm"><code>class-of</code></a>. The function <code>type-of</code> returns the type of a given
object in type specifier format while <code>class-of</code> returns the implementation
details.</p>
<pre><code class="language-lisp">* (type-of 1234)
(INTEGER 0 4611686018427387903)
* (class-of 1234)
#&lt;BUILT-IN-CLASS COMMON-LISP:FIXNUM&gt;
</code></pre>
<h2 id="checking-types">Checking Types</h2>
<p>The function <a href="http://www.lispworks.com/documentation/lw51/CLHS/Body/f_typep.htm"><code>typep</code></a> can be used to check if the first argument is of
the given type specified by the second argument.</p>
<pre><code class="language-lisp">* (typep 1234 'integer)
T
</code></pre>
<p>The function <a href="http://www.lispworks.com/documentation/lw71/CLHS/Body/f_subtpp.htm"><code>subtypep</code></a> can be used to inspect if a type inherits
from the another one. It returns 2 values:</p>
<ul>
<li><code>T, T</code> means first argument is sub-type of the second one.</li>
<li><code>NIL, T</code> means first argument is <em>not</em> sub-type of the second one.</li>
<li><code>NIL, NIL</code> means “not determined”.</li>
</ul>
<p>For example:</p>
<pre><code class="language-lisp">* (subtypep 'integer 'number)
T
T
* (subtypep 'string 'number)
NIL
T
</code></pre>
<p>Sometimes you may want to perform different actions according to the type of
an argument. The macro <a href="http://www.lispworks.com/documentation/lw60/CLHS/Body/m_tpcase.htm"><code>typecase</code></a> is your friend:</p>
<pre><code class="language-lisp">* (defun plus1 (arg)
(typecase arg
(integer (+ arg 1))
(string (concatenate 'string arg "1"))
(t 'error)))
PLUS1
* (plus1 100)
101 (7 bits, #x65, #o145, #b1100101)
* (plus1 "hello")
"hello1"
* (plus1 'hello)
ERROR
</code></pre>
<h2 id="type-specifier">Type Specifier</h2>
<p>A type specifier is a form specifying a type. As mentioned above, returning
value of the function <code>type-of</code> and the second argument of <code>typep</code> are both
type specifiers.</p>
<p>As shown above, <code>(type-of 1234)</code> returns <code>(INTEGER 0
4611686018427387903)</code>. This kind of type specifiers are called compound type
specifier. It is a list whose head is a symbol indicating the type. The rest
part of it is complementary information.</p>
<pre><code class="language-lisp">* (typep '#(1 2 3) '(vector number 3))
T
</code></pre>
<p>Here the complementary information of the type <code>vector</code> is its elements type
and size respectively.</p>
<p>The rest part of a compound type specifier can be a <code>*</code>, which means
“anything”. For example, the type specifier <code>(vector number *)</code> denotes a
vector consisting of any number of numbers.</p>
<pre><code class="language-lisp">* (typep '#(1 2 3) '(vector number *))
T
</code></pre>
<p>The trailing parts can be omitted, the omitted elements are treated as
<code>*</code>s:</p>
<pre><code class="language-lisp">* (typep '#(1 2 3) '(vector number))
T
* (typep '#(1 2 3) '(vector))
T
</code></pre>
<p>As you may have guessed, the type specifier above can be shortened as
following:</p>
<pre><code class="language-lisp">* (typep '#(1 2 3) 'vector)
T
</code></pre>
<p>You may refer to the <a href="http://www.lispworks.com/documentation/lw51/CLHS/Body/04_bc.htm">CLHS page</a> for more information.</p>
<h2 id="defining-new-types">Defining New Types</h2>
<p>You can use the macro <a href="http://www.lispworks.com/documentation/lw51/CLHS/Body/m_deftp.htm"><code>deftype</code></a> to define a new type-specifier.</p>
<p>Its argument list can be understood as a direct mapping to elements of rest
part of a compound type specifier. They are defined as optional to allow
symbol type specifier.</p>
<p>Its body should be a macro checking whether given argument is of this type
(see <a href="http://www.lispworks.com/documentation/lw70/CLHS/Body/m_defmac.htm"><code>defmacro</code></a>).</p>
<p>We can use <code>member</code> to define enum types, for example:</p>
<pre><code class="language-lisp">(deftype fruit () '(member :apple :orange :pear))
</code></pre>
<p>Now let us define a new data type. The data type should be a array with at
most 10 elements. Also each element should be a number smaller than 10. See
following code for an example:</p>
<pre><code class="language-lisp">* (defun small-number-array-p (thing)
(and (arrayp thing)
(&lt;= (length thing) 10)
(every #'numberp thing)
(every (lambda (x) (&lt; x 10)) thing)))
* (deftype small-number-array (&amp;optional type)
`(and (array ,type 1)
(satisfies small-number-array-p)))
* (typep '#(1 2 3 4) '(small-number-array number))
T
* (typep '#(1 2 3 4) 'small-number-array)
T
* (typep '#(1 2 3 4 100) 'small-number-array)
NIL
* (small-number-array-p '#(1 2 3 4 5 6 7 8 9 0 1))
NIL
</code></pre>
<h2 id="run-time-type-checking">Run-time type Checking</h2>
<p>Common Lisp supports run-time type checking via the macro
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_check_.htm#check-type"><code>check-type</code></a>. It accepts a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#place"><code>place</code></a> and a type specifier
as arguments and signals an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_tp_err.htm#type-error"><code>type-error</code></a> if the contents of
place are not of the given type.</p>
<pre><code class="language-lisp">* (defun plus1 (arg)
(check-type arg number)
(1+ arg))
PLUS1
* (plus1 1)
2 (2 bits, #x2, #o2, #b10)
* (plus1 "hello")
; Debugger entered on #&lt;SIMPLE-TYPE-ERROR expected-type: NUMBER datum: "Hello"&gt;
The value of ARG is "Hello", which is not of type NUMBER.
[Condition of type SIMPLE-TYPE-ERROR]
...
</code></pre>
<h2 id="compile-time-type-checking">Compile-time type checking</h2>
<p>You may provide type information for variables, function arguments
etc via <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_procla.htm"><code>proclaim</code></a>, <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_declai.htm"><code>declaim</code></a> (at the toplevel) and <a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_declar.htm"><code>declare</code></a> (inside functions and macros).</p>
<p>However, similar to the <code>:type</code> slot
introduced in <a href="clos.html">CLOS section</a>, the effects of type declarations are
undefined in Lisp standard and are implementation specific. So there is no
guarantee that the Lisp compiler will perform compile-time type checking.</p>
<p>However, it is possible, and SBCL is an implementation that does
thorough type checking.</p>
<p>Lets recall first that Lisp already warns about simple type
warnings. The following function wrongly wants to concatenate a string
and a number. When we compile it, we get a type warning.</p>
<pre><code class="language-lisp">(defconstant +foo+ 3)
(defun bar ()
(concatenate 'string "+" +foo+))
; caught WARNING:
; Constant 3 conflicts with its asserted type SEQUENCE.
; See also:
; The SBCL Manual, Node "Handling of Types"
</code></pre>
<p>The example is simple, but it already shows a capacity some other
languages dont have, and it is actually useful during development ;)
Now, well do better.</p>
<h3 id="declaring-the-type-of-variables">Declaring the type of variables</h3>
<p>Use the macro <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_declai.htm"><code>declaim</code></a> with a <code>type</code> declaration identifier (other identifiers are “ftype, inline, notinline, optimize…).</p>
<p>Lets declare that our global variable <code>*name*</code> is a string. You can
type the following in any order in the REPL:</p>
<pre><code class="language-lisp">(declaim (type (string) *name*))
(defparameter *name* "book")
</code></pre>
<p>Now if we try to set it with a bad type, we get a <code>simple-type-error</code>:</p>
<pre><code class="language-lisp">(setf *name* :me)
Value of :ME in (THE STRING :ME) is :ME, not a STRING.
[Condition of type SIMPLE-TYPE-ERROR]
</code></pre>
<p>We can do the same with our custom types. Lets quickly declare the type <code>list-of-strings</code>:</p>
<pre><code class="language-lisp">(defun list-of-strings-p (list)
"Return t if LIST is non nil and contains only strings."
(and (consp list)
(every #'stringp list)))
(deftype list-of-strings ()
`(satisfies list-of-strings-p))
</code></pre>
<p>Now lets declare that our <code>*all-names*</code> variables is a list of strings:</p>
<pre><code class="language-lisp">(declaim (type (list-of-strings) *all-names*))
;; and with a wrong value:
(defparameter *all-names* "")
;; we get an error, still at compile-time:
Cannot set SYMBOL-VALUE of *ALL-NAMES* to "", not of type
(SATISFIES LIST-OF-STRINGS-P).
[Condition of type SIMPLE-TYPE-ERROR]
</code></pre>
<h3 id="composing-types">Composing types</h3>
<p>We can compose types. Following the previous example:</p>
<pre><code class="language-lisp">(declaim (type (or null list-of-strings) *all-names*))
</code></pre>
<h3 id="declaring-the-input-and-output-types-of-functions">Declaring the input and output types of functions</h3>
<p>We use again the <code>declaim</code> macro, with <code>ftype (function …)</code> instead of just <code>type</code>:</p>
<pre><code class="language-lisp">(declaim (ftype (function (fixnum) fixnum) add))
;; ^^input ^^output [optional]
(defun add (n)
(+ n 1))
</code></pre>
<p>With this we get nice type warnings at compile time.</p>
<p>If we change the function to erroneously return a string instead of a
fixnum, we get a warning:</p>
<pre><code class="language-lisp">(defun add (n)
(format nil "~a" (+ n 1)))
; caught WARNING:
; Derived type of ((GET-OUTPUT-STREAM-STRING STREAM)) is
; (VALUES SIMPLE-STRING &amp;OPTIONAL),
; conflicting with the declared function return type
; (VALUES FIXNUM &amp;REST T).
</code></pre>
<p>If we use <code>add</code> inside another function, to a place that expects a
string, we get a warning:</p>
<pre><code class="language-lisp">(defun bad-concat (n)
(concatenate 'string (add n)))
; caught WARNING:
; Derived type of (ADD N) is
; (VALUES FIXNUM &amp;REST T),
; conflicting with its asserted type
; SEQUENCE.
</code></pre>
<p>If we use <code>add</code> inside another function, and that function declares
its argument types which appear to be incompatible with those of
<code>add</code>, we get a warning:</p>
<pre><code class="language-lisp">(declaim (ftype (function (string)) bad-arg))
(defun bad-arg (n)
(add n))
; caught WARNING:
; Derived type of N is
; (VALUES STRING &amp;OPTIONAL),
; conflicting with its asserted type
; FIXNUM.
</code></pre>
<p>This all happens indeed <em>at compile time</em>, either in the REPL,
either with a simple <code>C-c C-c</code> in Slime, or when we <code>load</code> a file.</p>
<h3 id="declaring-key-parameters">Declaring &amp;key parameters</h3>
<p>Use <code>&amp;key (:argument type)</code>.</p>
<p>For example:</p>
<pre><code>(declaim (ftype (function (string &amp;key (:n integer))) foo))
(defun foo (bar &amp;key n) …)
</code></pre>
<h3 id="declaring-rest-parameters">Declaring &amp;rest parameters</h3>
<p>This is less evident, you might need a well-placed <code>declare</code>.</p>
<p>In the following, we declare a fruit type and we write a function that
uses a single fruit argument, so compiling <code>placing-order</code> gives us a
type warning as expected:</p>
<pre><code class="language-lisp">(deftype fruit () '(member :apple :orange :pear))
(declaim (ftype (function (fruit)) one-order))
(defun one-order (fruit)
(format t "Ordering ~S~%" fruit))
(defun placing-order ()
(one-order :bacon))
</code></pre>
<p>But in this version, we use <code>&amp;rest</code> parameters, and we dont have a type warning anymore:</p>
<pre><code class="language-lisp">(declaim (ftype (function (&amp;rest fruit)) place-order))
(defun place-order (&amp;rest selections)
(dolist (s selections)
(format t "Ordering ~S~%" s)))
(defun placing-orders ()
(place-order :orange :apple :bacon)) ;; =&gt; no type warning
</code></pre>
<p>The declaration is correct, but our compiler doesnt check it. A well-placed <code>declare</code> gives us the compile-time warning back:</p>
<pre><code class="language-lisp">(defun place-order (&amp;rest selections)
(dolist (s selections)
(declare (type fruit s)) ;; &lt;= declare
(format t "Ordering ~S~%" s)))
(defun placing-orders ()
(place-order :orange :apple :bacon))
</code></pre>
<p>=&gt;</p>
<pre><code>The value
:BACON
is not of type
(MEMBER :PEAR :ORANGE :APPLE)
</code></pre>
<p>For portable code, we would add run-time checks with an <code>assert</code>.</p>
<h3 id="declaring-class-slots-types">Declaring class slots types</h3>
<p>A class slot accepts a <code>:type</code> slot option. It is however generally
<em>not</em> used to check the type of the initform. SBCL, starting with
<a href="http://www.sbcl.org/all-news.html#1.5.9">version 1.5.9</a> released on
november 2019, now gives those warnings, meaning that this:</p>
<pre><code class="language-lisp">(defclass foo ()
((name :type number :initform "17")))
</code></pre>
<p>throws a warning at compile time.</p>
<p>Note: see also <a href="https://github.com/fisxoj/sanity-clause">sanity-clause</a>, a data
serialization/contract library to check slots types during
<code>make-instance</code> (which is not compile time).</p>
<h3 id="alternative-type-checking-syntax-defstar-serapeum">Alternative type checking syntax: defstar, serapeum</h3>
<p>The <a href="https://github.com/ruricolist/serapeum/blob/master/REFERENCE.md#types">Serapeum</a> library provides a shortcut that looks like this:</p>
<pre><code class="language-lisp"> (-&gt; mod-fixnum+ (fixnum fixnum) fixnum)
(defun mod-fixnum+ (x y) ...)
</code></pre>
<p>The <a href="https://github.com/lisp-mirror/defstar">Defstar</a> library provides
a <code>defun*</code> macro that allows to add the type declarations into the
lambda list. It looks like this:</p>
<pre><code class="language-lisp">(defun* sum ((a real) (b real))
(+ a b))
</code></pre>
<p>It also allows:</p>
<ul>
<li>to declare the return type, either in the function definition or in its body</li>
<li>to quickly declare variables that are ignored, with the <code>_</code> placeholder</li>
<li>to add assertions for each arguments</li>
<li>to do the same with <code>defmethod</code>, <code>defparameter</code>, <code>defvar</code>, <code>flet</code>, <code>labels</code>, <code>let*</code> and <code>lambda</code>.</li>
</ul>
<h3 id="limitations">Limitations</h3>
<p>Complex types involving <code>satisfies</code> are not checked inside a function
body by default, only at its boundaries. Even if it does a lot, SBCL doesnt do
as much as a statically typed language.</p>
<p>Consider this example, where we badly increment an integer with a
string:</p>
<pre><code class="language-lisp">(declaim (ftype (function () string) bad-adder))
(defun bad-adder ()
(let ((res 10))
(loop for name in '("alice")
do (incf res name)) ;; &lt;= bad
(format nil "finally doing sth with ~a" res)))
</code></pre>
<p>Compiling this function doesnt throw a type warning.</p>
<p>However, if we had the problematic line at the functions boundary
wed get the warning:</p>
<pre><code class="language-lisp">(defun bad-adder ()
(let ((res 10))
(loop for name in '("alice")
return (incf res name))))
; in: DEFUN BAD-ADDER
; (SB-INT:NAMED-LAMBDA BAD-ADDER
; NIL
; (BLOCK BAD-ADDER
; (LET ((RES 10))
; (LOOP FOR NAME IN *ALL-NAMES* RETURN (INCF RES NAME)))))
;
; caught WARNING:
; Derived type of ("a hairy form" NIL (SETQ RES (+ NAME RES))) is
; (VALUES (OR NULL NUMBER) &amp;OPTIONAL),
; conflicting with the declared function return type
; (VALUES STRING &amp;REST T).
</code></pre>
<p>We could also use a <code>the</code> declaration in the loop body to get a compile-time warning:</p>
<pre><code class="language-lisp"> do (incf res (the string name)))
</code></pre>
<p>What can we conclude? This is yet another reason to decompose your
code into small functions.</p>
<h2 id="see-also">See also</h2>
<ul>
<li>the article <a href="https://medium.com/@MartinCracauer/static-type-checking-in-the-programmable-programming-language-lisp-79bb79eb068a">Static type checking in SBCL</a>, by Martin Cracauer</li>
<li>the article <a href="https://alhassy.github.io/TypedLisp">Typed List, a Primer</a> - lets explore Lisps fine-grained type hierarchy! with a shallow comparison to Haskell.</li>
<li>the <a href="https://github.com/coalton-lang/coalton/">Coalton</a> library: an
efficient, statically typed functional programming language that
supercharges Common Lisp. It is as an embedded DSL in Lisp that
resembles Haskell or Standard ML, but lets you seamlessly
interoperate with non-statically-typed Lisp code (and vice versa).</li>
<li><a href="https://dev.to/vindarel/compile-time-exhaustiveness-checking-in-common-lisp-with-serapeum-5c5i">exhaustiveness type checking at compile-time</a> with <a href="https://github.com/ruricolist/serapeum/blob/master/REFERENCE.md#ecase-of-type-x-body-body">Serapeum</a> for enum types and union types (ecase-of, etypecase-of).</li>
</ul>
<hr />
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>The term <em>object</em> here has nothing to do with Object-Oriented or so. It
means “any Lisp datum”. <a href="type.html#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
</li>
</ol>
</div>
<p class="page-source">
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/type.md">type.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>
2024-05-15 18:18:38 +02:00
📹 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>
2023-10-25 11:23:21 +02:00
</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>