Nicolas Martyanoff – Brain dumpBrain dumpen-us[email protected] (Nicolas Martyanoff)[email protected] (Nicolas Martyanoff)
https://www.n16f.net/tags/lisp/index.xml
Sun, 19 Nov 2023 00:00:00 +0000Interactive Common Lisp development
https://www.n16f.net/blog/interactive-common-lisp-development/
Sun, 19 Nov 2023 18:00:00 +0000https://www.n16f.net/blog/interactive-common-lisp-development/[email protected] (Nicolas Martyanoff)<p>Common Lisp programming is often presented as “interactive”. In most
languages, modifications to your program are applied by recompiling it and
restarting it. In contrast, Common Lisp lets you incrementally modify your
program while it is running.</p>
<p>While this approach is convenient, especially for exploratory programming, it
also means that the state of your program during execution does not always
reflect the source code. You do not just define new constructs: you look them
up, inspect them, modify them or delete them. I had to learn a lot of
subtleties the hard way. This article is a compendium of information related
to the interactive nature of Common Lisp.</p>
<h2 id="variables">Variables</h2>
<p>In Common Lisp variables are identified by symbols. Evaluating <code>(SETQ A 42)</code>
creates or updates a variable with the integer <code>42</code> as value, and associates
it to the <code>A</code> symbol. After the call to <code>SETQ</code>, <code>(BOUNDP 'A)</code> will return <code>T</code>
and <code>(SYMBOL-VALUE 'A)</code> will return <code>42</code>.</p>
<p>You do not delete a variable: instead, you remove the association between the
symbol and the variable. You do so with <code>MAKUNBOUND</code>. Following the previous
example, <code>(MAKUNBOUND 'A)</code> will remove the association between the <code>A</code> symbol
and the variable. And <code>(BOUNDP 'A)</code> returns <code>NIL</code> as expected. As for
<code>(SYMBOL-VALUE 'A)</code>, it now signals an <code>UNBOUND-VARIABLE</code> error as mandated by
the standard.</p>
<p>What about <code>DEFVAR</code> and <code>DEFPARAMETER</code>? They are also used to declare
variables (globally defined ones), associating them with symbols. Both define
“special” variables (i.e. variables for which all bindings are dynamic; see
CLtL2 9.2). The difference is that the initial value passed to <code>DEFVAR</code> is not
evaluated if it already has a value. <code>MAKUNBOUND</code> will work on variables
declared with <code>DEFVAR</code> or <code>DEFPARAMETER</code> as expected.</p>
<p><code>DEFCONSTANT</code> is a bit more complicated. CLtL2<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> 5.3.2 states that “once a
name has been declared by defconstant to be constant, any further assignment
to or binding of that special variable is an error”, but does not clearly
define whether <code>MAKUNBOUND</code> should or should not be able to be used on
constants. However, CLtL2 5.3.2 also states that “defconstant […] does assert
that the value of the variable name is fixed and does license the compiler to
build assumptions about the value into programs being compiled”. If the
compiler is allowed to rely on the value associated with the variable name, it
would make sense not to allow the deletion of the binding. Thus it is
recommended to only use constants for values that are guaranteed to never
change, e.g. mathematical constants. Most of the time you want <code>DEFPARAMETER</code>.</p>
<p>Note that <code>MAKUNBOUND</code> does not apply to lexical variables.</p>
<h2 id="functions">Functions</h2>
<p>Common Lisp is a Lisp-2, meaning that variables and functions are part of two
separate namespaces. Despite this clear separation, functions behave similarly
to variables.</p>
<p>Using <code>DEFUN</code> will either create or update the global function associated with
a symbol. <code>SYMBOL-FUNCTION</code> returns the globally defined function associated
with a symbol, and <code>FMAKUNBOUND</code> deletes this association.</p>
<p>Let us point out a common mistake when referencing functions: <code>(QUOTE F)</code>
(abbreviated as <code>'F</code>) yields a symbol while <code>(FUNCTION F)</code> (abbreviated as
<code>#'F</code>) yields a function. The function argument of <code>FUNCALL</code> and <code>APPLY</code> can
be either a symbol or a function (see CLtL2 7.3) It has two consequences:</p>
<p>First, one can write a function referencing <code>F</code> as <code>(QUOTE F)</code> with the
expectation that <code>F</code> will later be bound to a function. The following function
definition is perfectly valid even though <code>F</code> has not been defined yet:</p>
<pre><code class="language-lisp">(defun foo (a b)
(funcall 'f a b))
</code></pre>
<p>Second, redefining the <code>F</code> function will update its association (or binding)
to the <code>F</code> symbol, but the previous function will still be available if it has
been referenced somewhere before the update. For example:</p>
<pre><code class="language-lisp">(setf (symbol-function 'foo) #'1+)
(let ((old-foo #'foo))
(setf (symbol-function 'foo) #'1-)
(funcall old-foo 42))
</code></pre>
<p>What about macros? Since macros are a specific kind of functions (CLtL2 5.1.4
“a macro is essentially a function from forms to forms”), it is not surprising
that they share the same namespace and can be manipulated in the same way as
functions with <code>FBOUNDP</code>, <code>SYMBOL-FUNCTION</code> and <code>FMAKUNBOUND</code>.</p>
<h2 id="symbols-and-packages">Symbols and packages</h2>
<p>While functions and variables are familiar concepts to developers, Common Lisp
symbols and packages are a bit more peculiar.</p>
<p>A symbol is <em>interned</em> when it is part of a package. The most explicit way to
create an interned symbol is to use <code>INTERN</code>, e.g. <code>(INTERN "FOO")</code>. <code>INTERN</code>
interns the symbol in the current package by default, but one can pass a
package as second argument. After that, <code>(FIND-SYMBOL "FOO")</code> will return our
interned symbol as expected.</p>
<p>More surprising, the reader automatically interns symbols. You can test it by
evaluating <code>(READ-FROM-STRING "BAR")</code>. After evaluation, <code>BAR</code> is a symbol
interned in the current package. This also means that it is very easy to pollute
a package with symbols in ways you did not necessarily expect. To clean up,
simply use <code>UNINTERN</code>. Remember to refer to the right symbol: to remove the
symbol <code>BAR</code> from the package <code>FOO</code>, use <code>(UNINTERN 'FOO::BAR "BAR")</code>.</p>
<p>A symbol is either internal or external. <code>EXPORT</code> will make a symbol external
to its package while <code>UNEXPORT</code> will make it internal. As for <code>UNINTERN</code>,
confusion usually arises around which symbol is affected. <code>(UNEXPORT 'FOO:BAR "FOO")</code> correctly refers to the external symbol in the <code>FOO</code> package and makes
it internal again. <code>(UNEXPORT 'BAR "FOO")</code> will signal an error since the
<code>BAR</code> symbol is not part of the <code>FOO</code> package (unless of course the current
package happens to be <code>FOO</code>).</p>
<p>Packages themselves can be created with <code>MAKE-PACKAGE</code> and destroyed with
<code>DELETE-PACKAGE</code>. Developers are usually more familiar with <code>DEFPACKAGE</code>, a
macro allowing the creation of a package and its configuration (package use
list, imported and exported symbols, etc.) in a declarative way. A surprising
and frustrating behavior is that evaluating a <code>DEFPACKAGE</code> form for a package
that already exists will result in undefined behavior if the new declaration
“is not consistent” (CLtL2 11.7) with the current state of the package. As an
example, adding symbols to the export list is perfectly fine. Removing one
will result in undefined behavior (usually an error) due to the inconsistency
of the export list. Fortunately, Common Lisp offers all the necessary
functions to manipulate packages and their symbols: use them!</p>
<h2 id="classes">Classes</h2>
<p>The Common Lisp standard includes CLOS, the Common Lisp Object System.
Unsurprisingly it provides multiple ways to interact with classes and objects
dynamically.</p>
<p>As variables or functions, classes are identified by symbols and <code>FIND-CLASS</code>
returns the class associated with a symbol. Class names are part of a separate
namespace shared with structures and types.</p>
<p>The <code>DEFCLASS</code> macro is the only way to define or redefine a class. Redefining
a class means that instances created afterward with <code>MAKE-INSTANCE</code> will use
the new definition. Existing instances are updated: newly added slots are
added (either unbound or using the value associated with <code>:INITFORM</code>) and
slots that are not defined anymore are deleted.
<code>UPDATE-INSTANCE-FOR-REDEFINED-CLASS</code> is particularly interesting: developers
can define methods for this generic function in order to control how instances
are updated when their class is redefined.</p>
<p>Defining classes may imply implicitly defining methods: the <code>:ACCESSOR</code>,
<code>:READER</code> and <code>:WRITER</code> slot keyword arguments will lead to the creation of
generic functions. When a class is redefined, methods associated with slots
that have been removed will live on.</p>
<p>A limitation of CLOS is that classes cannot be deleted. <code>FIND-CLASS</code> can be
used as a <em>place</em>, and <code>(SETF (FIND-CLASS 'FOO) NIL)</code> will remove the
association between the <code>FOO</code> symbol and the class, but the class itself and
its instances will not disappear. While this limitation may seem strange, ask
yourself how an implementation should handle instances of a class that has
been deleted.</p>
<p>The class of an instance can be changed with <code>CHANGE-CLASS</code>: slots that exist
in the new class will be conserved while those that do not are deleted. New
slots are either unbound or set to the value associated with <code>:INITFORM</code> in
the new class. In a way similar to <code>UPDATE-INSTANCE-FOR-REDEFINED-CLASS</code>,
<code>UPDATE-INSTANCE-FOR-DIFFERENT-CLASS</code> lets developers control precisely the
process.</p>
<h3 id="generics-and-methods">Generics and methods</h3>
<p>Generics are functions which can be specialized based on the class (and not
type as one could expect) of their arguments and which can have a method
combination type.</p>
<p>Generics can be created explicitly with <code>DEFGENERIC</code> or implicitly when
<code>DEFMETHOD</code> is called and the list of parameter specializers and method
combination does not match any existing generic function. Since generics are
functions, <code>FBOUNDP</code>, <code>SYMBOL-FUNCTION</code> and <code>FMAKUNBOUND</code> will work as
expected.</p>
<p>Methods themselves are either defined as part of the <code>DEFGENERIC</code> call or
separately with <code>DEFMETHOD</code>. Discovering the different methods associated with
a generic function is a bit more complicated. There is no standard way to list
the methods associated with a generic, but it is at least possible to look up
a method with <code>FIND-METHOD</code>. Do remember to pass a function (and not a symbol)
as the generic, and to pass classes (and not symbols naming classes) in the
list of specializers.</p>
<p>Redefinition is not as obvious as for non-generic functions. When redefining a
generic with <code>DEFGENERIC</code> all methods defined as part of the previous
<code>DEFGENERIC</code> form are removed and methods defined in the redefinition are
added. However, methods defined separately with <code>DEFMETHOD</code> are not affected.</p>
<p>For example, in the following code, the second call to <code>DEFGENERIC</code> will
replace the two methods specialized on <code>INTEGER</code> and <code>FLOAT</code> respectively by a
single one specialized on a <code>STREAM</code>, but the method specialized on <code>STRING</code>
will remain unaffected.</p>
<pre><code class="language-lisp">(defgeneric foo (a)
(:method ((a integer))
(format nil "~A is an integer" a))
(:method ((a float))
(format nil "~A is a float" a)))
(defmethod foo ((a string))
(format nil "~S is a string" a))
(defgeneric foo (a)
(:method ((a stream))
(format nil "~A is a stream" a)))
</code></pre>
<p>Note that trying to redefine a generic with a different parameter lambda list
will cause the removal of all previously defined methods since none of them
can match the new parameters.</p>
<p>Removing a method will require you to find it first using <code>FIND-METHOD</code> and
then use <code>REMOVE-METHOD</code>. With the previous example, removing the method
specialized on a <code>STRING</code> argument is done with:</p>
<pre><code class="language-lisp">(remove-method #'foo (find-method #'foo nil (list (find-class 'string)) nil))
</code></pre>
<p>Working with methods is not always easy, and two errors are very common.</p>
<p>First, remember that changing the combinator in a <code>DEFMETHOD</code> will define a
new method. If you realize that your <code>:AFTER</code> method should use <code>:AROUND</code> and
reevaluate the <code>DEFMETHOD</code> form, remember to delete the method with the
<code>:AFTER</code> combinator or you will end up with two methods being called.</p>
<p>Second, when defining a method for a generic from another package, remember to
correctly refer to the generic. If you want to define a method on the <code>BAR</code>
generic from package <code>FOO</code>, use <code>(DEFMETHOD FOO:BAR (…) …)</code> and not
<code>(DEFMETHOD BAR (…) …)</code>. In the latter case, you will define a new <code>BAR</code>
generic in the current package.</p>
<h3 id="meta-object-protocol">Meta Object Protocol</h3>
<p>While CLOS is already quite powerful, various interactions are impossible. One
cannot create classes or methods programmatically, introspect classes or
instances for example to list their slots or obtain all their superclasses, or
list all the methods associated with a generic function.</p>
<p>In addition of an example of a CLOS implementation, The Art of the Metaobject
Protocol<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> defines multiple extensions to CLOS including metaclasses,
metaobjects, dynamic class and generic creation, class introspection and much
more.</p>
<p>Most Common Lisp implementations implement at least part of these extensions,
usually abbreviated as “MOP”, for “MetaObject Protocol”. The well-known
<a href="https://github.com/pcostanza/closer-mop">closer-mop</a> system can be used as a
compatibility layer for multiple implementations.</p>
<h3 id="structures">Structures</h3>
<p>Structures are record constructs defined with <code>DEFSTRUCT</code>. At a glance they
may seem very similar to classes, but they have a fundamental limitation:
the results of redefining a structure are undefined (CLtL2 19.2).</p>
<p>While this property allows implementations to handle structures in a more
efficient way than classes, it makes structures unsuitable for incremental
development. As such, they should only be used as a last resort, when a
regular class has been proved to be a performance bottleneck.</p>
<h3 id="conditions">Conditions</h3>
<p>While conditions look very similar to classes the Common Lisp standard does
not define them as classes. This is one of the few differences between the
standard and CLtL2 which clearly states in 29.3.4 that “Common Lisp condition
types are in fact CLOS classes, and condition objects are ordinary CLOS
objects”.</p>
<p>This is why one uses <code>DEFINE-CONDITION</code> instead of <code>DEFCLASS</code> and
<code>MAKE-CONDITION</code> instead of <code>MAKE-INSTANCE</code>. This also means that one should
not use slot-related functions (including the very useful <code>WITH-SLOTS</code> macro)
with conditions.</p>
<p>In practice, most modern implementations follow CLtL2 and the
<a href="https://www.lispworks.com/documentation/HyperSpec/Issues/iss049_w.htm"><code>CLOS-CONDITIONS:INTEGRATE</code> X3J13 Cleanup
Issue</a>
and implement conditions as CLOS classes, meaning that conditions can be
manipulated and redefined as any other classes. And the same way as any other
classes, they cannot be deleted.</p>
<h2 id="types">Types</h2>
<p>Types are identified by symbols and are part of the same namespace as classes
(which should not be surprising since defining a class automatically defines a
type with the same name).</p>
<p>Types are defined with <code>DEFTYPE</code>, but documentation is surprisingly silent on
the effects of type redefinition. This can lead to interesting situations. On
some implementations (e.g. SBCL and CCL), if a class slot is defined as having
the type <code>FOO</code>, redefining <code>FOO</code> will not be taken into account and the type
checking operation (which is not mandated by the standard) will use the
previous definition of the type. Infortunatly Common Lisp does not mandate
any specific behavior on slot type mismatches (CLtL2 28.1.3.2).</p>
<p>Thus developers should not expect any useful effect from redefining types.
Restarting the implementation after substantial type changes is probably best.</p>
<p>In the same vein interactions with types are very limited. You cannot find a
type by its symbol or even check whether a type exists or not. Calling
<code>TYPE-OF</code> on a value will return a type this value satisfies, but the nature
of the type is implementation-dependent (CLtL2 4.9): it could be any
supertype. In other words, <code>TYPE-OF</code> could absolutly return <code>T</code> for all values
but <code>NIL</code>. At least <code>SUBTYPE-P</code> lets you check whether a type is a subtype of
another type.</p>
<h2 id="going-further">Going further</h2>
<p>Common Lisp is a complex language with a lot of subtleties, way more than what
can be covered in a blog post. The curious reader will probably skip the
standard (not because you have to buy it, but because it is a <a href="https://www.xach.com/naggum/articles/[email protected]">low quality
scan of a printed
document</a>
and jump directly to CLtL2 or the <a href="https://www.lispworks.com/documentation/lw50/CLHS/Front/index.htm">Common Lisp
HyperSpec</a>.
The Art of the Metaobject Protocol is of course the normative reference for
the CLOS extensions usually referred to as “MOP”.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Guy L. Steele Jr. <em>Common Lisp the Language, 2nd edition.</em> 1990. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>Gregor Kiczales, Jim des Rivieres and Daniel G. Bobrow. <em>The Art of the
Metaobject Protocol.</em> 1991. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
lispReduce vs fold in Common Lisp
https://www.n16f.net/blog/reduce-vs-fold-in-common-lisp/
Fri, 02 Jun 2023 18:00:00 +0000https://www.n16f.net/blog/reduce-vs-fold-in-common-lisp/[email protected] (Nicolas Martyanoff)<h2 id="introduction">Introduction</h2>
<p>If you have already used functional languages, you are probably familiar with
fold, a high order function used to iterate on a collection of values to
combine them and return a result. You may be surprised that Common Lisp does
not have a fold function, but provides <code>REDUCE</code> which works a bit differently.
Let us see how they differ.</p>
<h2 id="understanding-reduce">Understanding <code>REDUCE</code></h2>
<p>In its simplest form, <code>REDUCE</code> accepts a function and a sequence (meaning
either a list or a vector). It then applies the function to successive pairs
of sequence elements.</p>
<p>You can easily check what happens by tracing the function:</p>
<pre><code>CL-USER> (trace +)
CL-USER> (reduce #'+ '(1 2 3 4 5))
0: (+ 1 2)
0: + returned 3
0: (+ 3 3)
0: + returned 6
0: (+ 6 4)
0: + returned 10
0: (+ 10 5)
0: + returned 15
15
</code></pre>
<p>In this example, the call to <code>REDUCE</code> evaluates <code>(+ (+ (+ (+ 1 2) 3) 4) 5)</code>.</p>
<p>You can reverse the order using the <code>:from-end</code> keyword argument:</p>
<pre><code>CL-USER> (trace +)
CL-USER> (reduce #'+ '(1 2 3 4 5) :from-end t)
0: (+ 4 5)
0: + returned 9
0: (+ 3 9)
0: + returned 12
0: (+ 2 12)
0: + returned 14
0: (+ 1 14)
0: + returned 15
15
</code></pre>
<p>In which case you will evaluate <code>(+ 1 (+ 2 (+ 3 (+ 4 5))))</code>. The result is of
course the same since the <code>+</code> function is associative.</p>
<p>You can of course provide an initial value, in which case <code>REDUCE</code> will behave
as if this value has been present at the beginning (or the end with
<code>:from-end</code>) of the sequence.</p>
<p>The surprising aspect of <code>REDUCE</code> is its behaviour when called on a
sequence with less than two elements:</p>
<ul>
<li>If the sequence contains a single element:
<ul>
<li>if there is no initial value, the function is not called and the element
is returned directly;</li>
<li>if there is one, the function is called on both the initial value and the
single element.</li>
</ul>
</li>
<li>If the sequence is empty:
<ul>
<li>if there is no initial value, the function is called without any argument;</li>
<li>if there is one, the function is not called and the initial value is
returned directly.</li>
</ul>
</li>
</ul>
<p>As a result, any function passed to <code>REDUCE</code> must be able to handle being
called with zero, one or two arguments. Most examples found on the Internet
use <code>+</code> or <code>append</code>, and these functions happen to handle it (e.g. <code>(+)</code>
returns the identity element of the addition, zero). If you write your own
functions, you will have to deal with it using the <code>&OPTIONAL</code> lambda list
keyword.</p>
<p>This can lead to unexpected behaviours. If you compute the sum of a sequence
of floats using <code>(reduce #'+ floats)</code>, you may find it logical to obtain a
float. But if <code>FLOATS</code> is an empty sequence, you will get <code>0</code> which is not a
float. Something to keep in mind.</p>
<h2 id="differences-with-fold">Differences with fold</h2>
<p>The fold function is traditionally defined as accepting three arguments: a
function, an initial value — or accumulator — and a list. The function is
called repeatedly with both the accumulator and a list element, using the
value returned by the function as next accumulator.</p>
<p>For example in Erlang:</p>
<pre><code class="language-erlang">lists:foldl(fun(X, Sum) -> Sum + X end, 0, [1, 2, 3, 4, 5]).
</code></pre>
<p>An interesting consequence is that fold functions are always called with the
same type of arguments (the list value and the accumulator), while <code>REDUCE</code>
functions can be called with zero or two list values. This makes it
harder to write functions when the accumulated value has a different type from
sequence values.</p>
<p>Fold is also simpler than <code>REDUCE</code> since it does not have any special case,
making it easier to reason about its behaviour.</p>
<p>It would be interesting to know why a function as fundamental as fold was not
included in the Common Lisp standard.</p>
<h2 id="implementing-foldl">Implementing <code>FOLDL</code></h2>
<p>We can of course implement a fold function in Common Lisp. We will concentrate
on the most common (and most efficient) left-to-right version. Let us start by
a simple implementation for lists:</p>
<pre><code class="language-lisp">(defun foldl/list (function value list)
(declare (type (or function symbol) function)
(type list list))
(if list
(foldl/list function (funcall function value (car list)) (cdr list))
value))
</code></pre>
<p>As clearly visible, the recursive call to <code>FOLDL/LIST</code> is in tail position and
SBCL will happily perform tail-call elimination.</p>
<p>For vectors we use an iterative approach:</p>
<pre><code class="language-lisp">(defun foldl/vector (function value vector)
(declare (type (or function symbol) function)
(type vector vector))
(do ((i 0 (1+ i))
(accumulator value))
((>= i (length vector))
accumulator)
(setf accumulator (funcall function accumulator (aref vector i)))))
</code></pre>
<p>Finally we write the main <code>FOLDL</code> function which operates on any sequence:</p>
<pre><code class="language-lisp">(defun foldl (function value sequence)
(declare (type (or function symbol) function)
(type sequence sequence))
(etypecase sequence
(list (foldl/list function value sequence))
(vector (foldl/vector function value sequence))))
</code></pre>
<p>At the point we can already use <code>FOLDL</code> for various operations. We could of
course improve it with the addition of the usual <code>:START</code>, <code>:END</code> and <code>:KEY</code>
keyword arguments for more flexibility.</p>
lispCounting lines with Common Lisp
https://www.n16f.net/blog/counting-lines-with-common-lisp/
Fri, 17 Mar 2023 18:00:00 +0000https://www.n16f.net/blog/counting-lines-with-common-lisp/[email protected] (Nicolas Martyanoff)<p>A good line counting program has two features: it only counts non-empty lines
to get a fair estimate of the size of a project, and it groups line counts by
file type to help see immediately which languages are used.</p>
<p>A long time ago I got frustrated with two well known line counters.
<a href="https://dwheeler.com/sloccount/">Sloccount</a> spits out multiple strange Perl
warnings about locales, and most of the output is a copyright notice and some
absurd cost estimations. <a href="https://github.com/AlDanial/cloc">Cloc</a> has fourteen
Perl packages as dependencies. Writing a simple line counter is an interesting
exercise; at the time I was discovering Common Lisp, so I wrote my own
version.</p>
<p>I made a few changes years after years, but most of the code stayed the same.
I thought it would be interesting to revisit this program and present it part
by part as a demonstration of how you can use Common Lisp to solve a simple
problem.</p>
<p>We are going to write the program bottom-up, starting with the smallest
building blocks and progressively building upon them.</p>
<h2 id="the-program">The program</h2>
<p>The program is written in Common Lisp. The most convenient way of storing and
executing it is a single executable file stored in a directory being part of
the PATH environment variable. In my case, the script will be called <code>locc</code>,
for “line of code counter”, and will be stored in the <code>~/bin</code> directory.</p>
<p>We start the file with a
<a href="https://en.wikipedia.org/wiki/Shebang_(Unix)">shebang</a> indicating how to
execute the file. We use the <a href="http://sbcl.org/">SBCL</a> implementation because
it is stable and actively developed. It also makes it easy to execute a simple
file:</p>
<pre><code>#!/usr/bin/sbcl --script
</code></pre>
<h2 id="finding-files">Finding files</h2>
<p>Our line counter will operate on directories, so it has to be able to list
files in them. Path handling functions are very disconcerting at first. Common
Lisp was designed a long time ago, and operating systems were different at the
time. Let us dig in!</p>
<p>First let us write a simple function to check if a pathname object is a
directory pathname:</p>
<pre><code class="language-lisp">(defun directory-path-p (path)
"Return T if PATH is a directory or NIL else."
(declare (type (or pathname string) path))
(and (not (pathname-name path))
(not (pathname-type path))))
</code></pre>
<p>Then we write a function to identify hidden files and directories since we do
not want to include them:</p>
<pre><code class="language-lisp">(defun hidden-path-p (path)
"Return T if PATH is a hidden file or directory or NIL else."
(declare (type pathname path))
(let ((name (if (directory-path-p path)
(car (last (pathname-directory path)))
(file-namestring path))))
(and (plusp (length name))
(eq (char name 0) #\.))))
</code></pre>
<p>As you can see we use <code>DIRECTORY-PATH-P</code> to extract the basename of the path,
then check if it starts with a full stop (only if it is not empty of course).</p>
<p>Finally we can write the function to actually list files in a directory
recursively:</p>
<pre><code class="language-lisp">(defun directory-path (path)
"If PATH is a directory pathname, return it as it is. If it is a file
pathname or a string, transform it into a directory pathname."
(declare (type (or pathname string) path))
(if (directory-path-p path)
path
(make-pathname :directory (append (or (pathname-directory path)
(list :relative))
(list (file-namestring path)))
:name nil :type nil :defaults path)))
(defun find-files (path)
"Return a list of all files contained in the directory at PATH or any of its
subdirectories."
(declare (type (or pathname string) path))
(flet ((list-directory (path)
(directory
(make-pathname :defaults (directory-path path)
:type :wild :name :wild))))
(let ((paths nil)
(children (list-directory (directory-path path))))
(dolist (child children paths)
(unless (hidden-path-p child)
(if (directory-path-p child)
(setf paths (append paths (find-files child)))
(push child paths)))))))
</code></pre>
<p>We use the <code>DIRECTORY</code> standard function with a path containing a wildcard
component to list the files in a directory, and do so recursively.</p>
<h2 id="counting-lines">Counting lines</h2>
<p>Now that we have files, we can start counting lines. Let us first write a
function to count the number of non-empty lines in a file.</p>
<pre><code class="language-lisp">(defun count-file-lines (path)
"Count the number of non-empty lines in the file at PATH. A line is empty if
it only contains space or tabulation characters."
(declare (type pathname path))
(with-open-file (stream path :element-type '(unsigned-byte 8))
(do ((nb-lines 0)
(blank-line t))
(nil)
(let ((octet (read-byte stream nil)))
(cond
((or (null octet) (eq octet #.(char-code #\Newline)))
(unless blank-line
(incf nb-lines))
(when (null octet)
(return-from count-file-lines nb-lines))
(setf blank-line t))
((and (/= octet #.(char-code #\Space))
(/= octet #.(char-code #\Tab)))
(setf blank-line nil)))))))
</code></pre>
<p>We open the file to obtain a steam of octets, and read it octet by octet,
keeping track of whether the current line is blank or not. Note how we make
sure to count the last line even if it does not end with a newline character.</p>
<p>Reading a file one octet could be a disaster for performances. Fortunately
SBCL file streams are buffered, something we can easily check by running our
program with <code>strace -e trace=openat,read</code>. We would not rely on this property
if we wanted our program to work on multiple Common Lisp implementations, but
this is a non issue here.</p>
<h2 id="identifying-the-file-type">Identifying the file type</h2>
<p>Counting lines is one thing, but we need to identify their content. The
simplest way is to do so based on the file extension.</p>
<p>Obviously we will want to ignore various files which are known not to contain
text content, so we start by building a hash table containing these
extensions:</p>
<pre><code class="language-lisp">(defparameter *ignored-extensions*
(let ((extensions '("a" "bin" "bmp" "cab" "db" "elc" "exe" "gif" "gz"
"jar" "jpeg" "jpg" "o" "pcap" "pdf" "png" "ps" "rar"
"svg" "tar" "tgz" "tiff" "zip"))
(table (make-hash-table :test 'equal)))
(dolist (extension extensions table)
(setf (gethash extension table) t)))
"A hash table containing all file extensions to ignore.")
</code></pre>
<p>We then create another hash table to associate a type symbol to each known
file extension:</p>
<pre><code class="language-lisp">(defparameter *extension-types*
(let ((pairs '(("asm" . assembly) ("s" . assembly)
("adoc" . asciidoc)
("awk" . awk)
("h" . c) ("c" . c)
("hpp" . cpp) ("cpp" . cpp) ("cc" . cpp)
("css" . css)
("el" . elisp)
("erl" . erlang)
("go" . go)
("html" . html) ("htm" . html)
("ini" . ini)
("hs" . haskell)
("java" . java)
("js" . javascript)
("json" . json)
("tex" . latex)
("lex" . lex)
("lisp" . lisp)
("mkd" . markdown) ("md" . markdown)
("rb" . ruby)
("pl" . perl) ("pm" . perl)
("php" . php)
("py" . python)
("sed" . sed)
("sh" . shell) ("bash" . shell) ("csh" . shell)
("zsh" . shell) ("ksh" . shell)
("scm" . scheme)
("sgml" . sgml)
("sql" . sql)
("texi" . texinfo)
("texinfo" . texinfo)
("vim" . vim)
("xml" . xml) ("dtd" . xml) ("xsd" . xml)
("yaml" . yaml) ("yml" . yaml)
("y" . yacc)))
(table (make-hash-table :test 'equal)))
(dolist (pair pairs table)
(setf (gethash (car pair) table) (cdr pair))))
"A hash table containing a symbol identifying the type of a file for
each known file extension.")
</code></pre>
<p>With these hash tables, the function identifying the type of a file is
trivial:</p>
<pre><code class="language-lisp">(defun identify-file-type (path)
"Return a symbol identifying the type of the file at PATH, or UNKNOWN if the
file extension is not known."
(declare (type pathname path))
(let ((extension (pathname-type path)))
(unless (gethash extension *ignored-extensions*)
(gethash extension *extension-types* 'unknown))))
</code></pre>
<h2 id="collecting-file-information">Collecting file information</h2>
<p>Up to this point, we wrote several functions without connecting them. But we
now have all the building blocks we need. Let us use them to accumulate
information about the files in a list of directories.</p>
<pre><code class="language-lisp">(defun collect-line-counts (directory-paths)
"Collect the line count of all files in the directories located at one of the
paths in DIRECTORY-PATHS and return them grouped by file type as an
association list."
(declare (type list directory-paths))
(let ((line-counts (make-hash-table)))
(dolist (directory-path directory-paths)
(dolist (path (find-files directory-path))
(handler-case
(let ((type (identify-file-type path)))
(when (and type (not (eq type 'unknown)))
(let ((nb-lines (count-file-lines path)))
(incf (gethash type line-counts 0) nb-lines))))
(error (condition)
(format *error-output* "~&error while reading ~A: ~A~%"
path condition)))))
(let ((line-count-list nil))
(maphash (lambda (type nb-lines)
(push (cons type nb-lines) line-count-list))
line-counts)
line-count-list)))
</code></pre>
<p>We get to use all previous functions. We iterate through directory paths to
find non-hidden files using <code>FIND-FILES</code>, then we use <code>IDENTIFY-FILE-TYPE</code> to
obtain a type symbol and <code>COUNT-FILE-LINES</code> to count the number of non-empty
lines in each file. Results are accumulated by file type in the <code>LINE-COUNTS</code>
hash table. During this process, we handle errors that may occur while reading
files with a message on the error output. Finally we transform the hash table
into an association list and return it.</p>
<h2 id="presenting-results">Presenting results</h2>
<p>Executing all these functions in the Lisp REPL is quite practical during
developement, but the default pretty printer is not really what you expect for
the final command line tool:</p>
<p><img src="./repl-output.png" alt="REPL output"></p>
<p>So let us write a function to format this association list:</p>
<pre><code class="language-lisp">(defun format-line-counts (line-counts &key (stream *standard-output*))
"Format the line counts in the LINE-COUNTS association list to STREAM."
(declare (type list line-counts)
(type stream stream))
(dolist (entry (sort line-counts '> :key 'cdr))
(let ((type (car entry))
(nb-lines (cdr entry)))
(format stream "~12A ~8@A~%"
(string-downcase (symbol-name type)) nb-lines))))
</code></pre>
<p>We print the list sorted in descending order, meaning that the file type with
the most number of lines comes first. Of course the output is padded to make
sure numbers are all aligned.</p>
<h2 id="finalizing-the-program">Finalizing the program</h2>
<p>The only thing left to do is the entry point of the script. This is the only
place where we need to call a non-standard function in order to access command
line arguments. If no command line argument were passed to the script, we look
for files in the current directory.</p>
<pre><code class="language-lisp">(let ((paths (or (cdr sb-ext:*posix-argv*) '("."))))
(format-line-counts
(collect-line-counts paths)))
</code></pre>
<p><img src="./shell-output.png" alt="Shell output"></p>
<p>Much better!</p>
<p>There does not seem to be any performance issue: on the Linux kernel source
tree, this program is almost 11 times faster than sloccount. It would be
interesting to profile the program to make sure IO is the bottleneck and
improve inefficient parts, but this code is fast enough for my needs.</p>
<p>As you can see, it is not that hard to use Common Lisp to solve problems.</p>
lispCustom Font Lock configuration in Emacs
https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/
Fri, 24 Feb 2023 18:00:00 +0000https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/[email protected] (Nicolas Martyanoff)<p><a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Font-Lock-Mode.html">Font
Lock</a>
is the builtin Emacs minor mode used to highlight textual elements in buffers.
Major modes usually configure it to detect various syntaxic constructions and
attach
<a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Faces.html">faces</a>
to them.</p>
<p>The reason I ended up deep into Font Lock is because I was not satisfied with
the way it is configured for <code>lisp-mode</code>, the major mode used for both Common
Lisp and Emacs Lisp code. This forced me to get acquainted with various
aspects of Font Lock in order to change its configuration. If you want to
change highlighting for your favourite major mode, you will find this article
useful.</p>
<h2 id="common-lisp-highlighting-done-wrong">Common Lisp highlighting done wrong</h2>
<p>The core issue of Common Lisp highlighting in Emacs is that a lot of it is
arbitrary and inconsistent:</p>
<ul>
<li>The mode highlights what it calls “definers” and “keywords”, but it does not
really make sense in Common Lisp. Why would <code>WITH-OUTPUT-TO-STRING</code> be
listed as a keyword, but not <code>CLASS-OF</code>?</li>
<li><code>SIGNAL</code> uses <code>font-lock-warning-face</code>. Why would it be a warning? Even
stranger, why would you use this warning face for <code>CHECK-TYPE</code>?</li>
<li>Keywords and uninterned symbols are all highlighted with
<code>font-lock-builtin-face</code>. But they are not functions or variables. They are
not even special in any way, and their syntax already indicates clearly
their nature. Having so many yellow symbols everywhere is really
distracting.</li>
<li>All symbols starting with <code>&</code> are highlighted using <code>font-lock-type-face</code>.
But lambda list arguments are not types, and symbols starting with <code>&</code> are
not always lambda list arguments.</li>
<li>All symbols preceded by <code>(</code> whose name starts with <code>DO-</code> or <code>WITH-</code> are
highlighted as keywords. There is even a comment by RMS stating that it is
too general. He is right.</li>
</ul>
<p>Beyond these issues, the mode sadly uses default Font Lock faces instead of
defining semantically appropriate faces and mapping them to existing ones as
default values.</p>
<p>The chances of successfully driving this kind of large and disruptive change
directly into Emacs are incredibly low. Even if it was to be accepted, the
result would not be available until the next release, which could mean months.
Fortunately, Emacs is incredibly flexible and we can change all of this
ourselves.</p>
<p>Note that you may not agree with the list of issues above, and this is fine.
The point of this article is to show you how you can change the way Emacs
highlights content in order to match your preferences. And you can do that for
all major modes!</p>
<h2 id="font-lock-configuration">Font Lock configuration</h2>
<p>Font Lock always felt a bit magic and it took me some time to find the
motivation to read the documentation. As is turned out, it can be used for
very complex highlighting schemes, but basic features are not that hard to
use.</p>
<p>The main configuration of Font Lock is stored in the <code>font-lock-defaults</code>
buffer-local variable. It is a simple list containing the following entries:</p>
<ul>
<li>A list of symbols containing the value to use for <code>font-lock-keywords</code> at
each
<a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Levels-of-Font-Lock.html">level</a>,
the first symbol being the default value.</li>
<li>The value used for <code>font-lock-keywords-only</code>. If it is <code>nil</code>, it enables
syntaxic highlighting (strings and comments) in addition of search-based
(keywords) highlighting.</li>
<li>The value used for <code>font-lock-keywords-case-fold-search</code>. If true,
highlighting is case insensitive.</li>
<li>The value used for <code>font-lock-syntax-table</code>, the association list
controlling syntaxic highlighting. If it is <code>nil</code>, Font Lock uses the syntax
table configured with <code>set-syntax-table</code>. In <code>lisp-mode</code> this would mean
<code>lisp-mode-syntax-table</code>.</li>
<li>All remaining values are bindings using the form <code>(VARIABLE-NAME . VALUE)</code>
used to set buffer-local values for other Font Lock variables.</li>
</ul>
<p>The part we are interested about is search-based highlighting which uses
regular expressions to find specific text fragments and attach faces to them.</p>
<p>Values used for <code>font-lock-keywords</code> are also lists. Each element is a
construct used to specify one or more keywords to highlight. While these
constructs can have multiple forms for more complex use cases, we will only
use the two simplest ones:</p>
<ul>
<li><code>(REGEXP . FACE)</code> tells Font Lock to use <code>FACE</code> for text fragments which
match <code>REGEXP</code>. For example, you could use <code>("\\_<-?[0-9]+\\_>" . font-lock-constant-face)</code> to highlight integers as constants (note the use
of <code>\_<</code> and <code>\_></code> to match the start and end of a symbol; see the <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Regexp-Backslash.html">regexp
documentation</a>
for more information).</li>
<li><code>(REGEXP (GROUP FACE)…)</code> is a bit more advanced. When <code>REGEXP</code> matches a
subset of the buffer, Font Lock assigns faces to the capture group
identified by their number. You could use this construction to detect a
complex syntaxic element and highlight some of its parts with different
faces.</li>
</ul>
<h2 id="simplified-common-lisp-highlighting">Simplified Common Lisp highlighting</h2>
<p>We are going to configure keyword highlighting for the following types of
values:</p>
<ul>
<li>Character literals, e.g. <code>#\Space</code>.</li>
<li>Function names in the context of a function call for standard Common lisp
functions.</li>
<li>Standard Common Lisp values such as <code>*STANDARD-OUTPUT*</code> or <code>PI</code>.</li>
</ul>
<p>Additionally, we want to keep the default syntaxic highlighting configuration
which recognizes character strings, documentation strings and comments.</p>
<h3 id="faces">Faces</h3>
<p>Let us start by defining new faces for the different values we are going to
match:</p>
<pre><code class="language-lisp">(defface g-cl-character-face
'((default :inherit font-lock-constant-face))
"The face used to highlight Common Lisp character literals.")
(defface g-cl-standard-function-face
'((default :inherit font-lock-keyword-face))
"The face used to highlight standard Common Lisp function symbols.")
(defface g-cl-standard-value-face
'((default :inherit font-lock-variable-name-face))
"The face used to highlight standard Common Lisp value symbols.")
</code></pre>
<p>Nothing complicated here, we simply inherit from default Font Lock faces. You
can then configure these faces in your color theme without affecting other
modes using Font Lock.</p>
<h3 id="keywords">Keywords</h3>
<p>To detect standard Common Lisp functions and values, we are going to need a
regular expression. The first step is to build a list of strings for both
functions and values. Easy to do with a bit of Common Lisp code!</p>
<pre><code class="language-lisp">(defun standard-symbol-names (predicate)
(let ((symbols nil))
(do-external-symbols (symbol :common-lisp)
(when (funcall predicate symbol)
(push (string-downcase (symbol-name symbol)) symbols)))
(sort symbols #'string<)))
(standard-symbol-names #'fboundp)
(standard-symbol-names #'boundp)
</code></pre>
<p>The <code>STANDARD-SYMBOL-NAMES</code> build a list of symbols exported from the
<code>:COMMON-LISP</code> package which satisfy a predicate. The first call gives us the
name of all symbols bound to a function, and the second all which are bound to
a value.</p>
<p>The astute reader will immediately wonder about symbols which are bound both a
function and a value. They are easy to find by calling <code>INTERSECTION</code> on both
sets of names: <code>+</code>, <code>/</code>, <code>*</code>, <code>-</code>. It is not really a problem: we can
highlight function calls by matching function names preceded by <code>(</code>, making
sure that these symbols will be correctly identified as either function
symbols or value symbols depending on the context.</p>
<p>We store these lists of strings in the <code>g-cl-function-names</code> and
<code>g-cl-value-names</code> (the associated code is not reproduced here: these lists
are quite long; but I posted them as a
<a href="https://gist.github.com/galdor/1c30c29471045d0365af72a4caf7b1f2">Gist</a>).</p>
<p>With this lists, we can use the <code>regexp-opt</code> Emacs Lisp function to build
optimized regular expressions matching them:</p>
<pre><code class="language-lisp">(defvar g-cl-font-lock-keywords
(let* ((character-re (concat "#\\\\" lisp-mode-symbol-regexp "\\_>"))
(function-re (concat "(" (regexp-opt g-cl-function-names t) "\\_>"))
(value-re (regexp-opt g-cl-value-names 'symbols)))
`((,character-re . 'g-cl-character-face)
(,function-re
(1 'g-cl-standard-function-face))
(,value-re . 'g-cl-standard-value-face))))
</code></pre>
<p>Characters literals are reasonably easy to match.</p>
<p>Functions are a bit more complicated since we want to match the function name
when it is preceded by an opening parenthesis. We use a capture capture (see
the last argument of <code>regexp-opt</code>) for the function name and highlight it
separately.</p>
<p>Values are always matched as full symbols: we do not want to highlight parts
of a symbol, for example <code>MAP</code> in a symbol named <code>MAPPING</code>.</p>
<h3 id="final-configuration">Final configuration</h3>
<p>Finally we can define the variable which will be used for <code>font-lock-defaults</code>
in the initialization hook; we copy the original value from <code>lisp-mode</code>, and
change the keyword list for what is going to be our own configuration:</p>
<pre><code class="language-lisp">(defvar g-cl-font-lock-defaults
'((g-cl-font-lock-keywords)
nil ; enable syntaxic highlighting
t ; case insensitive highlighting
nil ; use the lisp-mode syntax table
(font-lock-mark-block-function . mark-defun)
(font-lock-extra-managed-props help-echo)
(font-lock-syntactic-face-function
. lisp-font-lock-syntactic-face-function)))
</code></pre>
<p>To configure <code>font-lock-defaults</code>, we simply set it in the initialization hook
of <code>lisp-mode</code>:</p>
<pre><code class="language-lisp">(defun g-init-lisp-font-lock ()
(setq font-lock-defaults g-cl-font-lock-defaults))
(add-hook 'lisp-mode-hook 'g-init-lisp-font-lock)
</code></pre>
<h2 id="comparison">Comparison</h2>
<p>Let us compare highlighting for a fragment of code before and after our
changes:</p>
<p><img src="./before.png" alt="Before">
<img src="./after.png" alt="After"></p>
<p>The differences are subtle but important:</p>
<ul>
<li>All standard functions are highlighted, helping to distinguish them from
user-defined functions.</li>
<li>Standard values such as <code>*ERROR-OUTPUT*</code> are highlighted.</li>
<li>Character literals are highlighted the same way as character strings.</li>
<li>Keywords are not highlighted anymore, avoiding the confusion with function
names.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>That was not easy; but as always, the effort of going through the
documentation and experimenting with different Emacs components was very
rewarding. Font Lock does not feel like a black box anymore, opening the road
for the customization of other major modes.</p>
<p>In the future, I will work on a custom color scheme to use more subtle colors,
with the hope of reducing the rainbow effect of so many major modes, including
<code>lisp-mode</code>.</p>
emacslispCommon Lisp implementations in 2023
https://www.n16f.net/blog/common-lisp-implementations-in-2023/
Wed, 22 Feb 2023 18:00:00 +0000https://www.n16f.net/blog/common-lisp-implementations-in-2023/[email protected] (Nicolas Martyanoff)<p>Much has been written on Common Lisp; there is rarely one year without someone
proclaming the death of the language and how nobody uses it anymore. And yet
it is still here, so something must have been done right.</p>
<p>Common Lisp is not a software, it is a language described by the ANSI INCITS
226-1994 standard; there are multiple implementations available, something
often used as argument for how alive and thriving the language is.</p>
<p>Let us see what the 2023 situation is.</p>
<h2 id="general-information">General information</h2>
<table>
<thead>
<tr>
<th style="text-align:left">Implementation</th>
<th style="text-align:left">License</th>
<th style="text-align:left">Target</th>
<th style="text-align:left">Last release</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><a href="http://sbcl.org/">SBCL</a></td>
<td style="text-align:left">Public domain</td>
<td style="text-align:left">Native</td>
<td style="text-align:left">2023/01 (2.3.1)</td>
</tr>
<tr>
<td style="text-align:left"><a href="https://ccl.clozure.com/">CCL</a></td>
<td style="text-align:left">Apache 2.0</td>
<td style="text-align:left">Native</td>
<td style="text-align:left">2021/05 (1.12.1)</td>
</tr>
<tr>
<td style="text-align:left"><a href="https://ecl.common-lisp.dev/">ECL</a></td>
<td style="text-align:left">LGPL 2.1</td>
<td style="text-align:left">Native (C translation)</td>
<td style="text-align:left">2021/02 (21.2.1)</td>
</tr>
<tr>
<td style="text-align:left"><a href="https://abcl.org/">ABCL</a></td>
<td style="text-align:left">GPL2</td>
<td style="text-align:left">Java bytecode</td>
<td style="text-align:left">2023/02 (1.9.1)</td>
</tr>
<tr>
<td style="text-align:left"><a href="https://clasp-developers.github.io">CLASP</a></td>
<td style="text-align:left">LGPL 2.1</td>
<td style="text-align:left">Native (LLVM)</td>
<td style="text-align:left">2023/01 (2.1.0)</td>
</tr>
<tr>
<td style="text-align:left"><a href="https://www.cons.org/cmucl/">CMUCL</a></td>
<td style="text-align:left">Public domain</td>
<td style="text-align:left">Native</td>
<td style="text-align:left">2017/10 (21c)</td>
</tr>
<tr>
<td style="text-align:left"><a href="https://www.gnu.org/software/gcl/">GCL</a></td>
<td style="text-align:left">LGPL2</td>
<td style="text-align:left">Native (C translation)</td>
<td style="text-align:left">2023/01 (2.6.14)</td>
</tr>
<tr>
<td style="text-align:left"><a href="https://clisp.sourceforge.io/">CLISP</a></td>
<td style="text-align:left">GPL</td>
<td style="text-align:left">Bytecode</td>
<td style="text-align:left">2010/07 (2.49)</td>
</tr>
<tr>
<td style="text-align:left"><a href="http://www.lispworks.com/">Lispworks</a></td>
<td style="text-align:left">Proprietary</td>
<td style="text-align:left">Native</td>
<td style="text-align:left">2022/06 (8.0.1)</td>
</tr>
<tr>
<td style="text-align:left"><a href="https://franz.com/products/allegro-common-lisp/">Allegro</a></td>
<td style="text-align:left">Proprietary</td>
<td style="text-align:left">Native</td>
<td style="text-align:left">2017/04 (10.1)</td>
</tr>
</tbody>
</table>
<p>Note that all projects may have small parts with different licenses. This is
particularily important for CLASP which contains multiple components imported
from other projects.</p>
<p>I was quite surprised to see so many projects with recent releases. Clearly a
good sign. Let us look at each implementation.</p>
<h2 id="implementations">Implementations</h2>
<h3 id="sbcl">SBCL</h3>
<p>Steel Bank Common Lisp was forked from CMUCL in December 1999 and has since
massively grown in popularity; it is currently the most used implementation by
far. Unsurprisingly given its popularity, SBCL is supported by pretty much all
Common Lisp libraries and tools out there. It is well known for generating
fast native code compared to other implementations.</p>
<p>The most important aspect of SBCL is that it is actively maintained: its
developers release new versions on a monthly basis, bringing each time a small
list of improvements and bug fixes. Activity has actually increased these last
years, something uncommon in the Common Lisp world.</p>
<h3 id="ccl">CCL</h3>
<p>Clozure Common Lisp has a long and complex
<a href="https://ccl.clozure.com/history.html">history</a> and has been around for
decades. It is a mature implementation; it has two interesting aspects
compared to SBCL:</p>
<ul>
<li>The compiler is much faster.</li>
<li>Error messages tend to be clearer.</li>
</ul>
<p>This is why I currently use it to test my code along SBCL. And according to
what I have heard, this is a common choice among developers.</p>
<p>The main issue with CCL is that the project is almost completely abandonned.
<a href="https://github.com/Clozure/ccl/graphs/contributors">Git activity</a> has slowed
down to a crawl in the last two years, and none of the original maintainers
from Clozure seem to be actively working on it. It remains nonetheless a major
implementation.</p>
<h3 id="ecl">ECL</h3>
<p>Embeddable Common Lisp is a small implementation which can be used both as a
library or as a standalone program. It contains a bytecode interpreter, but
can also translate Lisp code to C to be compiled to native code.</p>
<p>While development is slow, improvements and bug fixes are still added on a
regular basis. Clearly an interesting project: I could see myself using ECL to
write plugins into an application able to call a C library.</p>
<h3 id="abcl">ABCL</h3>
<p>Armed Bear Common Lisp is quite different from other implementations: it
produces Java bytecode and targets the Java Virtual Machine, making it a
useful tool in Java ecosystems.</p>
<p>While it has not found the same success as <a href="https://clojure.org/">Clojure</a>,
ABCL is still a fully featured Common Lisp implementation which passes almost
the entire ANSI Common Lisp test suite.</p>
<p>Developement is slow nowadays but there are still new releases with lots of
bug fixes. Also note that two of the developers are able to provide paid
support.</p>
<h3 id="clasp">CLASP</h3>
<p>CLASP is a newcomer in the Common Lisp world (new meaning it is less than a
decade old). Developed by Christian Schafmeister for his research work, this
implementation has been used as an exemple of how alive and kicking Common
Lisp, mainly due to two
<a href="https://www.youtube.com/watch?v=8X69_42Mj-g">excellent</a>
<a href="https://www.youtube.com/watch?v=mbdXeRBbgDM">presentations</a>.</p>
<p>While very promising, CLASP suffers from its young age: trying to run the last
release on my code resulted in a brutal error with no details and no
backtrace. However I have no doubt that CLASP will get a lot better: it is
actively maintained and used in production, two of the necessary ingredients
for a software to stay relevant.</p>
<h3 id="gcl">GCL</h3>
<p>GNU Common Lisp is described as the official Common Lisp implementation for
the GNU project. While it clearly does not have the popularity of other
implementations, it is still a maintained project.</p>
<p>Trying to use it, I quickly realized it is not fully compliant with the
standard. For example it will fail when evaluating a call to <code>COMPILE-FILE</code>
with the <code>:VERBOSE</code> key argument.</p>
<p>Hopefully development will continue.</p>
<h3 id="clisp">CLISP</h3>
<p>CLISP is almost as old as I am; it was the first implementation I used a long
time ago, and it still works. While it has all the usual features
(multithreading, FFI, MOP, etc.), there is no real reason to use it compared
to other implementations.</p>
<p>Even if it was to have any specific feature, CLISP is almost completely
abandonned. While there are has been a semblant of activity a few years ago,
active development pretty much stopped around 2012; the last release was more
than 12 years ago.</p>
<h3 id="lispworks">Lispworks</h3>
<p>Moving to proprietary implementations; Lispworks has been around for more than
30 years and the company producing it still release new versions on a regular
basis.</p>
<p>While Lispworks supports most features you would expect from a commercial
product (native compiler, multithreading, FFI, GUI library, various graphical
tools, a Prolog implementation…), it is hampered by its licensing system.</p>
<p>The free “Personal Edition” limits the program size and the amount of time it
can run, making it pretty much useless for anything but evaluation. The
professional and enterprise licenses do not really make sense for anyone: you
will have to buy separate licenses for every single platform at more than a
thousand euros per license (with the enterprise version being 2-3 times more
expensive). Of course you will have to buy a maintenance contract on a yearly
basis… but it does not include technical support. It will have to be bought
with “incident packs” costing thousands of euros; because yes, paying for a
product and a maintenance contract does not mean they will fix bugs, and you
will have to pay for each of them.</p>
<p>I do not have anything personal against commercial software, and I strongly
support developers being paid for their work. But this kind of licensing makes
Lispworks irrelevant to everyone but those already using their proprietary
libraries.</p>
<h3 id="allegro">Allegro</h3>
<p>Allegro Common Lisp is the other well known proprietary implementation.
Developped by Franz Inc., it is apparently used by multiple organizations
including the U.S. Department of Defense.</p>
<p>Releases are uncommon, the last one being almost 6 years ago. But Allegro is a
mature implementation packed with features not easily replicated such as
<a href="https://franz.com/products/allegrocache/index.lhtml">AllegroCache</a>,
<a href="https://franz.com/products/web_tools/">AllegroServe</a>, libraries for multiple
protocols and data formats, analysis tools, a concurrent garbage collector and
even an OpenGL interface.</p>
<p>Allegro suffers the same issue as Lispworks: the enterprise-style pricing
system is incredibly frustrating. The website advertises a hefty $599 starting
price (which at least includes technical support), but there is no mention of
what it contains. Interested developpers will have to contact Franz Inc. to
get other prices. A quick Google search will reveal rumours of enterprise
versions priced above 8000 dollars. No comment.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Researching Common Lisp implementations has been interesting. While it is
clear that the language is far from dead, its situation is very fragile.
Proprietary implementations are completely out of touch with the needs of most
developers, leaving us with a single open source, actively maintained, high
performance implementation: SBCL. Unless of course they are willing to deal
with the JVM to use ABCL.</p>
<p>It might me interesting to investigate a possible solution to keep CCL somehow
alive, with patches being merged and releases being produced. I sent a patch
very recently, let us see what can be done!</p>
lispReading files faster in Common Lisp
https://www.n16f.net/blog/reading-files-faster-in-common-lisp/
Wed, 15 Feb 2023 18:00:00 +0000https://www.n16f.net/blog/reading-files-faster-in-common-lisp/[email protected] (Nicolas Martyanoff)<p>While Common Lisp has functions to open, read and write files, none of them
takes care of reading and returning the entire content. This is something that
I do very regularly, so it made sense to add such a function to
<a href="https://github.com/galdor/tungsten">Tungsten</a>. It turned out to be a bit more
complicated than expected.</p>
<h1 id="a-simple-but-incorrect-implementation">A simple but incorrect implementation</h1>
<p>The simplest implementation relies on the <code>FILE-LENGTH</code> function which returns
the length of a stream (which of course only makes sense for a file stream).
The
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_file_l.htm">Hyperspec</a>
clearly states that “for a binary file, the length is measured in units of the
element type of the stream”. Since we are only reading binary data, everything
is fine.</p>
<p>Let us write the function:</p>
<pre><code class="language-lisp">(defun read-file (path)
(declare (type (or pathname string) path))
(with-open-file (file path :element-type 'core:octet)
(let ((data (make-array (file-length file) :element-type 'core:octet)))
(read-sequence data file)
data)))
</code></pre>
<p>Note that <code>CORE:OCTET</code> is a Tungsten type for <code>(UNSIGNED-BYTE 8)</code>.</p>
<p>The function works as expected, returning the content of the file as an octet
vector. But it is not entirely correct.</p>
<p>This implementation only works for regular files. Various files on UNIX will
report a length of zero but can still be read. Now you might protest that it
would not make sense to call <code>READ-FILE</code> on a device such as <code>/dev/urandom</code>,
and you would be right. But a valid example would be pseudo files such as
those part of <a href="https://docs.kernel.org/filesystems/proc.html"><code>procfs</code></a>. If
you want to obtain memory stats about your process on Linux, you can simply
read <code>/proc/self/statm</code>. But this is not a regular file and <code>READ-FILE</code> will
return an empty octet vector.</p>
<h1 id="doing-it-right-and-slow">Doing it right and slow</h1>
<p>The right way to read a file is to read its content block by block until the
read operation fails because it reached the end of the file.</p>
<p>Let us re-write <code>READ-FILE</code>:</p>
<pre><code class="language-lisp">(defun read-file (path)
(declare (type (or pathname string) path))
(let ((data (make-array 0 :element-type 'core:octet :adjustable t))
(block-size 4096)
(offset 0))
(with-open-file (file path :element-type 'core:octet)
(loop
(let* ((capacity (array-total-size data))
(nb-left (- capacity offset)))
(when (< nb-left block-size)
(let ((new-length (+ capacity (- block-size nb-left))))
(setf data (adjust-array data new-length)))))
(let ((end (read-sequence data file :start offset)))
(when (= end offset)
(return-from read-file (adjust-array data end)))
(setf offset end))))))
</code></pre>
<p>This time we rely on an adjustable array; we iterate, making sure we have
enough space in the array to read an entire block each time. When the array is
too short, we use <code>ADJUST-ARRAY</code> to extend it, relying on its ability to reuse
the underlying storage instead of systematically copying its content.</p>
<p>Finally, once <code>READ-SEQUENCE</code> stops returning data, we truncate the array to
the right size and return it.</p>
<p>This function worked correctly and I started using it regularly. Recently I
started working with a file larger than usual and realized that <code>READ-FILE</code>
was way too slow. With a NVMe drive, I would expect to be able to read a 10+MB
file almost instantaneously, but it took several seconds.</p>
<p>After inspecting the code to find what could be so slow, I started to wonder
about <code>ADJUST-ARRAY</code>; while I thought SBCL would internally extend the
underlying memory in large blocks to minimize allocations, behaving similarly
to <code>realloc()</code> in C, it turned out not to be the case. While reading the code
behind <code>ADJUST-ARRAY</code>, I learned that it precisely allocates the required
size. As a result, this implementation of <code>READ-FILE</code> performs one memory
allocation for each 4kB block. Not a problem for small files, slow for larger
ones.</p>
<h1 id="a-final-version-correct-and-fast">A final version, correct and fast</h1>
<p>Since I understood what the problem was, fixing it was trivial. When there is
not enough space to read a block, we extend the array by at least 50% of its
current size. Of course this is a balancing act: for example doubling the size
at each allocation would reduce even more the number of allocations, but would
increase the total amount of memory allocated. The choice is up to you.</p>
<pre><code class="language-lisp">(defun read-file (path)
(declare (type (or pathname string) path))
(let ((data (make-array 0 :element-type 'core:octet :adjustable t))
(block-size 4096)
(offset 0))
(with-open-file (file path :element-type 'core:octet)
(loop
(let* ((capacity (array-total-size data))
(nb-left (- capacity offset)))
(when (< nb-left block-size)
(let ((new-length (max (+ capacity (- block-size nb-left))
(floor (* capacity 3) 2))))
(setf data (adjust-array data new-length)))))
(let ((end (read-sequence data file :start offset)))
(when (= end offset)
(return-from read-file (adjust-array data end)))
(setf offset end))))))
</code></pre>
<p>This last version reads a 250MB file in a quarter of a second, while the
original version took almost two minutes. Much better!</p>
lispCustom Common Lisp indentation in Emacs
https://www.n16f.net/blog/custom-common-lisp-indentation-in-emacs/
Mon, 23 Jan 2023 18:00:00 +0000https://www.n16f.net/blog/custom-common-lisp-indentation-in-emacs/[email protected] (Nicolas Martyanoff)<p>While <a href="https://slime.common-lisp.dev/">SLIME</a> is most of the time able to
indent Common Lisp correctly, it will sometimes trip on custom forms. Let us
see how we can customize indentation.</p>
<p>In the process of writing my PostgreSQL client in Common Lisp, I wrote a
<code>READ-MESSAGE-CASE</code> macro which reads a message from a stream and execute code
depending on the type of the message:</p>
<pre><code class="language-lisp">(defmacro read-message-case ((message stream) &rest forms)
`(let ((,message (read-message ,stream)))
(case (car ,message)
(:error-response
(backend-error (cdr ,message)))
(:notice-response
nil)
,@forms
(t
(error 'unexpected-message :message ,message)))))
</code></pre>
<p>This macro is quite useful: all message loops can use it to automatically
handle error responses, notices, and signal unexpected messages.</p>
<p>But SLIME does not know how to indent <code>READ-MESSAGE-CASE</code>, so by default it
will align all message forms on the first argument:</p>
<pre><code class="language-lisp">(read-message-case (message stream)
(:authentication-ok
(return))
(:authentication-cleartext-password
(unless password
(error 'missing-password))
(write-password-message password stream)))
</code></pre>
<p>While we want it aligned the same way as <code>HANDLER-CASE</code>:</p>
<pre><code class="language-lisp">(read-message-case (message stream)
(:authentication-ok
(return))
(:authentication-cleartext-password
(unless password
(error 'missing-password))
(write-password-message password stream)))
</code></pre>
<p>Good news, SLIME indentation is defined as a list of rules. Each rule
associates an indentation specification (a S-expression describing how to
indent the form) to a symbol and store it as the <code>common-lisp-indent-function</code>
property of the symbol.</p>
<p>You can obtain the indentation rule of a Common Lisp symbol easily. For
example, executing <code>(get 'defun 'common-lisp-indent-function)</code> (e.g. in IELM
or with <code>eval-expression</code>) yields <code>(4 &lambda &body)</code>. This indicates that
<code>DEFUN</code> forms are to be indented as follows:</p>
<ul>
<li>The first argument of <code>DEFUN</code> (the function name) is indented by four
spaces.</li>
<li>The second argument (the list of function arguments) is indented as a lambda
list.</li>
<li>The rest of the arguments are indented based on the <code>lisp-body-indent</code>
custom variable, which controls the indentation of the body of a lambda form
(two spaces by default).</li>
</ul>
<p>You can refer to the documentation of the <code>common-lisp-indent-function</code> Emacs
function (defined in SLIME of course) for a complete description of the
format.</p>
<p>We want <code>READ-MESSAGE-CASE</code> to be indented the same way as <code>HANDLER-CASE</code>,
whose indentation specification is <code>(4 &rest (&whole 2 &lambda &body))</code> (in
short, an argument and a list of lambda lists). Fortunately there is a way to
specify that a form must be indented the same way as another form, using <code>(as <symbol>)</code>.</p>
<p>Let us first define a function to set the indentation specification of a
symbol:</p>
<pre><code class="language-lisp">(defun g-common-lisp-indent (symbol indent)
"Set the indentation of SYMBOL to INDENT."
(put symbol 'common-lisp-indent-function indent))
</code></pre>
<p>Then use it for <code>READ-MESSAGE-CASE</code>:</p>
<pre><code class="language-lisp">(g-common-lisp-indent 'read-message-case '(as handler-case))
</code></pre>
<p>While it is in general best to avoid custom indentation, exceptions are
sometimes necessary for readability. And SLIME makes it easy.</p>
lispANSI color rendering in SLIME
https://www.n16f.net/blog/ansi-color-rendering-in-slime/
Mon, 16 Jan 2023 18:00:00 +0000https://www.n16f.net/blog/ansi-color-rendering-in-slime/[email protected] (Nicolas Martyanoff)<p>I was working on the terminal output for a Common Lisp logger, and I realized
that <a href="https://slime.common-lisp.dev/">SLIME</a> does not interpret ANSI escape
sequences.</p>
<p>This is not the end of the world, but having at least colors would be nice.
Fortunately there is a
<a href="https://gitlab.com/augfab/slime-repl-ansi-color">library</a> to do just that.</p>
<p>First let us install the package, here using
<a href="https://github.com/jwiegley/use-package"><code>use-package</code></a> and
<a href="https://github.com/radian-software/straight.el"><code>straight.el</code></a>.</p>
<pre><code class="language-lisp">(use-package slime-repl-ansi-color
:straight t)
</code></pre>
<p>While in theory we are supposed to just add <code>slime-repl-ansi-color</code> to
<code>slime-contribs</code>, it did not work for me, and I add to enable the minor mode
manually.</p>
<p>If you already have a SLIME REPL hook, simply add <code>(slime-repl-ansi-color-mode 1)</code>. If not, write an initialization function, and add it to the SLIME REPL
initialization hook:</p>
<pre><code class="language-lisp">(defun g-init-slime-repl-mode ()
(slime-repl-ansi-color-mode 1))
(add-hook 'slime-repl-mode-hook 'g-init-slime-repl-mode)
</code></pre>
<p>To test that it works as intended, fire up SLIME and print a simple message
using ANSI escape sequences:</p>
<pre><code class="language-lisp">(let ((escape (code-char 27)))
(format t "~C[1;33mHello world!~C[0m~%" escape escape))
</code></pre>
<p>While it is tempting to use the <code>#\Esc</code> character, it is part of the Common
Lisp standard; therefore we use <code>CODE-CHAR</code> to obtain it from its ASCII
numeric value. We use two escape sequences, the first one to set the bold flag
and foreground color, and the second one to reset display status.</p>
<p>If everything works well, should you see a nice bold yellow message:</p>
<p><img src="./output.png" alt="ANSI escape sequence rendering"></p>
lispSwitching between implementations with SLIME
https://www.n16f.net/blog/switching-between-implementations-with-slime/
Thu, 12 Jan 2023 18:00:00 +0000https://www.n16f.net/blog/switching-between-implementations-with-slime/[email protected] (Nicolas Martyanoff)<p>While I mostly use <a href="http://sbcl.org/">SBCL</a> for Common Lisp development, I
regularly switch to <a href="https://ccl.clozure.com/">CCL</a> or even
<a href="https://ecl.common-lisp.dev/">ECL</a> to run tests.</p>
<p>This is how I do it with <a href="https://slime.common-lisp.dev/">SLIME</a>.</p>
<h2 id="starting-implementations">Starting implementations</h2>
<p>SLIME lets you configure multiple implementations using the
<code>slime-lisp-implementations</code> setting. In my case:</p>
<pre><code class="language-lisp">(setq slime-lisp-implementations
'((sbcl ("/usr/bin/sbcl" "--dynamic-space-size" "2048"))
(ccl ("/usr/bin/ccl"))
(ecl ("/usr/bin/ecl"))))
</code></pre>
<p>Doing so means that running <code>M-x slime</code> will execute the first implementation,
i.e. SBCL. There are two ways to run other implementations.</p>
<p>First you can run <code>C-u M-x slime</code> which lets you type the path and arguments
of the implementation to execute. This is a bit annoying because the prompt
starts with the content of the <code>inferior-lisp-program</code> variable, i.e. <code>"lisp"</code>
by default, meaning it has to be deleted manually each time. Therefore I set
<code>inferior-lisp-program</code> to the empty string:</p>
<pre><code class="language-lisp">(setq inferior-lisp-program "")
</code></pre>
<p>Then you can run <code>C-- M-x slime</code> (or <code>M-- M-x slime</code> which is easier to type)
to instruct SLIME to use interactive completion (via <code>completing-read</code>) to let
you select the implementations among those configured in
<code>slime-lisp-implementations</code>.</p>
<p>To make my life easier, I bind <code>C-c C-s s</code> to a function which always prompt
for the implementation to start:</p>
<pre><code class="language-lisp">(defun g-slime-start ()
(interactive)
(let ((current-prefix-arg '-))
(call-interactively 'slime)))
</code></pre>
<p>Using <code>C-c C-s</code> as prefix for all my global SLIME key bindings helps me
remember them.</p>
<h2 id="switching-between-multiple-implementations">Switching between multiple implementations</h2>
<p>Running the <code>slime</code> function several times will create multiple connections as
expected. Commands executed in Common Lisp buffers are applied to the current
connection, which is by default the most recent one.</p>
<p>There are two ways to change the current implementation:</p>
<ol>
<li>Run <code>M-x slime-next-connection</code>.</li>
<li>Run <code>M-x slime-list-connections</code>, which opens a buffer listing connections,
and lets you choose the current one with the <code>d</code> key.</li>
</ol>
<p>I find both impractical: the first one does not let me choose the
implementation, forcing me to run potentially several times before getting the
one I want. The second one opens a buffer but does not switch to it.</p>
<p>All I want is a prompt with completion. So I wrote one.</p>
<p>First we define a function to select a connection among existing one:</p>
<pre><code class="language-lisp">(defun g-slime-select-connection (prompt)
(interactive)
(let* ((connections-data
(mapcar (lambda (process)
(cons (slime-connection-name process) process))
slime-net-processes))
(completion-extra-properties
'(:annotation-function
(lambda (string)
(let* ((process (alist-get string minibuffer-completion-table
nil nil #'string=))
(contact (process-contact process)))
(if (consp contact)
(format " %s:%s" (car contact) (cadr contact))
(format " %S" contact))))))
(connection-name (completing-read prompt connections-data)))
(let ((connection (cl-find connection-name slime-net-processes
:key #'slime-connection-name
:test #'string=)))
(or connection
(error "Unknown SLIME connection %S" connection-name)))))
</code></pre>
<p>Then use it to select a connection as the current one:</p>
<pre><code class="language-lisp">(defun g-slime-switch-connection ()
(interactive)
(let ((connection (g-slime-select-connection "Switch to connection: ")))
(slime-select-connection connection)
(message "Using connection %s" (slime-connection-name connection))))
</code></pre>
<p>I bind this function to <code>C-c C-s c</code>.</p>
<p>In a perfect world, we could format nice columns in the prompt and highlight
the current connection, but the <code>completing-read</code> interface is really limited,
and I did not want to use an external package such as Helm.</p>
<h2 id="stopping-implementations">Stopping implementations</h2>
<p>Sometimes it is necessary to stop an implementations and kill all associated
buffers. It is not something I use a lot; but when I need it, it is
frustrating to have to switch to the REPL buffer, run <code>slime-quit-lisp</code>, then
kill the REPL buffer manually.</p>
<p>Adding this feature is trivial with the <code>g-slime-select-connection</code> defined
earlier:</p>
<pre><code class="language-lisp">(defun g-slime-kill-connection ()
(interactive)
(let* ((connection (g-slime-select-connection "Kill connection: "))
(repl-buffer (slime-repl-buffer nil connection)))
(when repl-buffer
(kill-buffer repl-buffer))
(slime-quit-lisp-internal connection 'slime-quit-sentinel t)))
</code></pre>
<p>Finally I bind this function to <code>C-c C-s k</code>.</p>
<p>It is now much more comfortable to manage multiple implementations.</p>
lispImproving Git diffs for Lisp
https://www.n16f.net/blog/improving-git-diffs-for-lisp/
Sun, 08 Jan 2023 18:00:00 +0000https://www.n16f.net/blog/improving-git-diffs-for-lisp/[email protected] (Nicolas Martyanoff)<p>All my code is stored in various Git repositories. When Git formats a diff
between two objects, it generates a list of hunks, or groups of changes.</p>
<p>Each hunk can be displayed with a title which is automatically extracted. Git
ships with support for multiple languages, but Lisp dialects are not part of
it. Fortunately Git lets users configure their own extraction.</p>
<p>The first step is to identify the language using a pattern applied to the
filename. Edit your Git attribute file at <code>$HOME/.gitattributes</code> and add
entries for both Emacs Lisp and Common Lisp:</p>
<pre><code>*.lisp diff=common-lisp
*.el diff=elisp
</code></pre>
<p>Then edit your Git configuration file at <code>$HOME/.gitconfig</code> and configure the
path of the Git attribute file:</p>
<pre><code>[core]
attributesfile = ~/.gitattributes
</code></pre>
<p>Finally, set the regular expression used to match a top-level function name:</p>
<pre><code>[diff "common-lisp"]
xfuncname="^\\((def\\S+\\s+\\S+)"
[diff "elisp"]
xfuncname="^\\((((def\\S+)|use-package)\\s+\\S+)"
</code></pre>
<p>For Lisp dialects, we do not just identify function names: it is convenient to
identify hunks for all sorts of top-level definitions. We use a regular
expression which captures the first symbol of the form and the name that
follows.</p>
<p>Of course you can modifiy these expressions to identify more complex top-level
forms. For example, for Emacs Lisp, I also want to identify <code>use-package</code>
expressions.</p>
<p>You can see the result in all tools displaying Git diffs, for example in
Magit with Common Lisp code:</p>
<p><img src="./common-lisp-diff.png" alt="Common Lisp diff"></p>
<p>Or for my Emacs configuration file:</p>
<p><img src="./elisp-diff.png" alt="Emacs Lisp diff"></p>
<p>Hunk titles, highlighted in blue, now contain the type and name of the
top-level construction the changes are associated with.</p>
<p>A simple change, but one which really helps reading diffs.</p>
lispemacsgitConfiguring SLIME cross-referencing
https://www.n16f.net/blog/configuring-slime-cross-referencing/
Wed, 28 Dec 2022 18:00:00 +0000https://www.n16f.net/blog/configuring-slime-cross-referencing/[email protected] (Nicolas Martyanoff)<p>The <a href="https://slime.common-lisp.dev/">SLIME</a> Emacs package for Common Lisp
supports cross-referencing: one can list all references pointing to a symbol,
move through this list and jump to the source code of each reference.</p>
<h2 id="removing-automatic-reference-jumps">Removing automatic reference jumps</h2>
<p>While cross-referencing is very useful, the default configuration is
frustrating: moving through the list in the Emacs buffer triggers the jump to
the reference under the cursor. If you are interested in a reference in the
middle of the list, you will have to move to it, opening multiple buffers you
do not care about as a side effect. I finally took the time to fix it.</p>
<p>Key bindings for <code>slime-ref-mode</code> mode are stored in the <code>slime-xref-mode-map</code>
keymap. After a quick look in <code>slime.el</code>, it is easy to remove bindings for
<code>slime-xref-prev-line</code> and <code>slime-xref-next-line</code>:</p>
<pre><code class="language-lisp">(define-key slime-xref-mode-map (kbd "n") nil)
(define-key slime-xref-mode-map [remap next-line] nil)
(define-key slime-xref-mode-map (kbd "p") nil)
(define-key slime-xref-mode-map [remap previous-line] nil)
</code></pre>
<p>If you are using <code>use-package</code>, it is even simpler:</p>
<pre><code class="language-lisp">(use-package slime
(:map slime-xref-mode-map
(("n")
([remap next-line])
("p")
([remap previous-line]))))
</code></pre>
<h2 id="changing-the-way-references-are-used">Changing the way references are used</h2>
<p>SLIME supports two ways to jump to a reference:</p>
<ol>
<li>With <code>return</code> or <code>space</code>, it spawns a buffer containing the source file and
close the cross-referencing buffer.</li>
<li>With <code>v</code>, it spawns the source file buffer but keeps the cross-referencing
buffer open and keeps it current.</li>
</ol>
<p>This is not practical to me, so I made a change. The default action, triggered
by <code>return</code>, now keeps the cross-referencing buffer open and switches to the
source file in the same window. This way, I can switch back to the
cross-referencing buffer with <code>C-x b</code> to select another reference without
spawning buffers in other windows (I do not like having my windows hijacked by
commands).</p>
<p>To do that, I need a new function:</p>
<pre><code class="language-lisp">(defun g-slime-show-xref ()
"Display the source file of the cross-reference under the point
in the same window."
(interactive)
(let ((location (slime-xref-location-at-point)))
(slime-goto-source-location location)
(with-selected-window (display-buffer-same-window (current-buffer) nil)
(goto-char (point))
(g-recenter-window))))
</code></pre>
<p>Note the use of <code>g-recenter-window</code>, a custom function to <a href="https://www.n16f.net/blog/eye-level-window-centering-in-emacs/">move the current
point at eye level</a>. Feel free to use
the builtin <code>recenter</code> function instead.</p>
<p>I then bind the function to <code>return</code> and remove other bindings:</p>
<pre><code class="language-lisp">(define-key slime-xref-mode-map (kbd "RET") 'g-slime-show-xref)
(define-key slime-xref-mode-map (kbd "SPC") nil)
(define-key slime-xref-mode-map (kbd "v") nil)
</code></pre>
<p>Much better now!</p>
lispFixing unquote-splicing behaviour with Paredit
https://www.n16f.net/blog/fixing-unquote-splicing-behaviour-with-paredit/
Tue, 13 Dec 2022 18:00:00 +0000https://www.n16f.net/blog/fixing-unquote-splicing-behaviour-with-paredit/[email protected] (Nicolas Martyanoff)<p><a href="http://paredit.org/">Paredit</a> is an Emacs package for structural editing. It
is particularly useful in Lisp languages to manipulate expressions instead of
just characters.</p>
<p>One of the numerous little features of Paredit is the automatic insertion of a
space character before a delimiting pair. For example, if you are typing
<code>(length</code>, typing <code>(</code> will have Paredit automatically insert a space character
before the opening parenthesis, to produce the expected <code>(length (</code> content.</p>
<p>Paredit is smart enough to avoid doing so after quote, backquote or comma
characters, but not after an unquote-splicing sequence (<code>,@</code>) which is quite
annoying in languages such as Scheme or Common Lisp. As almost always in
Emacs, this behaviour can be customized.</p>
<p>Paredit decides whether to add a space or not using the
<code>paredit-space-for-delimiter-p</code> function, ending up with applying a list of
predicates from <code>paredit-space-for-delimiter-predicates</code>.</p>
<p>Let us add our own. For more flexibility, we will start by defining a list of
prefixes which are not to be followed by a space:</p>
<pre><code class="language-lisp">(defvar g-paredit-no-space-prefixes (list ",@"))
</code></pre>
<p>We then write our predicate which simply checks if we are right after one of
these prefixes:</p>
<pre><code class="language-lisp">(defun g-paredit-space-for-delimiter (endp delimiter)
(let ((point (point)))
(or endp
(seq-every-p
(lambda (prefix)
(and (> point (length prefix))
(let ((start (- point (length prefix)))
(end point))
(not (string= (buffer-substring start end) prefix)))))
g-paredit-no-space-prefixes))))
</code></pre>
<p>Finally we add a Paredit hook to append our predicate to the list:</p>
<pre><code class="language-lisp">(defun g-init-paredit-space-for-delimiter ()
(add-to-list 'paredit-space-for-delimiter-predicates
'g-paredit-space-for-delimiter))
(add-hook 'paredit-mode-hook 'g-init-paredit-space-for-delimiter)
</code></pre>
<p>Not only does it fix the problem for unquote-slicing, but it makes it easy to
add new prefixes. For example I immediately added <code>#p</code> (used for pathnames in
Common Lisp, e.g. <code>#p"/usr/bin/"</code>) to the list.</p>
lispemacsSLIME compilation tips
https://www.n16f.net/blog/slime-compilation-tips/
Mon, 12 Dec 2022 18:00:00 +0000https://www.n16f.net/blog/slime-compilation-tips/[email protected] (Nicolas Martyanoff)<p>I recently went back to Common Lisp to
<a href="https://github.com/galdor/advent-of-code-2022">solve</a> the daily problems of
the <a href="https://adventofcode.com/2022">Advent of Code</a>. Of course it started with
installing and configuring <a href="https://slime.common-lisp.dev/">SLIME</a>, the main
major mode used for Common Lisp development in Emacs.</p>
<p>The most useful feature of SLIME is the ability to load sections of code into
the Common Lisp implementation currently running. One can use <code>C-c C-c</code> to
evaluate the current top-level form, and <code>C-c C-k</code> to reload the entire file,
making incremental development incredibly convenient.</p>
<p>However I found the default configuration frustrating. Here are a few tips
which made my life easier.</p>
<h2 id="removing-the-compilation-error-prompt">Removing the compilation error prompt</h2>
<p>If the Common Lisp implementation fails to compile the file, SLIME will ask
the user if they want to load the fasl file (i.e. the compiled form of the
file) anyway.</p>
<p>I cannot find a reason why one would want to load the ouput of a file that
failed to compile, and having to decline every time is quite annoying.</p>
<p>Disable the prompt by setting <code>slime-load-failed-fasl</code> to <code>'never</code>:</p>
<pre><code class="language-lisp">(setq slime-load-failed-fasl 'never)
</code></pre>
<h2 id="removing-the-slime-compilation-buffer-on-success">Removing the SLIME compilation buffer on success</h2>
<p>When compilation fails, SLIME creates a new window containing the diagnostic
reported by the Common Lisp implementation. I use <code>display-buffer-alist</code> to
make sure the window is displayed on the right side of my three-column split,
and fix my code in the middle column.</p>
<p>However if the next compilation succeeds, SLIME updates the buffer to indicate
the absence of error, but keeps the window open even though it is not useful
anymore, meaning that I have to switch to it and close it with <code>q</code>.</p>
<p>One can look at the <code>slime-compilation-finished</code> function to see that SLIME
calls the function referenced by the <code>slime-compilation-finished-hook</code>
variable right after the creation or update of the compilation buffer. The
default value is <code>slime-maybe-show-compilation-log</code> which does not open a new
window if there is no error, but does not close an existing one.</p>
<p>Let us write our own function and use it:</p>
<pre><code class="language-lisp">(defun g-slime-maybe-show-compilation-log (notes)
(with-struct (slime-compilation-result. notes successp)
slime-last-compilation-result
(when successp
(let ((name (slime-buffer-name :compilation)))
(when (get-buffer name)
(kill-buffer name))))
(slime-maybe-show-compilation-log notes)))
(setq slime-compilation-finished-hook 'g-slime-maybe-show-compilation-log)`
</code></pre>
<p>Nothing crazy here, we obtain the compilation status (in a very SLIME-specific
way, <code>with-struct</code> is not a standard Emacs Lisp macro) and kill the
compilation buffer if there is one while compilation succeeded.</p>
<h2 id="making-compilation-less-verbose">Making compilation less verbose</h2>
<p>Common Lisp specifies two variables, <code>*compile-verbose*</code> and <code>*load-verbose*</code>,
which control how much information is displayed during compilation and loading
respectively.</p>
<p>My implementation of choice, <a href="http://sbcl.org/">SBCL</a>, is quite chatty by
default. So I always set both variables to <code>nil</code> in my <code>$HOME/.sbclrc</code> file.</p>
<p>However SLIME forces <code>*compile-verbose*</code>; this is done in SWANK, the Common
Lisp part of SLIME. When compiling a file, SLIME instructs the running Common
Lisp implementation to execute <code>swank:compile-file-for-emacs</code> which forces
<code>*compile-verbose*</code> to <code>t</code> around the call of a list of functions susceptible
to handle the file. The one we are interested about is
<code>swank::swank-compile-file*</code>.</p>
<p>First, let us write some Common Lisp code to replace the function with a wrapper
which sets <code>*compile-verbose*</code> to <code>nil</code>.</p>
<pre><code class="language-lisp">(let ((old-function #'swank::swank-compile-file*))
(setf (fdefinition 'swank::swank-compile-file*)
(lambda (pathname load-p &rest options &key policy &allow-other-keys)
(declare (ignore policy))
(let ((*compile-verbose* nil))
(apply old-function pathname load-p options)))))
</code></pre>
<p>We save it to a file in the Emacs directory.</p>
<p>In Emacs, we use the <code>slime-connected-hook</code> hook to load the code into the
Common Lisp implementation as soon as Slime is connected to it:</p>
<pre><code class="language-lisp">(defun g-slime-patch-swank-compilation-function ()
(let* ((path (expand-file-name "swank-patch-compilation-function.lisp"
user-emacs-directory))
(lisp-path (slime-to-lisp-filename path)))
(slime-eval-async `(swank:load-file ,lisp-path))))
(add-hook 'slime-connected-hook 'g-slime-patch-swank-compilation-function)
</code></pre>
<p>Quite a hack, but it works.</p>
lispLocal CLHS access in Emacs
https://www.n16f.net/blog/local-clhs-access-in-emacs/
Wed, 01 Jan 2020 18:00:00 +0000https://www.n16f.net/blog/local-clhs-access-in-emacs/[email protected] (Nicolas Martyanoff)<p>The CLHS, or Common Lisp HyperSpec, is one of the most important resource for
any Common Lisp developer. It is derived from the official Common Lisp
standard, and contains documentation for every aspect of the language.</p>
<p>While it is currently made available
<a href="http://www.lispworks.com/documentation/HyperSpec/Front/index.htm">online</a> by
<a href="http://www.lispworks.com/documentation/common-lisp.html">LispWorks</a>, it can
be useful to be able to access it locally, for example when you do not have
any internet connection available.</p>
<p>For this purpose, LispWorks provides an
<a href="ftp://ftp.lispworks.com/pub/software_tools/reference/HyperSpec-7-0.tar.gz">archive</a>
which can be downloaded and browsed offline.</p>
<p>While the HyperSpec is valuable on its own, the
<a href="https://common-lisp.net/project/slime/">SLIME</a> Emacs mode provides various
functions to make it even more useful.</p>
<p>I have found the following functions particularily useful:</p>
<ul>
<li><code>slime-documentation-lookup</code>, or <code>C-c C-d h</code> to browse the
documentation associated with a symbol.</li>
<li><code>common-lisp-hyperspec-format</code>, or <code>C-c C-d ~</code>, to lookup <code>format</code> control
characters.</li>
<li><code>common-lisp-hyperspec-glossary-term</code>, or <code>C-c C-d g</code>, to access terms in
the
<a href="http://www.lispworks.com/documentation/lw50/CLHS/Front/index.htm">glossary</a>.</li>
</ul>
<p>With the default configuration, Emacs will use the online HyperSpec. You can
have it use a local copy by setting <code>common-lisp-hyperspec-root</code> to a file
URI. For example, if you downloaded the content of the CLHS archive to
<code>~/common-lisp/</code>:</p>
<pre><code class="language-lisp">(setq common-lisp-hyperspec-root
(concat "file://" (expand-file-name "~/common-lisp/HyperSpec/")))
</code></pre>
<p>And if you configure Emacs to use the
<a href="https://www.gnu.org/software/emacs/manual/html_mono/eww.html">EWW</a> web
browser, you can work with the CLHS without leaving your editor.</p>
lispemacsASDF in Common Lisp scripts
https://www.n16f.net/blog/asdf-in-common-lisp-scripts/
Mon, 30 Dec 2019 18:00:00 +0000https://www.n16f.net/blog/asdf-in-common-lisp-scripts/[email protected] (Nicolas Martyanoff)<p>The usual way to develop programs in Common Lisp is to use
<a href="https://common-lisp.net/project/slime/">SLIME</a> in Emacs, which starts an
implementation and provides a REPL. When a program needs to be running in
production, one can either execute it from source or compile it to an
executable core, for example with <code>sb-ext:save-lisp-and-die</code> in SBCL.</p>
<p>While executable cores works well for conventional applications, they are less
suitable for small scripts which should be easy to run without having to build
anything.</p>
<p>Writing a basic script with SBCL is easy:</p>
<pre><code class="language-lisp">#!/bin/sh
#|
exec sbcl --script "$0" "$@"
|#
(format t "Hello world!~%")
</code></pre>
<p>Since UNIX shebangs cannot be used to run commands with more than one
argument, it is impossible to call SBCL directly (it requires the <code>--script</code>
argument, and <code>#!/usr/bin/env sbcl --script</code> contains two arguments). However
it is possible to start as a simple shell script and just execute SBCL with
the right arguments. And since we can include any shell commands, it is
possible to support multiple Common Lisp implementations depending on the
environment.</p>
<p>This method works. But if your script has any dependency, configuring
<a href="https://common-lisp.net/project/asdf/">ASDF</a> can be tricky. ASDF can pick up
system directory paths from <a href="https://common-lisp.net/project/asdf/asdf.html#Controlling-where-ASDF-searches-for-systems">multiple
places</a>,
and you do not want your program to depend on your development environment. If
you run your script in a CI environment or a production system, you will not
have access to your ASDF configuration and your systems.</p>
<p>Fortunately, ASDF makes it possible to manually configure the source registry
at runtime using <code>asdf:initialize-source-registry</code>, giving you total control
on the places which will be used to find systems.</p>
<p>For example, if your Common Lisp systems happen to be stored in a <code>systems</code>
directory at the same level as your script, you can use the <code>:here</code>
<a href="https://common-lisp.net/project/asdf/asdf.html#The-here-directive">directive</a>:</p>
<pre><code class="language-lisp">#!/bin/sh
#|
exec sbcl --script "$0" "$@"
|#
(require 'asdf)
(asdf:initialize-source-registry
`(:source-registry :ignore-inherited-configuration
(:tree (:here "systems"))))
</code></pre>
<p>And if you store all your systems in a Git repository, you can use
<a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules">submodules</a> to include a
<code>systems</code> directory in every project, making it simple to manage the systems
you need and their version. Additionally, anyone with an implementation
installed, SBCL in this example, can now execute these scripts without having
to install or configure anything. This is quite useful when you work with
people who do not know Common Lisp.</p>
<p>Of course, you can use the same method when building executables: just create
a script whose only job is to setup ASDF, load your program, and dump an
executable core. This way, you can make sure you control exactly which set of
systems is used. And it can easily be run in a CI environment.</p>
lisp