23 Jan 2023
Planet Lisp
Nicolas Martyanoff: Custom Common Lisp indentation in Emacs
While SLIME 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.
In the process of writing my PostgreSQL client in Common Lisp, I wrote a READ-MESSAGE-CASE
macro which reads a message from a stream and execute code depending on the type of the message:
(defmacro read-message-case ((message stream) &rest forms)
`(let ((,message (read-message ,stream)))
(case (car ,message)
(:error-response
(backend-error (cdr ,message)))
(:notice-response
nil)
,@forms
(t
(error 'unexpected-message :message ,message)))))
This macro is quite useful: all message loops can use it to automatically handle error responses, notices, and signal unexpected messages.
But SLIME does not know how to indent READ-MESSAGE-CASE
, so by default it will align all message forms on the first argument:
(read-message-case (message stream)
(:authentication-ok
(return))
(:authentication-cleartext-password
(unless password
(error 'missing-password))
(write-password-message password stream)))
While we want it aligned the same way as HANDLER-CASE
:
(read-message-case (message stream)
(:authentication-ok
(return))
(:authentication-cleartext-password
(unless password
(error 'missing-password))
(write-password-message password stream)))
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 common-lisp-indent-function
property of the symbol.
You can obtain the indentation rule of a Common Lisp symbol easily. For example, executing (get 'defun 'common-lisp-indent-function)
(e.g. in IELM or with eval-expression
) yields (4 &lambda &body)
. This indicates that DEFUN
forms are to be indented as follows:
- The first argument of
DEFUN
(the function name) is indented by four spaces. - The second argument (the list of function arguments) is indented as a lambda list.
- The rest of the arguments are indented based on the
lisp-body-indent
custom variable, which controls the indentation of the body of a lambda form (two spaces by default).
You can refer to the documentation of the common-lisp-indent-function
Emacs function (defined in SLIME of course) for a complete description of the format.
We want READ-MESSAGE-CASE
to be indented the same way as HANDLER-CASE
, whose indentation specification is (4 &rest (&whole 2 &lambda &body))
(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 (as <symbol>)
.
Let us first define a function to set the indentation specification of a symbol:
(defun g-common-lisp-indent (symbol indent)
"Set the indentation of SYMBOL to INDENT."
(put symbol 'common-lisp-indent-function indent))
Then use it for READ-MESSAGE-CASE
:
(g-common-lisp-indent 'read-message-case '(as handler-case))
While it is in general best to avoid custom indentation, exceptions are sometimes necessary for readability. And SLIME makes it easy.
23 Jan 2023 6:00pm GMT
18 Jan 2023
Planet Lisp
TurtleWare: Method Combinations
Table of Contents
- Introduction
- Defining method combinations - the short form
- Defining method combinations - the long form
- Conclusions
Update [2023-01-23]
Christophe Rhodes pointed out that "The Hooker" method combination is not conforming because there are multiple methods with the same "role" that can't be ordered and that have different qualifiers:
Note that two methods with identical specializers, but with different qualifiers, are not ordered by the algorithm described in Step 2 of the method selection and combination process described in Section 7.6.6 (Method Selection and Combination). Normally the two methods play different roles in the effective method because they have different qualifiers, and no matter how they are ordered in the result of Step 2, the effective method is the same. If the two methods play the same role and their order matters, an error is signaled. This happens as part of the qualifier pattern matching in define-method-combination.
http://www.lispworks.com/documentation/HyperSpec/Body/m_defi_4.htm
So instead of using qualifier patterns we should use qualifier predicates. They are not a subject of the above paragraph because of its last sentence (there is also an example in the spec that has multiple methods with a predicate). So instead of
(define-method-combination hooker ()
(... (hook-before (:before*)) ...) ...)
the method combination should use:
(defun hook-before-p (method-qualifier)
(typep method-qualifier '(cons (eql :before) (cons t null))))
(define-method-combination hooker ()
(... (hook-before hook-before-p) ...) ...)
and other "hook" groups should also use predicates.
Another thing worth mentioning is that both ECL and SBCL addressed issues with the qualifier pattern matching and :arguments since the publication of this blog post.
Introduction
Method combinations are used to compute the effective method for a generic function. An effective method is a body of the generic function that combines a set of applicable methods computed based on the invocation arguments.
For example we may have a function responsible for reporting the object status and each method focuses on a different aspect of the object. In that case we may want to append all results into a list:
(defgeneric status (object)
(:method-combination append))
(defclass base-car ()
((engine-status :initarg :engine :accessor engine-status)
(wheels-status :initarg :wheels :accessor wheels-status)
(fuel-level :initarg :fuel :accessor fuel-level))
(:default-initargs :engine 'ok :wheels 'ok :fuel 'full))
(defmethod status append ((object base-car))
(list :engine (engine-status object)
:wheels (wheels-status object)
:fuel (fuel-level object)))
(defclass premium-car (base-car)
((gps-status :initarg :gps :accessor gps-status)
(nitro-level :initarg :nitro :accessor nitro-level))
(:default-initargs :gps 'no-signal :nitro 'low))
(defmethod status append ((object premium-car))
(list :gps (gps-status object)
:nitro (nitro-level object)))
CL-USER> (status (make-instance 'premium-car))
(:GPS NO-SIGNAL :NITRO LOW :ENGINE OK :WHEELS OK :FUEL FULL)
CL-USER> (status (make-instance 'base-car))
(:ENGINE OK :WHEELS OK :FUEL FULL)
The effective method may look like this:
(append (call-method #<method status-for-premium-car>)
(call-method #<method status-for-base-car> ))
Note that append
is a function so all methods are called. It is possible to use other operators (for example a macro and
) and then the invocation of particular methods may be conditional:
(and (call-method #<method can-repair-p-for-premium-car>)
(call-method #<method can-repair-p-for-base-car> ))
Defining method combinations - the short form
The short form allows us to define a method combination in the spirit of the previous example:
(OPERATOR (call-method #<m1>)
(call-method #<m2>)
...)
For example we may want to return as the second value the count of odd numbers:
(defun sum-and-count-odd (&rest args)
(values (reduce #'+ args)
(count-if #'oddp args)))
(define-method-combination sum-and-count-odd)
(defclass a () ())
(defclass b (a) ())
(defclass c (b) ())
(defgeneric num (o)
(:method-combination sum-and-count-odd)
(:method sum-and-count-odd ((o a)) 1)
(:method sum-and-count-odd ((o b)) 2)
(:method sum-and-count-odd ((o c)) 3)
(:method :around ((o c))
(print "haa!")
(call-next-method)))
(num (make-instance 'b)) ;; (values 3 1)
(num (make-instance 'c)) ;; (values 6 2)
Note that the short form supports also around methods. It is also important to note that effective methods are cached, that is unless the generic function or the method combination changes, the computation of the effective method may be called only once per the set of effective methods.
Admittedly these examples are not very useful. Usually we operate on data stored in instances and this is not a good abstraction to achieve that. Method combinations are useful to control method invocations and their results. Here is another example:
(defmacro majority-vote (&rest method-calls)
(let* ((num-methods (length method-calls))
(tie-methods (/ num-methods 2)))
`(prog ((yes 0) (no 0))
,@(loop for invocation in method-calls
append `((if ,invocation
(incf yes)
(incf no))
(cond
((> yes ,tie-methods)
(return (values t yes no)))
((> no ,tie-methods)
(return (values nil yes no))))))
(error "we have a tie! ~d ~d" yes no))))
(define-method-combination majority-vote)
(defclass a () ())
(defclass b (a) ())
(defclass c (b) ())
(defclass d (c) ())
(defgeneric foo (object param)
(:method-combination majority-vote)
(:method majority-vote ((o a) param) nil)
(:method majority-vote ((o b) param) t)
(:method majority-vote ((o c) param) t)
(:method majority-vote ((o d) param) nil))
(foo (make-instance 'a) :whatever) ; (values nil 0 1)
(foo (make-instance 'b) :whatever) ; #<error tie 1 1>
(foo (make-instance 'c) :whatever) ; (values t 2 0)
(foo (make-instance 'd) :whatever) ; #<error tie 2 2>
Defining method combinations - the long form
The long form is much more interesting. It allows us to specify numerous qualifiers and handle methods without any qualifiers at all.
The Hooker
Here we will define a method combination that allows us to define named hooks that are invoked before or after the method. It is possible to have any number of hooks for the same set of arguments (something we can't achieve with the standard :before
and :after
auxiliary methods):
(defun combine-auxiliary-methods (primary around before after)
(labels ((call-primary ()
`(call-method ,(first primary) ,(rest primary)))
(call-methods (methods)
(mapcar (lambda (method)
`(call-method ,method))
methods))
(wrap-after (the-form)
(if after
`(multiple-value-prog1 ,the-form
,@(call-methods after))
the-form))
(wrap-before (the-form)
(if before
`(progn
,@(call-methods before)
,the-form)
the-form))
(wrap-around (the-form)
(if around
`(call-method ,(first around)
(,@(rest around)
(make-method ,the-form)))
the-form)))
(wrap-around (wrap-after (wrap-before (call-primary))))))
(define-method-combination hooker ()
((normal-before (:before))
(normal-after (:after)
:order :most-specific-last)
(normal-around (:around))
(hook-before (:before *))
(hook-after (:after *)
:order :most-specific-last)
(hook-around (:around *))
(primary () :required t))
(let ((around (append hook-around normal-around))
(before (append hook-before normal-before))
(after (append normal-after hook-after)))
(combine-auxiliary-methods primary around before after)))
With this we may define a generic function and associated methods similar to other functions with an extra feature - we may provide named :before
, :after
and :around
methods. Named auxiliary methods take a precedence over unnamed ones. Only after that the specialization is considered. There is one caveat - PCL
-derived CLOS
implementations (clasp
, cmucl
, ecl
, sbcl
) currently ( ) have a bug preventing wildcard qualifier pattern symbol *
from working. So better download ccl
or wait for fixes. Here's an example for using it:
;;; The protocol.
(defgeneric note-buffer-dimensions-changed (buffer w h)
(:method (b w h)
(declare (ignore b w h))
nil))
(defgeneric change-dimensions (buffer w h)
(:method-combination hooker))
;;; The implementation of unspecialized methods.
(defmethod change-dimensions :after (buffer w h)
(note-buffer-dimensions-changed buffer w h))
;;; The stanard class.
(defclass buffer ()
((w :initform 0 :accessor w)
(h :initform 0 :accessor h)))
;;; The implementation for the standard class.
(defmethod change-dimensions ((buffer buffer) w h)
(print "... Changing the buffer size ...")
(setf (values (w buffer) (h buffer))
(values w h)))
(defmethod note-buffer-dimensions-changed ((buffer buffer) w h)
(declare (ignore buffer w h))
(print "... Resizing the viewport ..."))
;;; Some dubious-quality third-party code that doesn't want to interfere with
;;; methods defined by the implementation.
(defmethod change-dimensions :after system (buffer w h)
(print `(log :something-changed ,buffer ,w ,h)))
(defmethod change-dimensions :after my-hook ((buffer buffer) w h)
(print `(send-email! :me ,buffer ,w ,h)))
CL-USER> (defvar *buffer* (make-instance 'buffer))
*BUFFER*
CL-USER> (change-dimensions *buffer* 10 30)
"... Changing the buffer size ..."
"... Resizing the viewport ..."
(LOG :SOMETHING-CHANGED #<BUFFER #x30200088220D> 10 30)
(SEND-EMAIL! :ME #<BUFFER #x30200088220D> 10 30)
10
30
The Memoizer
Another example (this time it will work on all implementations) is optional memoization of the function invocation. If we define a method with the qualifier :memoize
then the result will be cached depending on arguments. The method combination allows also "normal" auxiliary functions by reusing the function combine-auxiliary-methods
from the previous section.
The function ensure-memoized-result
accepts the following arguments:
test
: compare generationsmemo
: a form that returns the current generationcache-key
: a list composed of a generic function and its argumentsform
: a form implementing the method to be called
When the current generation is nil
that means that caching is disabled and we remove the result from the cache. Otherwise we use the test
to compare the generation of a cached value and the current one - if they are the same, then the cached value is returned. Otherwise it is returned.
(defparameter *memo* (make-hash-table :test #'equal))
(defun ensure-memoized-result (test memo cache-key form)
`(let ((new-generation ,memo))
(if (null new-generation)
(progn
(remhash ,cache-key *memo*)
,form)
(destructuring-bind (old-generation . cached-result)
(gethash ,cache-key *memo* '(nil))
(apply #'values
(if (,test old-generation new-generation)
cached-result
(rest
(setf (gethash ,cache-key *memo*)
(list* new-generation (multiple-value-list ,form))))))))))
The method with the qualifier :memoize
is used to compute the current generation key. When there is no such method then the function behaves as if the standard method combination is used. The method combination accepts a single argument test, so it is possible to define different predicates for deciding whether the cache is up-to-date or not.
(define-method-combination memoizer (test)
((before (:before))
(after (:after) :order :most-specific-last)
(around (:around))
(memoize (:memoize))
(primary () :required t))
(:arguments &whole args)
(:generic-function function)
(let ((form (combine-auxiliary-methods primary around before after))
(memo `(call-method ,(first memoize) ,(rest memoize)))
(ckey `(list* ,function ,args)))
(if memoize
(ensure-memoized-result test memo ckey form)
form)))
Now let's define a function with "our" method combination. We will use a counter to verify that values are indeed cached.
(defparameter *counter* 0)
(defgeneric test-function (arg &optional opt)
(:method-combination memoizer eql))
(defmethod test-function ((arg integer) &optional opt)
(list* `(:counter ,(incf *counter*)) arg opt))
CL-USER> (test-function 42)
((:COUNTER 1) 42)
CL-USER> (test-function 42)
((:COUNTER 2) 42)
CL-USER> (defmethod test-function :memoize ((arg integer) &optional (cache t))
(and cache :gen-z))
#<STANDARD-METHOD TEST-FUNCTION :MEMOIZE (INTEGER)>
CL-USER> (test-function 42)
((:COUNTER 3) 42)
CL-USER> (test-function 42)
((:COUNTER 3) 42)
CL-USER> (test-function 42 nil)
((:COUNTER 4) 42)
CL-USER> (test-function 42)
((:COUNTER 3) 42)
CL-USER> (test-function 43)
((:COUNTER 5) 43)
CL-USER> (test-function 43)
((:COUNTER 5) 43)
CL-USER> (defmethod test-function :memoize ((arg (eql 43)) &optional (cache t))
(and cache :gen-x))
#<STANDARD-METHOD TEST-FUNCTION :MEMOIZE ((EQL 43))>
CL-USER> (test-function 43)
((:COUNTER 6) 43)
CL-USER> (test-function 43)
((:COUNTER 6) 43)
CL-USER> (test-function 42)
((:COUNTER 3) 42)
Conclusions
Method combinations are a feature that is often overlooked but give a great deal of control over the generic function invocation. The fact that ccl
is the only implementation from a few that I've tried which got method combinations "right" doesn't surprise me - I've always had an impression that it shines in many unexpected places.
18 Jan 2023 12:00am GMT
16 Jan 2023
Planet Lisp
Nicolas Martyanoff: ANSI color rendering in SLIME
I was working on the terminal output for a Common Lisp logger, and I realized that SLIME does not interpret ANSI escape sequences.
This is not the end of the world, but having at least colors would be nice. Fortunately there is a library to do just that.
First let us install the package, here using use-package
and straight.el
.
(use-package slime-repl-ansi-color
:straight t)
While in theory we are supposed to just add slime-repl-ansi-color
to slime-contribs
, it did not work for me, and I add to enable the minor mode manually.
If you already have a SLIME REPL hook, simply add (slime-repl-ansi-color-mode 1)
. If not, write an initialization function, and add it to the SLIME REPL initialization hook:
(defun g-init-slime-repl-mode ()
(slime-repl-ansi-color-mode 1))
(add-hook 'slime-repl-mode-hook 'g-init-slime-repl-mode)
To test that it works as intended, fire up SLIME and print a simple message using ANSI escape sequences:
(let ((escape (code-char 27)))
(format t "~C[1;33mHello world!~C[0m~%" escape escape))
While it is tempting to use the #\Esc
character, it is part of the Common Lisp standard; therefore we use CODE-CHAR
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.
If everything works well, should you see a nice bold yellow message:
16 Jan 2023 6:00pm GMT
13 Jan 2023
Planet Lisp
Lispjobs: DevOps Engineer | HRL Laboratories | Malibu, CA
Job posting: https://jobs.lever.co/dodmg/85221f38-1def-4b3c-b627-6ad26d4f5df7?lever-via=CxJdiOp5C6
HRL has been on the leading edge of technology, conducting pioneering research and advancing the state of the art. This position is integrated with a growing team of scientists and engineers on HRL's quantum computing research program.
GENERAL DESCRIPTION:
As a DevOps/DevSecOps engineer, you'll be focused on maintaining reliable systems for testing and delivery of HRL's quantum software. (You will not be directly responsible for developing the software or its tests.)
Specifically, you will be responsible for:
* Monitoring the status of CI/CD infrastructure on open and air-gapped networks.
* Building and maintaining infrastructure for synchronizing software between open and air-gapped networks.
* Working closely with developers and IT staff to ensure continued reliability of integration and deployment infrastructure.
* Tracking and vetting software dependencies.
* Looking for and implementing improvements to DevSecOps practices.
Among other candidate requirements, we highly value expertise in Lisp, Python, and C++.
CONFIDENTIALITY NOTICE: The information transmitted in this email, including attachments, is intended only for the person(s) or entity to which it is addressed and may contain confidential, proprietary and/or privileged material exempt from disclosure under applicable law. Any review, retransmission, dissemination or other use of, or taking of any action in reliance upon this information by persons or entities other than the intended recipient is prohibited. If you received this message in error, please contact the sender immediately and destroy any copies of this information in their entirety.
13 Jan 2023 7:36pm GMT
12 Jan 2023
Planet Lisp
Nicolas Martyanoff: Switching between implementations with SLIME
While I mostly use SBCL for Common Lisp development, I regularly switch to CCL or even ECL to run tests.
This is how I do it with SLIME.
Starting implementations
SLIME lets you configure multiple implementations using the slime-lisp-implementations
setting. In my case:
(setq slime-lisp-implementations
'((sbcl ("/usr/bin/sbcl" "--dynamic-space-size" "2048"))
(ccl ("/usr/bin/ccl"))
(ecl ("/usr/bin/ecl"))))
Doing so means that running M-x slime
will execute the first implementation, i.e. SBCL. There are two ways to run other implementations.
First you can run C-u M-x slime
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 inferior-lisp-program
variable, i.e. "lisp"
by default, meaning it has to be deleted manually each time. Therefore I set inferior-lisp-program
to the empty string:
(setq inferior-lisp-program "")
Then you can run C-- M-x slime
(or M-- M-x slime
which is easier to type) to instruct SLIME to use interactive completion (via completing-read
) to let you select the implementations among those configured in slime-lisp-implementations
.
To make my life easier, I bind C-c C-s s
to a function which always prompt for the implementation to start:
(defun g-slime-start ()
(interactive)
(let ((current-prefix-arg '-))
(call-interactively 'slime)))
Using C-c C-s
as prefix for all my global SLIME key bindings helps me remember them.
Switching between multiple implementations
Running the slime
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.
There are two ways to change the current implementation:
- Run
M-x slime-next-connection
. - Run
M-x slime-list-connections
, which opens a buffer listing connections, and lets you choose the current one with thed
key.
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.
All I want is a prompt with completion. So I wrote one.
First we define a function to select a connection among existing one:
(defun g-slime-select-connection (prompt)
(interactive)
(let* ((connections-data
(mapcar (lambda (process)
(cons (slime-connection-name process) process))
slime-net-processes))
(completion-extra-properties
'(:annotation-function
(lambda (string)
(let* ((process (alist-get string minibuffer-completion-table
nil nil #'string=))
(contact (process-contact process)))
(if (consp contact)
(format " %s:%s" (car contact) (cadr contact))
(format " %S" contact))))))
(connection-name (completing-read prompt connections-data)))
(let ((connection (cl-find connection-name slime-net-processes
:key #'slime-connection-name
:test #'string=)))
(or connection
(error "Unknown SLIME connection %S" connection-name)))))
Then use it to select a connection as the current one:
(defun g-slime-switch-connection ()
(interactive)
(let ((connection (g-slime-select-connection "Switch to connection: ")))
(slime-select-connection connection)
(message "Using connection %s" (slime-connection-name connection))))
I bind this function to C-c C-s c
.
In a perfect world, we could format nice columns in the prompt and highlight the current connection, but the completing-read
interface is really limited, and I did not want to use an external package such as Helm.
Stopping implementations
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 slime-quit-lisp
, then kill the REPL buffer manually.
Adding this feature is trivial with the g-slime-select-connection
defined earlier:
(defun g-slime-kill-connection ()
(interactive)
(let* ((connection (g-slime-select-connection "Kill connection: "))
(repl-buffer (slime-repl-buffer nil connection)))
(when repl-buffer
(kill-buffer repl-buffer))
(slime-quit-lisp-internal connection 'slime-quit-sentinel t)))
Finally I bind this function to C-c C-s k
.
It is now much more comfortable to manage multiple implementations.
12 Jan 2023 6:00pm GMT
11 Jan 2023
Planet Lisp
Tim Bradshaw: A case-like macro for regular expressions
I often find myself wanting a simple case
-like macro where the keys are regular expressions. regex-case
is an attempt at this.
I use CL-PPCRE for the usual things regular expressions are useful for, and probably for some of the things they should not really be used for as well. I often find myself wanting a case
like macro, where the keys are regular expressions. There is a contributed package for Trivia which will do this, but Trivia is pretty overwhelming. So I gave in and wrote regex-case
which does what I want.
regex-case
is a case
-like macro. It looks like
(regex-case <thing>
(<pattern> (...)
<form> ...)
...
(otherwise ()
<form> ...))
Here <pattern>
is a literal regular expression, either a string or in CL-PPCRE's s-expression parse-tree syntax for them. Unlike case
there can only be a single pattern per clause: allowing the parse-tree syntax makes it hard to do anything else. otherwise
(which can also be t
) is optional but must be last.
The second form in a clause specifies what, if any, variables to bind on a match. As an example
(regex-case line
("fog\\s+(.*)\\s$" (:match m :registers (v))
...)
...)
will bind m
to the whole match and v
to the substring corresponding to the first register. You can also bind match and register positions. A nice (perhaps) thing is that you can not bind some register variables:
(regex-case line
(... (:registers (_ _ v))
...)
...)
will bind v
to the substring corresponding to the third register. You can use nil
instead of _
.
The current state of regex-case
is a bit preliminary: in particular I don't like the syntax for binding thngs very much, although I can't think of a better one. Currently therefore it's in my collection of toys: it will probably migrate from there at some point.
Currently documentation is here and source code is here.
11 Jan 2023 6:17pm GMT
Nicolas Hafner: Kandria is now out!
Kandria is now finally available for purchase and play!
I recommend buying it on Steam, as the algorithm there will help us bring the game in front of more people, as well. However, if that isn't a possibility for you, there's also options on Itch.io and through Xsolla on our webpage:
I am also live on Steam, Twitch, and YouTube right now, to celebrate the launch! Come on and hang out in the chat: https://stream.shinmera.com
I hope you all enjoy the game, and thank you very much for sticking with us for all this time!
11 Jan 2023 2:02pm GMT
10 Jan 2023
Planet Lisp
Nicolas Hafner: Kandria launches tomorrow!
βKandria launches tomorrow, on Wednesday the 11th, at 15:00 CET / 9:00 EST!
There'll be a launch stream for the occasion as well. It'll be live on Twitch! I'll be happy to answer any questions you may have about the game, and hope to see you there!β
Last opportunity to wishlist the game, too: https://kandria.com/steam
10 Jan 2023 1:08pm GMT
09 Jan 2023
Planet Lisp
vindarel: These Years in Common Lisp: 2022 in review
And 2022 is over. The Common Lisp language and environment are solid and stable, yet evolve. Implementations, go-to libraries, best practices, communities evolve. We don't need a "State of the Ecosystem" every two weeks but still, what happened and what did you miss in 2022?
This is my pick of the most exciting, fascinating, interesting or just cool projects, tools, libraries and articles that popped-up during that time (with a few exceptions that appeared in late 2021).
This overview is not a "State of the CL ecosystem" (HN comments (133)) that I did in 2020, for which you can find complementary comments on HN.
I think this article (of sorts) is definitely helpful for onlookers to Common Lisp, but doesn't provide the full "story" or "feel" of Common Lisp, and I want to offer to HN my own perspective.
And, suffice to say, I tried to talk about the most important things, but this article (of sorts) is by no means a compilation of all new CL projects or all the articles published on the internet. Look on Reddit, Quicklisp releases, Github, and my favourite resources:
- Awesome-cl - a curated list of libraries (there might be more than you think)
- the CL Cookbook
If I had to pick 3 achievements they would be:
- SBCL developments: SBCL is now callable as a shared library. See below in "Implementations".
- a new 3D graphics project: Kons-9: "The idea would be to develop a system along the lines of Blender/Maya/Houdini, but oriented towards the strengths of Common Lisp". And the project progresses at a good pace.
- CLOG, the Common Lisp Omnificent GUI. It's like a GUI framework to create web apps. Based on websockets, it offers a light abstraction to create fully-dynamic web applications, in Common Lisp. It has lots of demos to create websites, web apps, games, and it ships a complete editor. For development, we can connect our Lisp REPL to the browser, and see changes on the fly. The author had a similar commercial product written in Ada, discovered Common Lisp, and is now super active on this project.
Let's go for more.
Thanks to @k1d77a, @Hexstream, @digikar and @stylewarning for their feedback.
Table of Contents
Documentation
A newcomer to Lisp came, asked a question, and suddenly he created a super useful rendering of the specification. Check it out!
- π Common Lisp CommunitySpec (CLCS) - a rendition of the Common Lisp ANSI Specification draft.
- Github: https://github.com/fonol/cl-community-spec
- It is readable, it has syntax highlighting for code snippets, an interactive search bar, it looks modern, it is open.
But that's not all, he also started work on a new Common Lisp editor, built in Rust and Tauri, see below.
We continue to enrich the Common Lisp Cookbook. You are welcome to join, since documention is best built by newcomers for newcomers.
A resurrected project:
Also:
- the revamped Common Lisp Document Repository (CDR) site, "a repository of documents that are of interest to the Common Lisp community. The most important property of a CDR document is that it will never change"
- I am creating a Common Lisp video course on Udemy π₯
- read more about my motivation and follow the project on Github: https://github.com/vindarel/common-lisp-course-in-videos/
- the course has some free videos. If you are a student, drop me a line for a free link.
- direct link to the course.
- Thanks for your support!
Implementations
We saw achievements in at least 7 8 implementations.
- SBCL continues to ship monthly. In 2022:
- π SBCL is now callable as a shared library. See sbcl-librarian below.
- π sb-simd - SIMD programming in SBCL.
- core compression uses zstd instead of zip: compression is about 4 times faster, decompression about two times, compression saves Β±10% of size.
trace
now supports tracing macro functions, compiler-macro functions, individual methods and local functions (flet and labels) (SBCL 2.2.5)- the SBCL repository reached 20,000 commits.
- Prebuilt SBCL binary for Android (Termux) (unofficial)
- Clasp 2.0.0 released
- This is Common Lisp on LLVM.
- Christian Schafmeister talk - brief update about his "molecular lego" supported by his Lisp compiler
- there's less funding than in the 80s, but still funding: "CLASP was supported by The Defense Threat Reduction Agency, The National Institutes of Health, The National Science Foundation".
- ECL:
- LQML: a lightweight ECL binding to QML (both Qt5 and Qt6) derived from EQL5
- tested on the following platforms: Linux, macOS, android, iOS.
- ECL targetting WASM via Emscripten - preliminary support
- LispWorks Personal Edition updated to version 8.0.1, incl. native Apple Silicon version
- Major New release of Allegro Common Lisp Express Edition for 10.1
- Browser-based IDE for Linux and macOS
- no syntax highlighting for the editor though :S
- Applications built using Common Graphics can use browsers for delivery. Firefox, Chrome, Safari, Edge and many other browsers are supported.
- New platforms: aarch64, x86_64
- download: https://franz.com/downloads/clp/survey
- ABCL 1.9.0 was released in May.
- "ABCL 1.9.0 has been best tested on the openjdk8, openjdk11, and openjdk17 runtimes. It will run other places but those are the best supported."
- GNU Common Lisp 2.6.13 released, after 8 years.
New implementation! It's 2022 and people start new CL implementations.
See also:
- LCL, Lua Common Lisp - The goal of this project is to provide an implementation of Common Lisp that can be used wherever an unmodified Lua VM is running.
- not a complete implementation.
They are doing great work to revive a Lisp machine:
Medley Interlisp is a project aiming to restore the Interlisp-D software environment of the Lisp Machines Xerox produced since the early 1980s, and rehost it on modern operating systems and computers. It's unique in the retrocomputing space in that many of the original designers and implementors of major parts of the system are participating in the effort.
Paolo Amoroso blog post: my encounter with Medley Interlisp.
Jobs
I won't list expired job announces, but this year Lispers could apply for jobs in: web development(WebCheckout, freelance announces), cloud service providers (Keepit), big-data analysis (Ravenpack, and chances are they are still hiring)), quantum computing (HLR Laboratories), AI (Mind AI, SRI International), real-time data aggregration and alerting engines for energy systems (3E); for a startup building autism tech (and using CLOG already); there have been a job seeking to rewrite a Python backend to Common Lisp (RIFFIT); there have been some bounties; etc.
Prior Lisp experience was not 100% necessary. There were openings for junior and senior levels, remote and not remote (Australia for "a big corp", U.S., Spain, Ukraine...).
Comes a question:
I remind the reader that most Lisp jobs do not have a public job posting, instead candidates are often found organically on the community channels: IRC, Twitter, Discord, Reddit... or teams simply train their new developer.
In 2022 we added a few companies to the ongoing, non-official list on awesome-lisp-companies. If your company uses Common Lisp, feel free to tell us on an issue or in the comments!
For example, Feetr.io "is entirely Lisp".
Lisp was a conscious decision because it allows a small team to be incredibly productive, plus the fact that it's a live image allows you to connect to it over the internet and poke and prod the current state, which has really allowed a much clearer understanding of the data.
They post SLY screenshots on their Twitter^^
We're using CL in prod for an embedded system for some years now, fairly smooth sailing. It started out as an MVP/prototype so implementation was of no concern, then gained enough velocity and market interest that a rewrite was infeasible. We re-train talent on the job instead.
Pandorabots, or barefootnetworks, designing the Intel Tofino programmable switches, and more.
Projects
- Ultralisp now supports tags. We can browse a list of projects under a tag.
- Ultralisp is a Quicklisp distribution that ships every five minutes.
- see also CLPM for a new package manager.
Language libraries
- Typo: A portable type inference library for Common Lisp, by Marco Heisig.
- Testing:
- fiveam-matchers - An extensible, composable matchers library for FiveAM.
- testiere - TDD-like dev for Common Lisp. Tests are included at the top of a defun/t form. When you recompile your functions interactively, the tests are run.
- melisgl/try test framework. - "it is what we get if we make tests functions and build a test framework on top of the condition system."
- journal - A Common Lisp library for logging, tracing, testing and persistence
- 40ants-critic - a wrapper around LISP-CRITIC which provides a better interface to analyze ASDF systems and a command-line interface.
- CLEDE - the Common Lisp Emacs Development Environment
- "The idea is to supply features that other language with and static analyzer have, like refactoring and code generation."
- easy-macros - An easy way to write 90% of your macros.
- Quicklisp doctor - a program that examines the quicklisp installation.
- more-cffi: Additional helper macros for the cffi project
Editors, online editors, REPLs, plugins
- π Parrot - A cross-platform Common Lisp editor.
- built in Rust with Tauri, CodeMirror, the Slynk server.
- "A hobby project started in Summer 2022. It aims to be an editor for Common Lisp (SBCL), that mostly works out of the box."
- in development, untested on Linux and Mac.
- Alive LSP for VSCode v0.1.9 Β· Add initial step debugger support
- Common Lisp at Riju, a fast online playground for every programming language.
- Codewars (code training platform) now has Common Lisp (SBCL 2.0.9)
- Mobile app "cl-repl" (LQML) to replace "CL REPL" (EQL5)
- β slime-star - SLIME configuration with some extensions pre-installed.
- a Lisp System Browser
- SLIME Doc Contribs
- Quicklisp Systems browsers
- Quicksearch utility
- Slime breakpoints - Inspect objects from their printed representation in output streams.
- custom utilities and menus.
- parachute-browser: A lightweight UI for using the Parachute testing framework in LispWorks
New releases:
- Lem editor 1.10.0: lsp-mode by default, multiple cursors, sql mode, and more.
- Lem is a general purpose editor written in Common Lisp. It works for many languages thanks to its LSP client.
Concurrency
- π New version of the Sento Actor Framework released with a few new goodies in future handling. Nicer syntax and futures can now be mapped.
- in v2.2.0: stashing and replay of messages.
- in v1.12: "Shutdown and stop of actor, actor context and actor system can now wait for a full shutdown/stop of all actors to really have a clean system shutdown."
- Writing distributed apps with cl-etcd
See also lisp-actors, which also does networking. It looks like more of a research project, as it doesn't have unit-tests nor documentation, but it was used for the (stopped) Emotiq blockchain.
Discussions:
- Concurrency: Common Lisp vs Clojure
- Moving from the BEAM to Common Lisp: What are my concurrency options?
Databases
- DuckDB Lisp bindings
- ndbapi: Common Lisp bindings to the C++ NDB API of RonDB
- CLSQL released under a non-restrictive license
- Document Store/DB Implemented in Common Lisp
More choices: awesome-cl#databases.
Delivery tools
There has been outstanding work done there. It is also great to see the different entities working on this. That includes SBCL developers, Doug Katzman of Google, and people at HRL Laboratories (also responsible of Coalton, Haskell-like on top of CL).
Have you ever wanted to call into your Lisp library from C? Have you ever written your nice scientific application in Lisp, only to be requested by people to rewrite it in Python, so they can use its functionality? Or, maybe you've written an RPC or pipes library to coordinate different programming languages, running things in different processes and passing messages around to simulate foreign function calls.
[...] If you prefer using SBCL, you can now join in on the cross-language programming frenzy too.
- π sbcl-librarian - An opinionated interface for creating C- and Python-compatible shared libraries in Common Lisp with SBCL. Requires SBCL version >2.1.10.
- introductory blogpost: Using Lisp libraries from other programming languages - now with SBCL.
- HN comments (67)
- π alien-works-delivery - WIP system for delivering Common Lisp applications as executable bundles. For now it only supports AppImage format for Linux and MSIX for Windows, but .APK for Android and later MacOSX and iOS bundle formats are planned too.
- Support for compiling Common Lisp code using bazel.io, by the CL team at Google.
Games
Kandria launches on Steam on the 11th of January, in two days!
-
cl-defender - Toy Game using Common Lisp and Raylib.
π₯ Kandria trailer.
Graphics, GUIs
We saw the release of fresh bindings for Gtk4.
We had bindings for Qt5... but they are still very rough, hard to install so far.
- CommonQt 5 - Qt5 bindings.
Also:
- I made a Wayland client from scratch for Common Lisp
- vk - CFFI bindings to Vulkan
History:
- Izware Mirai is available on the Internet Archive (no source code)
- Hacking Lisp on Mirai (screencast)
But an awesome novelty of 2022 is Kons-9.
Kons-9, a new 3D graphics project
π A new 3D graphics project: Kons-9.
The idea would be to develop a system along the lines of Blender/Maya/Houdini, but oriented towards the strengths of Common Lisp.
I'm an old-time 3D developer who has worked in CL on and off for many years.
I don't consider myself an expert [...] A little about me: Β· wrote 3D animation software used in "Jurassic Park" Β· software R&D lead on "Final Fantasy: The Spirits Within" movie Β· senior software developer on "The Hobbit" films.
- kons-9, a Common Lisp 3D Graphics Project
- π₯ trailer
- author's blog posts
- and see also his screencasts below.
Interfaces with other languages
- py4cl2-cffi: CFFI based alternative to py4cl2.
- it does one big new thing: it supports passing CL arrays by reference. That means we actually have access to numpy, scipy, and friends.
- "If py4cl2-cffi reaches stability, and I find that the performance of (i) cffi-numpy, (ii) magicl, as well as (iii) a few BLAS functions I have handcrafted for numericals turn out to be comparable, I might no longer have to reinvent numpy." @digikar
- Small update to RDNZL (CL .NET bridge by Edi Weitz)
- forked project, added support for Int16, fixed Int64, re-building the supporting DLLs.
- see also: Bike
- jclass: Common Lisp library for Java class file manipulation
For more, see awesome-cl.
Numerical and scientific
- π new Lisp Stats release
- "emphasis on plotting and polishing of sharp edges. data-frames, array operations, documentation."
- HN comments (55)
- " I've been using lisp-stat in production as part of an algorithmic trading application I wrote. It's been very solid, and though the plotting is (perhaps was, in light of this new release) kinda unwieldy, I really enjoyed using it. Excited to check out the newest release."
- "For example, within Lisp-Stat the statistics routines [1] were written by an econometrician working for the Austrian government (Julia folks might know him - Tamas Papp). It would not be exaggerating to say his job depending on it. These are state of the art, high performance algorithms, equal to anything available in R or Python. So, if you're doing econometrics, or something related, everything you need is already there in the tin."
- "For machine learning, there's CLML, developed by NTT. This is the largest telco in Japan, equivalent to ATT in the USA. As well, there is MGL, used to win the Higgs Boson challenge a few years back. Both actively maintained."
- "For linear algebra, MagicCL was mention elsewhere in the thread. My favourite is MGL-MAT, also by the author of MGL. This supports both BLAS and CUBLAS (CUDA for GPUs) for solutions."
- "Finally, there's the XLISP-STAT archive. Prior to Luke Tierney, the author of XLISP-Stat joining the core R team, XLISP-STAT was the dominate statistical computing platform. There's heaps of stuff in the archive, most at least as good as what's in base R, that could be ported to Lisp-Stat."
- "Common Lisp is a viable platform for statistics and machine learning. It isn't (yet) quite as well organised as R or Python, but it's all there."
- numericals - Performance of NumPy with the goodness of Common Lisp
numericals
is "a high performance basic math library with vectorized elementary and transcendental functions. It also provides support for a numpy-like array object throughdense-arrays
and static-dispatch through the CLTL2 API."- the post is about numericals, dense-arrays, magicl, numcl, py4cl/2...
- the author's comparison and wish-list of features for a Common Lispy approach to a (better) Numpy
- MGL-MAT - a library for working with multi-dimensional arrays which supports efficient interfacing to foreign and CUDA code. BLAS and CUBLAS bindings are available.
- hbook - Text-based histograms in Common Lisp inspired by the venerable HBOOK histogramming library from CERN.
New releases:
- Maxima 5.46 was released.
- "Maxima is a Computer Algebra System comparable to commercial systems like Mathematica and Maple. It emphasizes symbolic mathematical computation: algebra, trigonometry, calculus, and much more."
- see its frontends, for example WxMaxima.
Call to action:
Web
Screenshotbot (Github) was released. It is "a screenshot testing service to tie with your existing Android, iOS and Web screenshot tests".
It is straightforward to install with a Docker command. They offer more features and support with their paid service.
LicensePrompt was released. It is "a single place to track all recurring software and IT expenses and send relevant reminders to all interested people". It's built in CL, interface with HTMX.
- Lisp Interpreter in a browser using WASM
- Show HN: Common Lisp running natively over WebAssembly for the first time
Libraries:
- jingle: Common Lisp web framework with bells and whistles (based on ningle)
- jingle demo: OpenAPI 3.x spec, Swagger UI, Docker and command-line interface app with jingle.
- ciao: Ciao is an easy-to-use Common Lisp OAuth 2.0 client library. It is a port of the Racket OAuth 2.0 Client to Common Lisp.
- stepster: a web scraping library, on top of Plump and Clss (new in QL)
- openrpc: Automatic OpenRPC spec generation, automatic JSON-RPC client building
- HTTP/2 implementation in Common Lisp
Skeletons:
- cl-cookieweb: my project skeleton to start web projects. Demo in video. I am cheating, the bulk of it was done in 2021.
- "Provides a working toy web app with the Hunchentoot web server, easy-routes, Djula templates, styled with Bulma, based on SQLite, with migrations and an example table definition."
- if you don't know where to start for web dev in CL, enjoy all the pointers of this starter kit and find your best setup.
- see also this web template by @dnaeon, and check out all his other Lisp libraries.
Bindings:
- π lisp-pay: Wrappers around various Payment Processors (Paypal, Stripe, Coinpayment)
- lunamech-matrix-api: Implementation of the Matrix API, LunaMech a Matrix bot
Apps:
- Ackfock - a platform of mini agreements and mini memos of understanding (built with CLOG, closed source).
- todolist-cl: a nice looking todolist with a web UI, written in Common Lisp (and by a newcomer to CL, to add credit)
I don't have lots of open-source apps to show. Mines are running in production and all is going well. I share everything on my blog posts. I also have an open-source one in development, but that's for the 2023 showcase :D
CLOG
π The awesome novelty of 2022 I spoke of in the introduction is CLOG, the Common Lisp Omnificent GUI:
- Native Desktop Executables for CLOG
- CLOG and CLOG Builder Release 1.8 - IDE for Common Lisp and CLOG
- CLOG on Android, APK download
I know of one open-source consequent CLOG app: mold-desktop, in development.
I'm developing a programmable desktop and a bookmarks manager application with CLOG. I think I know one of the things that make CLOG user interfaces so easy to develop. It is that they are effortlessly composable. That's it for now :)
@mmontone
New releases
There are lots of awesome projects in music composition, including OpusModus and OpenMusic which saw new releases. I also like to cite ScoreCloud, a mobile app built with LispWorks, where you whistle, sing or play your instrument, and the app writes the music score O_o
- π΅ Opusmodus 3.0, Music Composition System, macOS Intel & Apple Silicon native, based on LispWorks, just released
- π΅ OpenMusic 7.0, now also native for M1 Macs, visual programming language designed for music composition
- Consfigurator, a Common Lisp based declarative configuration management system, reaches v1.0
- April 1.0 released - APL in Common Lisp.
- cl-cmark approaching stable release
- cl-git: a Common Lisp CFFI interface to the libgit2 library
- tinmop 0.9.9.1, a terminal client for gopher, gemini, kami and pleroma.
- look here for how to build full-screen terminal applications in Common Lisp.
- clingon - new release, new features (command-line options parser)
- a full-featured options parser. Supports sub-commands, bash and zsh completions, many arguments types...
See awesome-cl and Cliki for more.
(re) discoveries
- The math pastebin Mathb.in is written in Common Lisp
- TIL that PTC's Creo Elements/Direct 3D CAD modeling software has a free version for Windows. "7+ million lines of Common Lisp code", used by Eterna for their watches.
- that's a huge software. 7 million lines. There's a free version to try out!
Articles
Graphics
- Playing with raycasting
- Playing with graphics - a simple game with SDL2
- Multipass Translator for CLIM
- McCLIM: Implementing a simpleminded REPL from scratch
Tooling
- Debugging Lisp: trace options, break on conditions
- HN comments (28), I learned, again, new tips.
- Developing Common Lisp using GNU Screen, Rlwrap, and Vim
- again, learned new tips. The
--remember
flag ofrlwrap
allows to TAB-complete whatever was previously typed at the prompt. That's dumb autocompletion, but autocompletion nonetheless. My summary.
- again, learned new tips. The
- Configuring Slime cross-referencing
- SLIME Compilation Tips
- How to save lisp and die from Slime or Sly. trivial-dump-core
- that may be super useful, at least if answers a question everybody has one time or another. I should try it more.
- How to approach a Lisp sandbox environment
- log4cl questions
- A StumpWM debugger
- Windows environment for SBCL
- Securing Quicklisp through mitmproxy
- because Quicklisp doesn't use HTTPS. Here's how to add security. The new CLPM uses HTTPS.
- Lisp in Vim
Scripting
- Day 3: Roswell: Common Lisp scripting, by E. Fukamachi.
- Qlot tutorial with Docker
- Qlot v1.0.0 was officially released.
- Day 4: Roswell: How to make Roswell scripts faster
- Day 5: Roswell: Hidden feature of "-s" option
- Writing scripts in lisp?
- a legitimate question. Many tips.
- where I show my very new and un-released CIEL scripting facility. Batteries included for Common Lisp. That's for 2023 but you can be an early adopter :p
- Lisp for Unix-like systems
- in particular, read this answer by /u/lispm to learn about past and present attempts and solutions.
- will the OP manage to make WCL work? "WCL: Delivering efficient Common Lisp applications under Unix", an implementation "tailored to zillions of small Lisp programs. Make sure that much of Lisp is shared memory. Be able to create shared memory libraries/applications".
- (SBCL) Is there a way to detect if lisp code was run by -script vs interactively?
Around the language
- π Using Coalton to Implement a Quantum Compiler
- Coalton brings Haskell-like type checking on top of Common Lisp. Groundbreaking. They use it for their quantum compiler.
- it is still under development and there is their Discord to talk about it.
- Eliminating CLOS for a 4.5x speedup
- sometimes it's easy, use a struct.
- The empty list. "Someone has been getting really impressively confused and cross on reddit about empty lists, booleans and so on in Common Lisp, which led us to a discussion about what the differences really are. Here's a summary which we think is correct."
- Are there any tutorials or examples people would recommend to get started with unit testing common lisp?
- Fun with Macros: Do-File
- Series tips and tricks
- STATIC-LET, Or How I Learned To Stop Worrying And Love LOAD-TIME-VALUE
History:
Web related
- Common Lisp web development and the road to a middleware
- π Lisp for the web: building one standalone binary with foreign libraries, templates and static assets
- what I managed to do here, with the help of the community, represents a great step forward for my Lisp web stack.
- I can build a standalone binary for my web app, containing all static assets (a patch was required for the Djula HTML templating engine), so I can just
rsync
it to my server and it works. - a standalone binary is easy to integrate into an Electron window. Stay tuned.
- Lisp for the web: deploying with Systemd, gotchas and solutions
- all the little gotchas I faced are now google-able.
- Woo: a high-performance Common Lisp web server, by E. Fukamachi.
- HTTP over unix sockets in Common Lisp. By the same author:
- Using TailwindCSS in Common Lisp web apps without Node.js tooling
- Using SVGs in Common Lisp web apps with Djula
- Create a Common Lisp Web app using ningle
- Using environment variables with cl-dotenv in Common Lisp
- Mito: An ORM for Common Lisp
- Running docker commands from Common Lisp REPLs
- he also put up several little examples of a Common Lisp web app with HTMX, such as cl-beer. His stack: Caveman, Djula templates, HTMX for the interactivity. Thanks again, they are super useful.
- A new static site generator
- Web Development with CL Backend and ClojureScript Frontend
- Update on gRPC Server - gRPC now has the ability to create servers based on protocol buffer service descriptors.
- π Almost 600,000 entries per second from Lisp to Apache Accumulo over Apache Thrift
- Writing an interactive web app in Common Lisp: Hunchentoot then CLOG
- Review of CL Json Libraries, updated 15 Jan 2022
Call for action:
Other articles
- Astronomical Calculations for Hard Science Fiction in Common Lisp, by Fernando Borretti
- HN comments (42) (yes, 42)
- we miss you Fernando. Oh, he just released a new library: lcm: "Manage your system configuration in Common Lisp. Think of it as Ansible, for your localhost"
- π A LISP REPL Inside ChatGPT
- FreeBSD Jail Quick Setup with Networking for a Common Lisp environment
- Proceedings, Seventeenth International Workshop on the ACL2 Theorem Prover and its Applications, 2022.
- ACL2 v8.5 was released this year too.
- Case study - house automation tool - part 2 - getting serial
Screencasts and podcasts
New videos by me:
- π Debugging Lisp: fix and resume a program from any point in stack π₯
- How to request the GitHub API: demo of Dexador, Jonathan, Shasht (and Serapeum), livecoding in Emacs and SLIME.
- How to create, run, build and load a new Lisp project, with my fully-featured project skeleton.
- Interactively Fixing Failing Tests in Common Lisp
by Gavin Freeborn:
- Why Learn Lisp In 2022?
- Why Are Lisp Macros So Great!?
- Lisp Is More Than Just A Language It's An Environment
- Rewrite Your Scripts In LISP - with Roswell
- Creating Your First Lisp Project - Quicklisp, asdf, and Packages
- Unleash the REPL with SLY
- Lem: what if Emacs was multithreaded
KONS-9 series:
- Kaveh's Common Lisp Lesson 01: points and shapes
- Common Lisp OpenGL programming tutorial #12 - Animation & Hierarchies
CLOG series:
- CLOG Extra 3 - The CLOG Project System for CLOG and NON-CLOG projects
- CLOG Extra 4 - All about panels
- Repl Style. Dev visually with CLOG Builder
- "This is amazing work! Having Pharo/Smalltalk capabilities with Common Lisp is fascinating."
CL study group:
Others:
- Common Lisp Game of Life visualisation with LispWorks and CAPI
- Nyxt on GambiConf (The hacker's power-browser, written in Common Lisp)
- Far and Away the Simplest Tutorial on Macros
and of course, find 3h48+ of condensed Lisp content on my Udemy video course! (I'm still working on new content, as a student you get updates).
Aside screncasts, some podcasts:
- CORECURSIVE #076: LISP in Space, With Ron Garret β a podcast, trascribed with pictures
- The Array Cast - Andrew Sengul, creator of the April language tells us about the advantages of combining Lisp and APL
Other discussions
Community
- π Impressions of Common Lisp and the Lisp Community
- "So, thank you. I'm glad I found this group (and some other Lisp groups) on Reddit. You guys are a real blessing."
- CL Community(?) Norms
- In what domains is common lisp used in 2022?
- What are you working on?
- A brief interview with Common Lisp creator Dr. Scott Fahlman
Learning Lisp
- Advice on professional development in Common Lisp?
- Trying to get into Lisp, Feeling overwhelmed
- Looking for good common lisp projects on github to read?
- this GitHub advanced search searches for
defclass
in 4 users projects: Shinmera, 40ants, Ruricolist, edicl (maintained Edi Weitz libraries):
- this GitHub advanced search searches for
- Teach lisp to high schoolers?
- Why buy LispWorks?
- it's expensive (there's a free limited edition), but all this feedback is much interesting.
- Writing robust software in CL
- talking Erlang, Actors libraries.
- Moira - Monitor and restart background threads.
- What should a new programmer writing in Common Lisp know about security?
Common Lisp VS ...
- what were the lessons you learned from programming in Lisp that carried over to other languages (more on the imperative side)?
- For serious/industrial/business use, in what ways do you think Common Lisp beat Clojure, and vice versa?
- Common Lisp VS Racket (openly biased over CL, with testimonies of lispers knowing both)
- HN comments (143): "I'm a heavy user of Common Lisp, and I only dabble in Racket from time to time. While Common Lisp is my tool of choice for a lot of reasons stated in the post, since the post largely skews favorably toward Common Lisp, I'll offer two things that Racket shines at."
- Why not: from Common Lisp to Julia
- HackerNews comments (200) (for this post and the post it responds to)
- The author of the first post said he was "migrating from Common Lisp to Julia as [his] primary programming language". He was starting "to grow increasingly frustrated with various aspects of the language" and the CL ecosystem. Many of his arguments were harsh towards CL and didn't mention existing better alternatives.
- Two months later, in another blog post, he admits the previous article "was unfair to both languages", and while "[he] is still mostly using Julia", "Common Lisp is still on the table for [him]".
- On LiberaChat, he says he's back from Julia to Common Lisp: "As of yesterday, I'm back to making libraries in CL. I cannot consider Julia as a serious language anymore, for it has deceived me after 7 years of research."
- Welcome back and thanks to him for the past and future CL libraries.
- Is Lisp too malleable to use for serious development?
- Why Lisp? (version franΓ§aise). "We will show how our own LispE interpreter was implemented."
Thanks everyone, happy lisping and see you around!
09 Jan 2023 6:54pm GMT
08 Jan 2023
Planet Lisp
Nicolas Martyanoff: Improving Git diffs for Lisp
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.
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.
The first step is to identify the language using a pattern applied to the filename. Edit your Git attribute file at $HOME/.gitattributes
and add entries for both Emacs Lisp and Common Lisp:
*.lisp diff=common-lisp
*.el diff=elisp
Then edit your Git configuration file at $HOME/.gitconfig
and configure the path of the Git attribute file:
[core]
attributesfile = ~/.gitattributes
Finally, set the regular expression used to match a top-level function name:
[diff "common-lisp"]
xfuncname="^\\((def\\S+\\s+\\S+)"
[diff "elisp"]
xfuncname="^\\((((def\\S+)|use-package)\\s+\\S+)"
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.
Of course you can modifiy these expressions to identify more complex top-level forms. For example, for Emacs Lisp, I also want to identify use-package
expressions.
You can see the result in all tools displaying Git diffs, for example in Magit with Common Lisp code:
Or for my Emacs configuration file:
Hunk titles, highlighted in blue, now contain the type and name of the top-level construction the changes are associated with.
A simple change, but one which really helps reading diffs.
08 Jan 2023 6:00pm GMT