Nicolas Martyanoff – Brain dump Brain dump en-us [email protected] (Nicolas Martyanoff) [email protected] (Nicolas Martyanoff) https://www.n16f.net/tags/lisp/index.xml Sun, 19 Nov 2023 00:00:00 +0000 Interactive Common Lisp development https://www.n16f.net/blog/interactive-common-lisp-development/ Sun, 19 Nov 2023 18:00:00 +0000 https://www.n16f.net/blog/interactive-common-lisp-development/ [email protected] (Nicolas Martyanoff) <p>Common Lisp programming is often presented as &ldquo;interactive&rdquo;. 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 &ldquo;special&rdquo; 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 &ldquo;once a name has been declared by defconstant to be constant, any further assignment to or binding of that special variable is an error&rdquo;, 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 &ldquo;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&rdquo;. 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 &ldquo;a macro is essentially a function from forms to forms&rdquo;), 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 &quot;FOO&quot;)</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 &quot;FOO&quot;)</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 &quot;BAR&quot;)</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 &quot;BAR&quot;)</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 &quot;FOO&quot;)</code> correctly refers to the external symbol in the <code>FOO</code> package and makes it internal again. <code>(UNEXPORT 'BAR &quot;FOO&quot;)</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 &ldquo;is not consistent&rdquo; (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 &quot;~A is an integer&quot; a)) (:method ((a float)) (format nil &quot;~A is a float&quot; a))) (defmethod foo ((a string)) (format nil &quot;~S is a string&quot; a)) (defgeneric foo (a) (:method ((a stream)) (format nil &quot;~A is a stream&quot; 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 &ldquo;MOP&rdquo;, for &ldquo;MetaObject Protocol&rdquo;. 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 &ldquo;Common Lisp condition types are in fact CLOS classes, and condition objects are ordinary CLOS objects&rdquo;.</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 &ldquo;MOP&rdquo;.</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.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</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.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </div> lisp Reduce vs fold in Common Lisp https://www.n16f.net/blog/reduce-vs-fold-in-common-lisp/ Fri, 02 Jun 2023 18:00:00 +0000 https://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&gt; (trace +) CL-USER&gt; (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&gt; (trace +) CL-USER&gt; (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>&amp;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) -&gt; 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)) ((&gt;= 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> lisp Counting lines with Common Lisp https://www.n16f.net/blog/counting-lines-with-common-lisp/ Fri, 17 Mar 2023 18:00:00 +0000 https://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 &ldquo;line of code counter&rdquo;, 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) &quot;Return T if PATH is a directory or NIL else.&quot; (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) &quot;Return T if PATH is a hidden file or directory or NIL else.&quot; (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) &quot;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.&quot; (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) &quot;Return a list of all files contained in the directory at PATH or any of its subdirectories.&quot; (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) &quot;Count the number of non-empty lines in the file at PATH. A line is empty if it only contains space or tabulation characters.&quot; (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 '(&quot;a&quot; &quot;bin&quot; &quot;bmp&quot; &quot;cab&quot; &quot;db&quot; &quot;elc&quot; &quot;exe&quot; &quot;gif&quot; &quot;gz&quot; &quot;jar&quot; &quot;jpeg&quot; &quot;jpg&quot; &quot;o&quot; &quot;pcap&quot; &quot;pdf&quot; &quot;png&quot; &quot;ps&quot; &quot;rar&quot; &quot;svg&quot; &quot;tar&quot; &quot;tgz&quot; &quot;tiff&quot; &quot;zip&quot;)) (table (make-hash-table :test 'equal))) (dolist (extension extensions table) (setf (gethash extension table) t))) &quot;A hash table containing all file extensions to ignore.&quot;) </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 '((&quot;asm&quot; . assembly) (&quot;s&quot; . assembly) (&quot;adoc&quot; . asciidoc) (&quot;awk&quot; . awk) (&quot;h&quot; . c) (&quot;c&quot; . c) (&quot;hpp&quot; . cpp) (&quot;cpp&quot; . cpp) (&quot;cc&quot; . cpp) (&quot;css&quot; . css) (&quot;el&quot; . elisp) (&quot;erl&quot; . erlang) (&quot;go&quot; . go) (&quot;html&quot; . html) (&quot;htm&quot; . html) (&quot;ini&quot; . ini) (&quot;hs&quot; . haskell) (&quot;java&quot; . java) (&quot;js&quot; . javascript) (&quot;json&quot; . json) (&quot;tex&quot; . latex) (&quot;lex&quot; . lex) (&quot;lisp&quot; . lisp) (&quot;mkd&quot; . markdown) (&quot;md&quot; . markdown) (&quot;rb&quot; . ruby) (&quot;pl&quot; . perl) (&quot;pm&quot; . perl) (&quot;php&quot; . php) (&quot;py&quot; . python) (&quot;sed&quot; . sed) (&quot;sh&quot; . shell) (&quot;bash&quot; . shell) (&quot;csh&quot; . shell) (&quot;zsh&quot; . shell) (&quot;ksh&quot; . shell) (&quot;scm&quot; . scheme) (&quot;sgml&quot; . sgml) (&quot;sql&quot; . sql) (&quot;texi&quot; . texinfo) (&quot;texinfo&quot; . texinfo) (&quot;vim&quot; . vim) (&quot;xml&quot; . xml) (&quot;dtd&quot; . xml) (&quot;xsd&quot; . xml) (&quot;yaml&quot; . yaml) (&quot;yml&quot; . yaml) (&quot;y&quot; . yacc))) (table (make-hash-table :test 'equal))) (dolist (pair pairs table) (setf (gethash (car pair) table) (cdr pair)))) &quot;A hash table containing a symbol identifying the type of a file for each known file extension.&quot;) </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) &quot;Return a symbol identifying the type of the file at PATH, or UNKNOWN if the file extension is not known.&quot; (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) &quot;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.&quot; (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* &quot;~&amp;error while reading ~A: ~A~%&quot; 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 &amp;key (stream *standard-output*)) &quot;Format the line counts in the LINE-COUNTS association list to STREAM.&quot; (declare (type list line-counts) (type stream stream)) (dolist (entry (sort line-counts '&gt; :key 'cdr)) (let ((type (car entry)) (nb-lines (cdr entry))) (format stream &quot;~12A ~8@A~%&quot; (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*) '(&quot;.&quot;)))) (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> lisp Custom Font Lock configuration in Emacs https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/ Fri, 24 Feb 2023 18:00:00 +0000 https://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 &ldquo;definers&rdquo; and &ldquo;keywords&rdquo;, 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>&amp;</code> are highlighted using <code>font-lock-type-face</code>. But lambda list arguments are not types, and symbols starting with <code>&amp;</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>(&quot;\\_&lt;-?[0-9]+\\_&gt;&quot; . font-lock-constant-face)</code> to highlight integers as constants (note the use of <code>\_&lt;</code> and <code>\_&gt;</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)) &quot;The face used to highlight Common Lisp character literals.&quot;) (defface g-cl-standard-function-face '((default :inherit font-lock-keyword-face)) &quot;The face used to highlight standard Common Lisp function symbols.&quot;) (defface g-cl-standard-value-face '((default :inherit font-lock-variable-name-face)) &quot;The face used to highlight standard Common Lisp value symbols.&quot;) </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&lt;))) (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 &quot;#\\\\&quot; lisp-mode-symbol-regexp &quot;\\_&gt;&quot;)) (function-re (concat &quot;(&quot; (regexp-opt g-cl-function-names t) &quot;\\_&gt;&quot;)) (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> emacs lisp Common Lisp implementations in 2023 https://www.n16f.net/blog/common-lisp-implementations-in-2023/ Wed, 22 Feb 2023 18:00:00 +0000 https://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 &ldquo;Personal Edition&rdquo; 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 &ldquo;incident packs&rdquo; 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> lisp Reading files faster in Common Lisp https://www.n16f.net/blog/reading-files-faster-in-common-lisp/ Wed, 15 Feb 2023 18:00:00 +0000 https://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 &ldquo;for a binary file, the length is measured in units of the element type of the stream&rdquo;. 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 (&lt; 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 (&lt; 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> lisp Custom Common Lisp indentation in Emacs https://www.n16f.net/blog/custom-common-lisp-indentation-in-emacs/ Mon, 23 Jan 2023 18:00:00 +0000 https://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) &amp;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 &amp;lambda &amp;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 &amp;rest (&amp;whole 2 &amp;lambda &amp;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 &lt;symbol&gt;)</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) &quot;Set the indentation of SYMBOL to INDENT.&quot; (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> lisp ANSI color rendering in SLIME https://www.n16f.net/blog/ansi-color-rendering-in-slime/ Mon, 16 Jan 2023 18:00:00 +0000 https://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 &quot;~C[1;33mHello world!~C[0m~%&quot; 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> lisp Switching between implementations with SLIME https://www.n16f.net/blog/switching-between-implementations-with-slime/ Thu, 12 Jan 2023 18:00:00 +0000 https://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 (&quot;/usr/bin/sbcl&quot; &quot;--dynamic-space-size&quot; &quot;2048&quot;)) (ccl (&quot;/usr/bin/ccl&quot;)) (ecl (&quot;/usr/bin/ecl&quot;)))) </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>&quot;lisp&quot;</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 &quot;&quot;) </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 &quot; %s:%s&quot; (car contact) (cadr contact)) (format &quot; %S&quot; 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 &quot;Unknown SLIME connection %S&quot; 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 &quot;Switch to connection: &quot;))) (slime-select-connection connection) (message &quot;Using connection %s&quot; (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 &quot;Kill connection: &quot;)) (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> lisp Improving Git diffs for Lisp https://www.n16f.net/blog/improving-git-diffs-for-lisp/ Sun, 08 Jan 2023 18:00:00 +0000 https://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 &quot;common-lisp&quot;] xfuncname=&quot;^\\((def\\S+\\s+\\S+)&quot; [diff &quot;elisp&quot;] xfuncname=&quot;^\\((((def\\S+)|use-package)\\s+\\S+)&quot; </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> lisp emacs git Configuring SLIME cross-referencing https://www.n16f.net/blog/configuring-slime-cross-referencing/ Wed, 28 Dec 2022 18:00:00 +0000 https://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 &quot;n&quot;) nil) (define-key slime-xref-mode-map [remap next-line] nil) (define-key slime-xref-mode-map (kbd &quot;p&quot;) 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 ((&quot;n&quot;) ([remap next-line]) (&quot;p&quot;) ([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 () &quot;Display the source file of the cross-reference under the point in the same window.&quot; (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 &quot;RET&quot;) 'g-slime-show-xref) (define-key slime-xref-mode-map (kbd &quot;SPC&quot;) nil) (define-key slime-xref-mode-map (kbd &quot;v&quot;) nil) </code></pre> <p>Much better now!</p> lisp Fixing unquote-splicing behaviour with Paredit https://www.n16f.net/blog/fixing-unquote-splicing-behaviour-with-paredit/ Tue, 13 Dec 2022 18:00:00 +0000 https://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 &quot;,@&quot;)) </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 (&gt; 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&quot;/usr/bin/&quot;</code>) to the list.</p> lisp emacs SLIME compilation tips https://www.n16f.net/blog/slime-compilation-tips/ Mon, 12 Dec 2022 18:00:00 +0000 https://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 &amp;rest options &amp;key policy &amp;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 &quot;swank-patch-compilation-function.lisp&quot; 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> lisp Local CLHS access in Emacs https://www.n16f.net/blog/local-clhs-access-in-emacs/ Wed, 01 Jan 2020 18:00:00 +0000 https://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 &quot;file://&quot; (expand-file-name &quot;~/common-lisp/HyperSpec/&quot;))) </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> lisp emacs ASDF in Common Lisp scripts https://www.n16f.net/blog/asdf-in-common-lisp-scripts/ Mon, 30 Dec 2019 18:00:00 +0000 https://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 &quot;$0&quot; &quot;$@&quot; |# (format t &quot;Hello world!~%&quot;) </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 &quot;$0&quot; &quot;$@&quot; |# (require 'asdf) (asdf:initialize-source-registry `(:source-registry :ignore-inherited-configuration (:tree (:here &quot;systems&quot;)))) </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