<?xml version="1.0"?>
<rss version="2.0">
<channel>
	<title>Planet Lisp</title>
	<link>http://planet.lisp.org/</link>
	<description>Planet Lisp</description>
	<language>en</language>


<item>
	<title>ECL News: ECL 26.5.5 release</title>
	<guid isPermaLink="true">https://common-lisp.net/project/ecl/posts/ECL-2655-release.html</guid>
	<link>https://common-lisp.net/project/ecl/posts/ECL-2655-release.html</link>
	
	<description>&lt;p&gt;We are announcing a bugfix ECL release that addresses a few issues that has
slipped through testing of the recent one.&lt;/p&gt;

&lt;p&gt;Addressed issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;bugfix: MAKE-PACKAGE destructively modified defining form's cons cells of
  the package local nicknames, breaking package literals in bytecmp (#839)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;bugfix: the first environment is now always page-aligned by using the
  same allocation mechanism as all subsequent envs (#828)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;bugfix: allow loading concatenated fasc files (#842)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;bugfix: defclass does not redefine existing classes at compile time with
  forward-referenced classes in the bytecodes compiler (#843)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This release is available for download in a form of a source code archive (we do
not ship prebuilt binaries):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://common-lisp.net/project/ecl/static/files/release/ecl-26.5.5.tgz"&gt;ECL 26.5.5 tarball archive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://common-lisp.net/project/ecl/static/files/manual/ecl-26.5.5/"&gt;The ECL Manual&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy Hacking,&lt;br /&gt;
The ECL Developers&lt;/p&gt;</description>
	
	<pubDate>Tue, 05 May 2026 12:00:00 GMT</pubDate>
</item>

<item>
	<title>G&#225;bor Melis: DRef Leaves Home</title>
	<guid isPermaLink="true">http://quotenil.com/dref-leaves-home.html</guid>
	<link>http://quotenil.com/dref-leaves-home.html</link>
	
	<description>&lt;p&gt;Version 0.5 of &lt;a href="https://github.com/melisgl/dref" title="DRef"&gt;DRef&lt;/a&gt;, the definition reifier, is now available. 
It has moved to its own repository, completing its separation from
&lt;a href="https://github.com/melisgl/mgl-pax" title="PAX"&gt;PAX&lt;/a&gt;, where it was originally developed.&lt;/p&gt;

&lt;p&gt;&lt;img alt="" src="http://quotenil.com/blog-files/dref-logo.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;This was a long time coming. Twelve years ago today, PAX was born.
From the start, PAX used the concept of locatives to refer to
definitions without first-class objects. For example, to generate
documentation for the &lt;code&gt;*MY-VAR*&lt;/code&gt; variable, one could use the
&lt;code&gt;VARIABLE&lt;/code&gt; locative as in &lt;code&gt;(*MY-VAR* VARIABLE)&lt;/code&gt;. PAX needed to be able
to tell whether such a definition exists, as well as access its
docstring and source location.&lt;/p&gt;
Over time, this mechanism evolved into a portable, extensible
introspection library independent of PAX. I began
separating the two projects two years ago and
named the new library, though they continued to share a repository.
I have now removed the remaining dependencies so that DRef can live
on its own.</description>
	
	<pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate>
</item>

<item>
	<title>Joe Marshall: Echoes of the Lisp Listener</title>
	<guid isPermaLink="true">http://funcall.blogspot.com/2026/05/echoes-of-lisp-listener.html</guid>
	<link>http://funcall.blogspot.com/2026/05/echoes-of-lisp-listener.html</link>
	
	<description>&lt;p&gt;The Lisp Machine Listener had an electric close parenthesis.  When
  the user typed a close parenthesis, and this was the close
  parenthesis that finished the complete form at top level, the form
  would be sent to the REPL right away with no need to press enter.
  Here's how to get this behavior with SLY:&lt;/p&gt;
&lt;pre&gt;(defun my-sly-mrepl-electric-close-paren ()
  "Insert ')' and auto-send ONLY if we are closing a top-level Lisp form."
  (interactive)
  (let ((state (syntax-ppss)))
    (insert ")")
    ;; Safety checks:
    ;; 1. We were at depth 1 (so we are now at depth 0)
    ;; 2. We aren't in a string or comment
    ;; 3. The input actually starts with a paren (it's a form, not a sentence)
    (when (and (= (car state) 1)
               (not (nth 3 state))
               (not (nth 4 state))
               (string-match-p "^\\s-*(" 
                               (buffer-substring-no-properties (sly-mrepl--mark) (point))))
      (sly-mrepl-return))))&lt;/pre&gt;
&lt;p&gt;Another cool hack is to get the REPL to do double duty as a command
  line to the LLM chatbot.  When you type RET in the REPL, it will
  check if the input is a complete lisp form.  If so, it will send the
  form to the REPL as normal.  If not, it will send the input to the
  chatbot. Here's how to do this:&lt;/p&gt;
&lt;pre&gt;(defun my-sly-mrepl-electric-return ()
  "Send to Lisp if it's a form/symbol, or wrap in (chat ...) if it's a sentence."
  (interactive)
  (let* ((beg (marker-position (sly-mrepl--mark)))
         (end (point-max))
         (input (buffer-substring-no-properties beg end))
         (trimmed (string-trim input)))
    (cond
     ;; If it's empty, just do a normal return
     ((string-blank-p trimmed)
      (sly-mrepl-return))
     
     ;; If it starts with a paren, quote, or hash, it's definitely a Lisp form
     ((string-match-p "^\\s-*[(#'\"]" trimmed)
      (sly-mrepl-return))
     
     ;; If it's a single word (no spaces), treat it as a symbol/form (e.g., *package*)
     ((not (string-match-p "\\s-" trimmed))
      (sly-mrepl-return))
     
     ;; Otherwise, it's a sentence. Wrap it and fire.
     (t
      (delete-region beg end)
      (insert (format "(chat %S)" trimmed))
      (sly-mrepl-return)))))&lt;/pre&gt;

&lt;p&gt;Install as follows:&lt;/p&gt;
&lt;pre&gt;;; Apply to SLY MREPL with a safety check for the mode map
(with-eval-after-load 'sly-mrepl
  (define-key sly-mrepl-mode-map (kbd "RET") 'my-sly-mrepl-electric-return)
  (define-key sly-mrepl-mode-map (kbd ")") 'my-sly-mrepl-electric-close-paren))&lt;/pre&gt;</description>
	
	<pubDate>Fri, 01 May 2026 17:29:00 GMT</pubDate>
</item>

<item>
	<title>Tim Bradshaw: Making CLOS slot access less slow</title>
	<guid isPermaLink="true">https://www.tfeb.org/fragments/2026/05/01/making-clos-slot-access-less-slow/?utm_source=lisp&amp;utm_medium=RSS</guid>
	<link>https://www.tfeb.org/fragments/2026/05/01/making-clos-slot-access-less-slow/?utm_source=lisp&amp;utm_medium=RSS</link>
	
	<description>&lt;p&gt;Access to slots in CLOS instances is often very slow. It&amp;rsquo;s probably not possible for it ever to be really fast, but the AMOP MOP does provide a way of making it, at least, less slow.&lt;/p&gt;
&lt;!-- more--&gt;

&lt;h2 id="how-slow-is-it"&gt;How slow is it?&lt;/h2&gt;

&lt;p&gt;Here are some benchmarks for accessing fields in objects of various kinds, using SBCL. All of these tests do something equivalent to&lt;/p&gt;

&lt;pre class="brush: lisp"&gt;&lt;code&gt;(defclass a ()
  ((i :initform 0 :type fixnum)))

(defclass a/no-fixnum ()
  ((i :initform 0)))

(defmethod svn ((a a) n)
  (declare (type fixnum n)
           (optimize speed (safety 0)))
  (dotimes (i n)
    (incf (the fixnum (slot-value a 'i)))))

(defmethod svn ((a a/no-fixnum) n)
  (declare (type fixnum n)
           (optimize speed (safety 0)))
  (dotimes (i n)
    (incf (the fixnum (slot-value a 'i)))))&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;They then call &lt;code&gt;svn&lt;/code&gt; (or equivalent) with a large value of \(n\), do that a number of times \(m\) and then divide by \(2 \times n \times m\) to get an average time per access (&lt;code&gt;incf&lt;/code&gt; accesses the slot twice).&lt;/p&gt;

&lt;p&gt;For SBCL 2.6.3.178-a190d9710 on ARM64 Apple M1, seconds per access:&lt;/p&gt;

&lt;ul&gt;
 &lt;li&gt;raw fixnum increment \(1.58\times 10^{-10}\), ratio \(1.0\);&lt;/li&gt;
 &lt;li&gt;slot access with &lt;code&gt;slot-value&lt;/code&gt; (slot type &lt;code&gt;fixnum&lt;/code&gt;) \(1.20\times 10^{-8}\), ratio \(76\);&lt;/li&gt;
 &lt;li&gt;slot access with &lt;code&gt;slot-value&lt;/code&gt; (no slot type) \(1.22\times 10^{-8}\), ratio \(77\);&lt;/li&gt;
 &lt;li&gt;slot access with &lt;code&gt;slot-value&lt;/code&gt; (single &lt;code&gt;slot-value-using-class&lt;/code&gt; method) \(1.69\times 10^{-8}\), ratio \(107\);&lt;/li&gt;
 &lt;li&gt;slot access using &lt;code&gt;standard-instance-access&lt;/code&gt; \(1.00\times 10^{-9}\), ratio \(6.4\);&lt;/li&gt;
 &lt;li&gt;slot access, struct (slot type &lt;code&gt;fixnum&lt;/code&gt;) \(1.57\times 10^{-10}\), ratio \(1.0\);&lt;/li&gt;
 &lt;li&gt;slot access, struct (no type) \(1.58\times 10^{-10}\), ratio \(1.0\);&lt;/li&gt;
 &lt;li&gt;slot access, cons (&lt;code&gt;car&lt;/code&gt;) \(1.59\times 10^{-10}\), ratio \(1.0\).&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;These numbers vary slightly, but this gives a good picture of what is going on. In particular you can see that &lt;code&gt;slot-value&lt;/code&gt; within a method specialised on the class is more than 70 times slower than access for a structure slot, but if you can use &lt;code&gt;standard-instance-access&lt;/code&gt; it is only about 6 times slower: &lt;code&gt;standard-instance-access&lt;/code&gt; speeds things up by a factor of about 10, which changes CLOS slot access performance from laughably slow to merely pretty slow.&lt;/p&gt;

&lt;h2 id="a-macro"&gt;A macro&lt;/h2&gt;

&lt;p&gt;I&amp;rsquo;ve written a macro, called &lt;code&gt;with-sia-slots&lt;/code&gt; which is like &lt;code&gt;with-slots&lt;/code&gt; but uses &lt;code&gt;standard-instance-access&lt;/code&gt;. It therefore has all the constraints imposed by that, but it is significantly faster than &lt;code&gt;with-slots&lt;/code&gt; or &lt;code&gt;slot-value&lt;/code&gt;. It has some overhead, as it has to dynamically compute the slot locations: this is better done outside any inner loop. This means that, for instance, you probably want to write code that looks like&lt;/p&gt;

&lt;pre class="brush: lisp"&gt;&lt;code&gt;(with-sia-slots (x) o
  (dotimes (i many)
    (setf x (... x ...))))&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will mean you only pay the overhead once.&lt;/p&gt;

&lt;p&gt;The above tests don&amp;rsquo;t use &lt;code&gt;with-sia-slots&lt;/code&gt;, as I wrote them partly to see if something like this was worth writing. However on a current (at the time of writing) SBCL &lt;code&gt;with-sia-slots&lt;/code&gt; is asymptotically about 10 times faster than &lt;code&gt;with-slots&lt;/code&gt; as demonstrated by these tests.&lt;/p&gt;

&lt;p&gt;Up to package names it should be portable to any CL with an AMOP-compatible MOP. It can be found in my implementation-specific hacks, linked from &lt;a href="https://tfeb.org/fragments/documentation/" title="Documentation"&gt;here&lt;/a&gt;.&lt;/p&gt;</description>
	
	<pubDate>Fri, 01 May 2026 15:43:22 GMT</pubDate>
</item>

<item>
	<title>Yukari Hafner: On Lisp, LLMs, and Community</title>
	<guid isPermaLink="true">https://reader.tymoon.eu/article/444</guid>
	<link>https://reader.tymoon.eu/article/444</link>
	
	<description>&lt;article&gt;&lt;figure&gt;&lt;a href="https://studio.tymoon.eu/api/studio/file?id=3892" target="_blank"&gt;&lt;img alt="https://studio.tymoon.eu/api/studio/file?id=3892" src="https://studio.tymoon.eu/api/studio/file?id=3892" style="display: block; margin: auto;" /&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;In 2015 in London I attended my first European Lisp Symposium. I was 21 at the time, and while this wasn't my first time abroad on my own, it was still a pretty stressful affair. I remember it still pretty clearly to that day: meeting Robert Strandh, Zach Beane, Didier Verna, Daniel Kochma&amp;#324;ski and many other people I'd previously admired from afar through many discussions on IRC. It was an important event for me, and was the first time I'd felt like I was in a group of people I could talk with about my interests and ambitions.&lt;/p&gt;&lt;p&gt;Last year in 2025 I was the local chair for ELS in ZÃ¼rich. It was a stressful time and I don't remember much of it other than how the stage looked, the food, and me rushing all over to get supplies and take care of other emergencies. I barely talked to anyone because I was either rushing about, stressed, or too tired.&lt;/p&gt;&lt;p&gt;In that time, my life has changed significantly. Over the years I took on more and more organisational roles for ELS itself: remaking the website, handling the transition to a hybrid online conference, handling the live streaming on-site, and last year being local chair.&lt;/p&gt;&lt;p&gt;But for other parts of the broader Lisp community I gradually changed in the opposite direction: I stopped religiously reading the #lisp/#commonlisp IRC channels. I left the Lisp Discord. I stopped replying on and ultimately altogether reading the /r/lisp subreddit. I stopped blogging about what was going on both in other places and with my own projects.&lt;/p&gt;&lt;p&gt;All of these changes happened over time as I found myself with less tolerance for things that annoyed me and wasted my time and energy. The endless debates about why there wasn't a new standard, the constant humm-hawwing about what &amp;quot;&amp;quot;&amp;quot;the community&amp;quot;&amp;quot;&amp;quot; should do, why Lisp wasn't more widely used if it's so great, someone starting yet another project that was already done instead of contributing to an existing implementation, and so on and so forth.&lt;/p&gt;&lt;p&gt;And then I found myself thinking today: &amp;quot;gee, I'm not very excited to go to ELS'26, huh? Whatever happened?&amp;quot; I've already booked my flight and hotel, and I'll be there anyway, partly because I have to for organisational reasons. But now that I'm thinking about how I feel, I can't say for certain if I will be back next year, too. Both for financial and emotional reasons.&lt;/p&gt;&lt;p&gt;In recent years I've found myself more and more disconnected with male-dominated spaces in general. I don't feel at home in them. I'm already not a very social person and struggle with any kind of gathering that has more than 6 people, but a lot more so still if it's mostly men. Not necessarily because I feel like I'm in any kind of danger, but simply because I don't feel like I belong. And... you know, that's sad. Obviously me leaving won't make the situation better for the other women that &lt;em&gt;do&lt;/em&gt; attend, but that's the dilemma with all of these situations: unless the organisation creates intentional pressure to correct the situation, it will inevitably only reinforce itself.&lt;sup class="footnote-reference"&gt;&lt;a href="https://blog.tymoon.eu/api/reader/atom?tag=common%20lisp#1"&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;And then there's what I can, in the nicest way, only describe as &amp;quot;The LLM Situation,&amp;quot; though I will be increasingly un-nice going forward. As of early this year SBCL has happily accepted patches that are authored by or with the use of LLMs, and the maintainers have rebuffed complaints about this practise. The mailing list has also gotten its fair share of useless blather by apologists and pointless drivel dreamed up by LLMs that only wastes everyone's time, to the point where I had to just stop reading it altogether. A few maintainers of other significant projects seem to also have embraced the capitalist wasteland mass exploitation machine that disguises itself as &amp;quot;technology.&amp;quot;&lt;/p&gt;&lt;p&gt;On the other side of things as the lead developer of Shirakumo I've decided to put out a &lt;a class="external-link" href="https://shirakumo.org/policy#llm"&gt;blanket ban&lt;/a&gt; on all of this garbage. I do not care if LLMs work at all, or if they will ever work, or whatever. The usability of LLMs is completely irrelevant. By using them you are happily handing over the single last remaining shred of your human spirit to the capitalists to help them burn everyone else and the world with it to the ground.&lt;/p&gt;&lt;p&gt;I think back to the impromptu &amp;quot;LLM roundtable&amp;quot; discussion that took place at the end of ELS last year, along with the usual apologist bullshit that was spread in the ELS Signal group at the time, and some of the lightning talks that were shown. And as I think about this, I am filled with trepidation about the coming conference.&lt;/p&gt;&lt;p&gt;Obviously I have no idea what it will be like yet, and I have no idea what the programme will be, nor what people will be there, or what the general vibe is going to be. But nevertheless, I really hope I won't have to &amp;quot;crash out&amp;quot; as the kids say. I already lost my mind last year, seemingly being the only one that wanted to hold a firm stance against this wave of shit at the time.&lt;/p&gt;&lt;p&gt;So what does this all mean going forward? Well, for just now, nothing. I'll continue to be in the places I have dug out myself: &lt;a class="external-link" href="https://mastodon.shinmera.com"&gt;mastodon&lt;/a&gt;, the shirakumo lichat/libera channel, &lt;a class="external-link" href="https://patreon.shinmera.com"&gt;my patreon&lt;/a&gt;, and other small, purpose-driven communities. But it's very possible I'll be leaving ELS behind me permanently after this year, cutting off even the last part of the community that used to be most of my world.&lt;/p&gt;&lt;p&gt;Regardless, I will still be working on my Lisp projects. If nothing else, one of the nice things about the looming tower of software I've built over all these years is that I am in control of the vast majority of it, and replacing any particular part I didn't write should it get enshittified is not that big of an endeavour.&lt;/p&gt;&lt;p&gt;Make no mistake though: I will continue to be increasingly outspoken and annoying about political matters that I consider important and relevant, and this will also be visible in the software I write, be that in licensing, ecosystem integration, or documentation.&lt;/p&gt;&lt;p&gt;I hope that more people will speak out publicly about their stance. It's important to show what you stand for, even if you're just a small part. What is considered &amp;quot;normal&amp;quot; and acceptable is only ever a matter of what people get to see, regardless of how prevalent that stance is among the population. Currently people are getting to see a lot of folks proudly and loudly making trash and littering it all over the place. This normalisation is dangerous, because it makes the average joes think it's OK for them to do it too, or even that they should be doing it.&lt;/p&gt;&lt;p&gt;Just the same way as any other social movement, you &amp;#129781; play a role in it, and your voice matters. Whether you use your voice for the betterment of humans, for the betterment of the ghouls feeding off of us, or silently let the ghouls feed off of us.&lt;/p&gt;&lt;p&gt;See you at ELS'26 in KrakÃ³w!&lt;/p&gt;&lt;footer class="footnotes"&gt;&lt;hr style="clear: both;" /&gt;&lt;ol&gt;&lt;li id="1" value="1"&gt;A very dramatic but clear demonstration of this principle is found in the &lt;a class="external-link" href="https://en.wiktionary.org/wiki/Nazi_bar"&gt;&amp;quot;Nazi Bar&amp;quot; anecdote&lt;/a&gt;. People that don't feel comfortable will just leave, even when there is no explicit and obvious push for them to do so.&lt;/li&gt;&lt;/ol&gt;&lt;/footer&gt;&lt;/article&gt;</description>
	
	<pubDate>Wed, 29 Apr 2026 17:12:03 GMT</pubDate>
</item>

<item>
	<title>Tim Bradshaw: Structures of arrays</title>
	<guid isPermaLink="true">https://www.tfeb.org/fragments/2026/04/16/structures-of-arrays/?utm_source=lisp&amp;utm_medium=RSS</guid>
	<link>https://www.tfeb.org/fragments/2026/04/16/structures-of-arrays/?utm_source=lisp&amp;utm_medium=RSS</link>
	
	<description>&lt;p&gt;Or, second system.&lt;/p&gt;
&lt;!-- more--&gt;

&lt;p&gt;A while ago, I decided that I&amp;rsquo;d like to test my intuition that Lisp (specifically implementations of Common Lisp) was not, in fact, bad at floating-point code and that the ease of designing languages in Lisp could make traditional Fortran-style array-bashing numerical code pretty pleasant to write.&lt;/p&gt;

&lt;p&gt;I used an intentionally naÃ¯ve numerical solution to a gravitating many-body system as a benchmark, so I could easily compare Lisp &amp;amp; C versions. The brief result is that the Lisp code is a little slower than C, but not much: Lisp is not, in fact, slow. Who knew?&lt;/p&gt;

&lt;p&gt;The point here though, is that I wanted to dress up the array-bashing code so it looked a lot more structured. To do this I wrote a macro which hid what was in fact an array of (for instance) double floats behind a bunch of syntax which made it look like an array of structures. That macro took a couple of hours.&lt;/p&gt;

&lt;p&gt;This was fine and pretty simple, but it only dealt with a single type for each conceptual array of objects, there was no inheritance and it was restricted in various other ways. In particular it really was syntactic sugar on a vector: there was no distinct implementational type at all. So I thought well, I could make it more general and nicer.&lt;/p&gt;

&lt;p&gt;Big mistake.&lt;/p&gt;

&lt;h2 id="the-second-system"&gt;The second system&lt;/h2&gt;

&lt;p&gt;Here is an example of what I wanted to be able to do (this is in fact the current syntax):&lt;/p&gt;

&lt;pre class="brush: lisp"&gt;&lt;code&gt;(define-soa-class example ()
  ((x :array t :type double-float)
   (y :array t :type double-float)
   (p :array t :type double-float :group pq)
   (q :array t :type double-float :group pq)
   (r :array t :type fixnum)
   (s)))&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This defines a class, instances of which have five array slots and one scalar slot. Of the array slots:&lt;/p&gt;

&lt;ul&gt;
 &lt;li&gt;&lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; share an array and will be neighbouring elements;&lt;/li&gt;
 &lt;li&gt;&lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; share a different array, because the group option says they must not share with &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;;&lt;/li&gt;
 &lt;li&gt;&lt;code&gt;r&lt;/code&gt; will be in its own array, unless the upgraded element type of &lt;code&gt;fixnum&lt;/code&gt; is the same as that of &lt;code&gt;double-float&lt;/code&gt;;&lt;/li&gt;
 &lt;li&gt;&lt;code&gt;s&lt;/code&gt; is just a slot.&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;The implementation will tell you this:&lt;/p&gt;

&lt;pre class="brush: lisp"&gt;&lt;code&gt;&amp;gt; (describe (make-instance 'example :dimensions '(2 2)))
#&amp;lt;example 8010059EEB&amp;gt; is an example
[...]
dimensions      (2 2)
total-size      4
rank            2
tick            1
its class example has a valid layout
it has 3 arrays:
 index 0, element type double-float, 2 slots
 index 1, element type (signed-byte 64), 1 slot
 index 2, element type double-float, 2 slots
it has 5 array slots:
 name x, index 0 offset 0
 name y, index 0 offset 1
 name r, index 1 offset 0
 name p, index 2 offset 0
 name q, index 2 offset 1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is already too complicated: the ability to control sharing via groups is almost certainly never going to be useful: it&amp;rsquo;s only even there because I thought of it quite early on and never removed it.&lt;/p&gt;

&lt;p&gt;The class definition macro then needs to arrange life so that enough information is available so that a macro can be written which turns indexed slot access into indexed array access of the underlying arrays which are secretly stored in instances, inserting declarations to make this as fast as possible: anything slower than explicit array access is not acceptable. This might (and does) look like this, for example:&lt;/p&gt;

&lt;pre class="brush: lisp"&gt;&lt;code&gt;(with-array-slots (x y) (thing example)
  (for* ((i ...) (j ...))
    (setf (x i j) (- (y i j) (y j i)))))&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see from this, the resulting objects should be allowed to have rank other than 1. Inheritance should also work, including for array slots. Redefinition should be supported and obsolete macro expansions and instances at least detected.&lt;/p&gt;

&lt;p&gt;In other words there are &lt;em&gt;exactly two things&lt;/em&gt; I should have aimed at achieving: the ability to define fields of various types and have them grouped into (generally fewer) underlying arrays, and an implementational type to hold these things. Everything else was just unnecessary baggage which made the implementation much more complicated than it needed to be.&lt;/p&gt;

&lt;p&gt;I had not finished making mistakes. The system needs to store some metadata about how slots map onto the underlying arrays, element types and so on, so the macro can use this to compile efficient code. There are two obvious ways to do this: use the property list of the class name, or subclass &lt;code&gt;standard-class&lt;/code&gt; and store the metadata in the class. The first approach is simple, portable, has clear semantics, but it&amp;rsquo;s &amp;lsquo;hacky&amp;rsquo;; the second is more complicated, not portable, has unclear semantics&lt;sup&gt;&lt;a href="https://www.tfeb.org/fragments/feeds/lisp.rss.xml#2026-04-16-structures-of-arrays-footnote-1-definition" name="2026-04-16-structures-of-arrays-footnote-1-return"&gt;1&lt;/a&gt;&lt;/sup&gt;, but it&amp;rsquo;s The Right Thing&lt;sup&gt;&lt;a href="https://www.tfeb.org/fragments/feeds/lisp.rss.xml#2026-04-16-structures-of-arrays-footnote-2-definition" name="2026-04-16-structures-of-arrays-footnote-2-return"&gt;2&lt;/a&gt;&lt;/sup&gt;. Another wrong decision I made without even trying.&lt;/p&gt;

&lt;p&gt;The only thing that saved me was that the nature of software is that you can only make a finite number of bad decisions in a finite time.&lt;/p&gt;

&lt;h2 id="more-bad-decisions"&gt;More bad decisions&lt;/h2&gt;

&lt;p&gt;I was not done. Early on, I thought that, well, I could make this whole thing be a shim around &lt;code&gt;defstruct&lt;/code&gt;: single inheritance was more than enough, and obviously I could store metadata on the property list of the type name as described above. And there&amp;rsquo;s no nausea with multiple accessors or any of that nonsense.&lt;/p&gt;

&lt;p&gt;But, somehow, I found writing a thing which would process the &lt;code&gt;(structure-name ...)&lt;/code&gt; case of &lt;code&gt;defstruct&lt;/code&gt; too painful, so I decided to go for the shim-around-&lt;code&gt;defclass&lt;/code&gt; version instead. I even have a partly-complete version of the &lt;code&gt;defstruct&lt;/code&gt;y code which I abandoned. Another mistake.&lt;/p&gt;

&lt;p&gt;I also decided that The Right Thing was to have the system support objects of rank 0. That constrains the underlying array representation (it needs to use rank \(n+1\) arrays for an object of rank \(n\)) in a way which I thought for a long time might limit performance.&lt;/p&gt;

&lt;h2 id="things-i-already-knew"&gt;Things I already knew&lt;/h2&gt;

&lt;p&gt;At any point during the implementation of this I could have told you that it was too general and the implementation was going to be too complicated for no real gain. I don&amp;rsquo;t know why I made so many bad choices.&lt;/p&gt;

&lt;p&gt;The whole process took weeks and I nearly just gave up several times.&lt;/p&gt;

&lt;h2 id="the-light-at-the-end-of-the-tunnel"&gt;The light at the end of the tunnel&lt;/h2&gt;

&lt;p&gt;Or: all-up testing.&lt;/p&gt;

&lt;p&gt;Eventually, I had a thing I thought might work. The macro syntax was a bit ugly (that macro still exists, with a different name) but it seemed to work. But since the whole purpose of the thing was performance, that needed to be checked. I wasn&amp;rsquo;t optimistic.&lt;/p&gt;

&lt;p&gt;What I did was to write a version of my naÃ¯ve gravitational many-body system using the new code, based closely on the previous one. The function that updates the state of the particles looks like this:&lt;/p&gt;

&lt;pre class="brush: lisp"&gt;&lt;code&gt;(defun/quickly step-pvs (source destination from below dt G &amp;amp;aux
                                (n (particle-vector-length source)))
  ;; Step a source particle vector into a destination one.
  ;;
  ;; Operation count:
  ;;  3
  ;;  + (below - from) * (n - 1) * (3 + 8 + 9)
  ;;  + (below - from) * (12 + 6)
  ;;  = (below - from) * (20 * (n - 1) + 18) + 3
  (declare (type particle-vector source destination)
           (type vector-index from)
           (type vector-dimension below)
           (type fpv dt G)
           (type vector-dimension n))
  (when (eq source destination)
    (error "botch"))
  (let*/fpv ((Gdt (* G dt))
             (Gdt^2/2 (/ (* Gdt dt) (fpv 2.0))))
    (binding-array-slots (((source particle-vector :check nil :rank 1 :suffix _s)
                           m x y z vx vy vz)
                          ((destination particle-vector :check nil :rank 1 :suffix _d)
                           m x y z vx vy vz))
      (for ((i1 (in-naturals :initially from :bound below :fixnum t)))
        (let/fpv ((ax/G zero.fpv)
                  (ay/G zero.fpv)
                  (az/G zero.fpv)
                  (x1 (x_s i1))
                  (y1 (y_s i1))
                  (z1 (z_s i1))
                  (vx1 (vx_s i1))
                  (vy1 (vy_s i1))
                  (vz1 (vz_s i1)))
          (for ((i2 (in-naturals n t)))
            (when (= i1 i2) (next))
            (let/fpv ((m2 (m_s i2))
                      (x2 (x_s i2))
                      (y2 (y_s i2))
                      (z2 (z_s i2)))
              (let/fpv ((rx (- x2 x1))
                        (ry (- y2 y1))
                        (rz (- z2 z1)))
                (let/fpv ((r^3 (let* ((r^2 (+ (* rx rx) (* ry ry) (* rz rz)))
                                      (r (sqrt r^2)))
                                 (declare (type nonnegative-fpv r^2 r))
                                 (* r r r))))
                  (incf ax/G (/ (* rx m2) r^3))
                  (incf ay/G (/ (* ry m2) r^3))
                  (incf az/G (/ (* rz m2) r^3))))))
          (setf (x_d i1) (+ x1 (* vx1 dt) (* ax/G Gdt^2/2))
                (y_d i1) (+ y1 (* vy1 dt) (* ay/G Gdt^2/2))
                (z_d i1) (+ z1 (* vz1 dt) (* az/G Gdt^2/2)))
          (setf (vx_d i1) (+ vx1 (* ax/G Gdt))
                (vy_d i1) (+ vy1 (* ay/G Gdt))
                (vz_d i1) (+ vz1 (* az/G Gdt)))))))
  destination)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And it not only worked, the performance was very close to the previous version, straight out of the gate. The syntax is not as nice as that of the initial, quick-and-dirty version, but it is much more general, so I think that&amp;rsquo;s worth it on the whole.&lt;/p&gt;

&lt;p&gt;There have been problems since then: in particular the dependency on when classes get defined. It will never be as portable as I&amp;rsquo;d like because of the unnecessary MOP dependencies&lt;sup&gt;&lt;a href="https://www.tfeb.org/fragments/feeds/lisp.rss.xml#2026-04-16-structures-of-arrays-footnote-3-definition" name="2026-04-16-structures-of-arrays-footnote-3-return"&gt;3&lt;/a&gt;&lt;/sup&gt;, but it is usable and quick&lt;sup&gt;&lt;a href="https://www.tfeb.org/fragments/feeds/lisp.rss.xml#2026-04-16-structures-of-arrays-footnote-4-definition" name="2026-04-16-structures-of-arrays-footnote-4-return"&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Was it worth it? May be, but it should have been simpler.&lt;/p&gt;

&lt;hr /&gt;

&lt;div class="footnotes"&gt;
 &lt;ol&gt;
  &lt;li class="footnote-definition" id="2026-04-16-structures-of-arrays-footnote-1-definition"&gt;
   &lt;p&gt;When exactly do classes get defined? Right.&amp;nbsp;&lt;a href="https://www.tfeb.org/fragments/feeds/lisp.rss.xml#2026-04-16-structures-of-arrays-footnote-1-return"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li class="footnote-definition" id="2026-04-16-structures-of-arrays-footnote-2-definition"&gt;
   &lt;p&gt;Nothing that uses the AMOP MOP is ever The Right Thing, because the whole thing was designed by people who were extremely smart, but still not as smart as they needed to be and thought they were. It&amp;rsquo;s unclear if any MOP for CLOS can ever be satisfactory, in part because CLOS itself suffers from the same smart-but-not-smart-enough problem to a large extent not helped by bring dropped wholesale into CL at the last minute: by the time CL was standardised people had written large systems in it, but almost nobody had written anything significant using CLOS, let alone the AMOP MOP.&amp;nbsp;&lt;a href="https://www.tfeb.org/fragments/feeds/lisp.rss.xml#2026-04-16-structures-of-arrays-footnote-2-return"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li class="footnote-definition" id="2026-04-16-structures-of-arrays-footnote-3-definition"&gt;
   &lt;p&gt;A mistake I somehow managed to avoid was using the whole slot-definition mechanism the MOP wants you to use.&amp;nbsp;&lt;a href="https://www.tfeb.org/fragments/feeds/lisp.rss.xml#2026-04-16-structures-of-arrays-footnote-3-return"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li class="footnote-definition" id="2026-04-16-structures-of-arrays-footnote-4-definition"&gt;
   &lt;p&gt;I will make it available at some point.&amp;nbsp;&lt;a href="https://www.tfeb.org/fragments/feeds/lisp.rss.xml#2026-04-16-structures-of-arrays-footnote-4-return"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
	
	<pubDate>Thu, 16 Apr 2026 11:01:13 GMT</pubDate>
</item>

<item>
	<title>Robert Smith: Not all elementary functions can be expressed with exp-minus-log</title>
	<guid isPermaLink="true">http://www.stylewarning.com/posts/not-all-elementary/</guid>
	<link>http://www.stylewarning.com/posts/not-all-elementary/</link>
	
	<description>&lt;p&gt;&lt;em&gt;By Robert Smith&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://arxiv.org/html/2603.21852v2"&gt;&lt;em&gt;All Elementary Functions from a Single Operator&lt;/em&gt;&lt;/a&gt; is a paper by Andrzej Odrzywo&amp;#322;ek that has been making rounds on the internet lately, being called everything from a &amp;ldquo;breakthrough&amp;rdquo; to &amp;ldquo;groundbreaking&amp;rdquo;. Some are going as far as to suggest that the entire foundations of computer engineering and machine learning should be re-built as a result of this. The paper says that the function&lt;/p&gt;
&lt;p&gt;$$
E(x,y) := \exp x - \log y
$$&lt;/p&gt;
&lt;p&gt;together with variables and the constant $1$, which we will call &lt;em&gt;EML terms&lt;/em&gt;, are sufficient to express all elementary functions, and proceeds to give constructions for many constants and functions, from addition to $\pi$ to hyperbolic trigonometry.&lt;/p&gt;
&lt;p&gt;I think the result is neat and thought-provoking. Odrzywo&amp;#322;ek is explicit about his definition of &amp;ldquo;elementary function&amp;rdquo;. His Table 1 fixes &amp;ldquo;elementary&amp;rdquo; as 36 specific symbols, and under that definition his theorem is correct and clever, so long as we accept some of his modifications to the conventional $\log$ function and do arithmetic with infinities.&lt;/p&gt;
&lt;p&gt;My concern is that the word &amp;ldquo;elementary&amp;rdquo; in the title carries a much broader meaning in standard mathematical usage. Odrzywo&amp;#322;ek recognizes this, saying little more than &amp;ldquo;[t]hat generality is not needed here&amp;rdquo; and that his work takes &amp;ldquo;the ordinary scientific-calculator point of view&amp;rdquo;. He does not offer further commentary.&lt;/p&gt;
&lt;p&gt;What is this more general setting, and does his claim still hold? In modern pure mathematics, dating back to the 19th century, the definition of &amp;ldquo;elementary function&amp;rdquo; has been well established. We&amp;rsquo;ll get to a definition shortly, but to cut to the chase, the titular result does &lt;em&gt;not&lt;/em&gt; hold in this setting. As such, in layman&amp;rsquo;s terms, I do not consider the &amp;ldquo;Exp-Minus-Log&amp;rdquo; function to be the continuous analog of the Boolean NAND gate or the universal quantum CCNOT/CSWAP gates.&lt;/p&gt;
&lt;p&gt;The rough TL;DR is this: Elementary functions typically include &lt;em&gt;arbitrary&lt;/em&gt; polynomial root functions, and EML terms cannot express them. Below, I&amp;rsquo;ll give a relatively technical argument that EML terms are not sufficient to express what I consider standard elementary functions.&lt;/p&gt;
&lt;p&gt;To avoid any confusion, the purpose of this blog post is manifold:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To elucidate what many mathematicians consider to be an &amp;ldquo;elementary function&amp;rdquo;, which is the foundation for a variety of rich and interesting math (&lt;em&gt;especially&lt;/em&gt; if you like computer science).&lt;/li&gt;
&lt;li&gt;To prove a result about EML terms using topological Galois theory.&lt;/li&gt;
&lt;li&gt;To demonstrate how this result may be used to show an elementary function not expressible by EML terms.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This blog post is not a refutation of Odrzywo&amp;#322;ek&amp;rsquo;s work, though the title might be considered just as clickbait (and accurate) as his, depending on where you sit in the hall of mathematics and computation.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: I audited graduate-level mathematics courses almost 20 years ago, and I am not a professional mathematician. Please email me if my statements are clumsy or incorrect.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The 19th century is where all modern understanding of elementary functions was developed, Liouville being one of the big names with countless theorems of analysis and algebra named after him. One such result is about integration: do the outputs of integrals look the same as their inputs? Well, what does &amp;ldquo;input&amp;rdquo; and &amp;ldquo;look the same&amp;rdquo; mean? Liouville defined a class of functions called &lt;em&gt;elementary functions&lt;/em&gt;, and said that the integral of an elementary function will sometimes be elementary, and when it is, it will always resemble the input in a specific way, plus potential extra logarithmic factors.&lt;/p&gt;
&lt;p&gt;Since then, elementary functions have been defined by starting with rational functions and closing under arithmetic operations, composition, exponentiation, logarithms, and polynomial roots. While EML terms are quite expressive, they are unable to capture the &amp;ldquo;polynomial roots&amp;rdquo; in full generality. We will show this by using Khovanskii&amp;rsquo;s topological Galois theory: the monodromy group of a function built from rational functions by composition with $\exp$ and $\log$ is solvable. For anybody that has studied Galois theory in an algebra course, this will be familiar, as the destination here is effectively the same, but with more powerful intermediate tooling to wrangle exponentials and logarithms.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s be more precise by what we mean by an EML term and by a standard elementary function.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Definition (EML Term)&lt;/strong&gt;: An &lt;em&gt;EML term&lt;/em&gt; in the variables $x_1,\dots,x_n$ is any expression obtained recursively, starting from $\{1, x_1,\dots,x_n\}$, by the rule
$$
T,S \mapsto \exp T-\log S.
$$
Each such term, evaluated at a point where all the $\log$ arguments are nonzero, determines an analytic germ; we take $\mathcal T_n$ to be the class of germs representable this way, together with their maximal analytic continuations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Definition (Standard Elementary Function)&lt;/strong&gt;: The &lt;em&gt;standard elementary functions&lt;/em&gt; $\mathcal{E}_n$ are the smallest class of multivalued analytic functions on domains in $\mathbb{C}^n$ containing the rational functions and closed under&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;arithmetic operations and composition,&lt;/li&gt;
&lt;li&gt;exponentiation and logarithms,&lt;/li&gt;
&lt;li&gt;algebraic adjunctions: if $P(Y)\in K[Y]$ is a polynomial whose coefficients lie in a previously constructed class $K$, then any local branch of a solution of $P(Y)=0$ is admitted.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What we will show is that the class of elementary functions defined this way is strictly &lt;em&gt;larger&lt;/em&gt; than the class induced by EML terms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lemma&lt;/strong&gt;: Every EML term has solvable monodromy group. In particular, if $f\in\mathcal T_n$ is algebraic over $\mathbb C(x_1,\dots,x_n)$, then its monodromy group is a finite solvable group.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Proof&lt;/strong&gt;: We prove by induction on EML term construction. Constants and coordinate functions have trivial monodromy.&lt;/p&gt;
&lt;p&gt;For the inductive step, suppose $f = \exp A-\log B$ with $A,B\in\mathcal T_n$, and assume that $\mathrm{Mon}(A)$ and $\mathrm{Mon}(B)$ are solvable. We argue in three steps.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Step 1: $\mathrm{Mon}(\exp A)$ is solvable.&lt;/em&gt; The germs of $\exp A$ are images under $\exp$ of the germs of $A$, with germs of $A$ differing by $2\pi i\mathbb Z$ collapsing to the same value. So there is a surjection $\mathrm{Mon}(A)\twoheadrightarrow\mathrm{Mon}(\exp A)$, and a quotient of a solvable group is solvable.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Step 2: $\mathrm{Mon}(\log B)$ is solvable.&lt;/em&gt; At a generic point $p$, germs of $\log B$ are parameterized by pairs $(b,k)$ where $b$ is a germ of $B$ at $p$ and $k\in\mathbb Z$ selects the branch of $\log$. A loop $\gamma$ acts by
$$
(b,k)\mapsto\bigl(\rho_B(\gamma)(b), k+n(\gamma,b)\bigr),
$$
where $\rho_B(\gamma)$ is the monodromy action of $\gamma$ on germs of $B$, and $n(\gamma,b)\in\mathbb Z$ is the winding number around $0$ of the analytic continuation of $b$ along $\gamma$. The projection $\mathrm{Mon}(\log B)\to\mathrm{Mon}(B)$ onto the first component is a surjective homomorphism. Its kernel consists of the elements of $\mathrm{Mon}(\log B)$ induced by loops $\gamma$ with $\rho_B(\gamma)=\mathrm{id}$, which then act only by integer shifts on the $k$-coordinate. Let $S_B$ be the set of germs of $B$ at $p$. For each $b\in S_B$, such a loop determines an integer shift $n(\gamma,b)$, so the kernel embeds in the direct product $\mathbb Z^{S_B}$. In particular, the kernel is abelian. Hence $\mathrm{Mon}(\log B)$ is an extension of $\mathrm{Mon}(B)$ by an abelian group, and extensions of solvable groups by abelian groups are solvable.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Step 3: $\mathrm{Mon}(f)$ is solvable.&lt;/em&gt; At a generic point, a germ of $f=\exp A-\log B$ is obtained by subtraction from a pair (germ of $\exp A$, germ of $\log B$), and analytic continuation acts componentwise on such pairs. This gives a surjection of $\pi_1$ onto some subgroup
$$
H \le \mathrm{Mon}(\exp A)\times\mathrm{Mon}(\log B),
$$
and, since $f$ is obtained from the pair by subtraction, this descends to a surjection $H\twoheadrightarrow\mathrm{Mon}(f)$. So $\mathrm{Mon}(f)$ is a quotient of a subgroup of a direct product of solvable groups, hence solvable.&lt;/p&gt;
&lt;p&gt;The second statement of the lemma follows: an algebraic function has finitely many branches, so its monodromy group is finite; a solvable group that is finite is, well, finite and solvable. &amp;#8718;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Remark.&lt;/em&gt; This is the core of Khovanskii&amp;rsquo;s topological Galois theory; see &lt;a href="https://link.springer.com/book/10.1007/978-3-642-38871-2"&gt;&lt;em&gt;Topological Galois Theory: Solvability and Unsolvability of Equations in Finite Terms&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Theorem&lt;/strong&gt;: $\mathcal T_n \subsetneq \mathcal E_n$.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Proof&lt;/strong&gt;: $\mathcal E_n$ is closed under algebraic adjunction, so any local branch of an algebraic function is elementary. In particular, a branch of a root of the generic quintic
$$
f^5+a_1f^4+a_2f^3+a_3f^2+a_4f+a_5=0
$$
is elementary.&lt;/p&gt;
&lt;p&gt;Suppose for contradiction that at some point $p$ a germ of a branch of this root agrees with a germ of an EML term $T$. By uniqueness of analytic continuation, the Riemann surfaces obtained by maximally continuing these two germs coincide, so in particular their monodromy groups coincide. The monodromy group of the generic quintic is $S_5$, which is not solvable. But by the lemma, the monodromy group of any EML term is solvable. Contradiction.&lt;/p&gt;
&lt;p&gt;Hence $\mathcal T_n$ is a strict subset of $\mathcal E_n$. &amp;#8718;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Edit (15 April 2026): This article used to have an example proving that the real and complex absolute value cannot be expressed over their entire domain as EML terms under the conventional definition of $\log$. I wrote it to emphasize that Odrzywo&amp;#322;ek&amp;rsquo;s approach required mathematical &amp;ldquo;patching&amp;rdquo; in order to work as intended. However, it ended up more distracting than illuminating, and was tangential to the point about the definition of &amp;ldquo;elementary&amp;rdquo;, so it has been removed.&lt;/em&gt;&lt;/p&gt;</description>
	
	<pubDate>Tue, 14 Apr 2026 00:00:00 GMT</pubDate>
</item>

<item>
	<title>Scott L. Burson: FSet v2.4.2: CHAMP Bags, and v1.0 of my FSet book!</title>
	<guid isPermaLink="true">https://scottlburson2.blogspot.com/2026/04/fset-v242-champ-bags-and-v10-of-my-fset.html</guid>
	<link>https://scottlburson2.blogspot.com/2026/04/fset-v242-champ-bags-and-v10-of-my-fset.html</link>
	
	<description>&lt;p&gt;A couple of weeks ago I released &lt;a href="https://github.com/slburson/fset/releases/tag/v2.4.0"&gt;FSet 2.4.0&lt;/a&gt;, which brought a CHAMP implementation of bags, filling out the suite of CHAMP types.&amp;nbsp; &amp;#128640;&amp;nbsp; FSet users should have a look at the release page, as it also contained a number of bug fixes and minor changes.&lt;/p&gt;&lt;p&gt;I've since released v2.4.1 and v2.4.2, with some more bug fixes.&lt;/p&gt;&lt;p&gt;But the big news is the &lt;a href="https://fset.common-lisp.dev/Modern-CL/Top_html/index.html"&gt;book&lt;/a&gt;!&amp;nbsp; &amp;nbsp;It brings together all the introductory material I have written, plus a lot more, along with a complete API Reference chapter.&lt;/p&gt;&lt;p&gt;FSet is now in the state I decided last summer I wanted to get it into: faster, better tested and debugged, more feature-complete, and much better documented than it has ever been in its nearly two decades of existence.&amp;nbsp; I am, of course, very much hoping that these months of work have made the library more interesting and accessible to CL programmers who haven't tried it yet.&amp;nbsp; I am even hoping that its existence helps attract newcomers to the CL community.&amp;nbsp; Time will tell!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
	
	<pubDate>Mon, 13 Apr 2026 06:21:00 GMT</pubDate>
</item>

<item>
	<title>Wimpie Nortje: Dependency hell revisited, updating my Qlot workflow.</title>
	<guid isPermaLink="true">https://www.darkchestnut.com/2026/dependency-hell-revisited-updating-qlot-workflow/</guid>
	<link>https://www.darkchestnut.com/2026/dependency-hell-revisited-updating-qlot-workflow/</link>
	
	<description>&lt;p&gt;&lt;em&gt;I wrote on this &lt;a href="https://www.darkchestnut.com/2016/free-your-project-from-dependency-hell/" title="Older Qlot workflow."&gt;topic&lt;/a&gt; before but the landscape has changed
a lot since then.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Skip to the new Qlot &lt;a href="https://www.darkchestnut.com/feed.xml#workflow" title="The new Qlot workflow."&gt;workflow&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you work on projects that become even slightly complex it is a matter of
time until you run into problems where the specific version of a particular
library becomes important. This happens in most, if not all, programming
languages.&lt;/p&gt;

&lt;p&gt;In the Common Lisp environment Quicklisp has become the de facto standard for
loading libraries, including fetching and loading their dependencies. Quicklisp
distributes libraries in "distributions" which are point-in-time snapshots of
all the known and working libraries at the time of distribution creation.&lt;/p&gt;

&lt;p&gt;An advantage of this approach is that you are guaranteed that all libraries
available in the distribution can be loaded with any of the others. Some
disadvantages are that 1) if a library was included in an older distribution
but no longer loads cleanly, it gets removed from the distribution, and 2)
libraries are only added or updated when a new distribution is cut.&lt;/p&gt;

&lt;p&gt;Though libraries can be put in &lt;code class="language-plaintext highlighter-rouge"&gt;~/quicklisp/local-projects/&lt;/code&gt; in order to
&lt;a href="https://www.darkchestnut.com/2016/quicklisp-load-personal-projects-from-arbitrary-locations/" title="Systems don't need to be in a single place."&gt;supplement or override&lt;/a&gt; those in the distribution, Quicklisp
does not provide any mechanism for pinning the state of
&lt;code class="language-plaintext highlighter-rouge"&gt;~/quicklisp/local-projects/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some Quicklisp attributes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;All libraries in a distribution can be loaded with any of the others. Those
that no longer do are removed from the distribution.&lt;/li&gt;
  &lt;li&gt;Libraries are only added or updated at distribution cutting time.&lt;/li&gt;
  &lt;li&gt;You specify a single distribution version, not individual library
versions. The distribution is used globally, across all projects and
across all lisp implementations.&lt;/li&gt;
  &lt;li&gt;Libraries can be loaded from &lt;code class="language-plaintext highlighter-rouge"&gt;~/quicklisp/local-projects/&lt;/code&gt;. Anything
there will override the version in the distribution.&lt;/li&gt;
  &lt;li&gt;Nothing about the &lt;code class="language-plaintext highlighter-rouge"&gt;~/quicklisp/local-projects/&lt;/code&gt; content can be
specified using Quicklisp. It needs to be managed outside of Quicklisp.&lt;/li&gt;
  &lt;li&gt;Libraries are loaded from the current Quicklisp distribution. There is no
way to specify which distribution version a particular project must use.&lt;/li&gt;
  &lt;li&gt;Quicklisp can create a bundle of all the loaded systems that can be
committed with the project code and used from there rather than the
distribution, i.e. vendoring.&lt;/li&gt;
  &lt;li&gt;The source code for libraries included in a distribution are stored in a
dedicated location for Quicklisp, they are not read from the original source
repo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Depending on your situation these attributes may be positive, negative or
irrelevant.&lt;/p&gt;

&lt;p&gt;There are projects like Ultralisp that have different philosophies regarding
the distribution content but they still depend on Quicklisp for all other
aspects. Thus they share most of the above attributes.&lt;/p&gt;

&lt;p&gt;Since my &lt;a href="https://www.darkchestnut.com/2016/free-your-project-from-dependency-hell/" title="Older Qlot workflow."&gt;previous post&lt;/a&gt; on this topic much has changed in the
library version arena. There are now many projects that address different
aspects of the above list; the topic of vendoring has gained momentum; and Qlot
has changed a lot, to the extent that some code samples in older posts no
longer work.&lt;/p&gt;

&lt;p&gt;Vendoring is the idea that all the libraries your project depend on are
actually part of your project and as such should be committed as part of the
project code. Both Quicklisp and Qlot support this with &lt;code class="language-plaintext highlighter-rouge"&gt;QL:bundle&lt;/code&gt; and
&lt;code class="language-plaintext highlighter-rouge"&gt;QLOT:bundle&lt;/code&gt;, and the &lt;a href="https://github.com/fosskers/vend" title="Vend tool for vendoring dependencies."&gt;Vend&lt;/a&gt; tool is entirely focused on vendoring.&lt;/p&gt;

&lt;p&gt;The significant changes in Qlot broke my development workflow. Since I now had
to spend time fixing this it was a good opportunity to evaluate some of the
other library versioning tools. The issues that made me hesitant to adapt to
the new Qlot without considering other options are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Roswell: Qlot pushes hard for using Roswell. That ads Roswell as another
intermediary and dependency in an already complex process.&lt;/li&gt;
  &lt;li&gt;The Qlot documentation is heavily biased towards using it as an external tool
rather than through the REPL. Previously I used Qlot from the REPL and wanted
to continue doing so if possible.&lt;/li&gt;
  &lt;li&gt;Qlot is developed on SBCL which means that any issues in CCL take longer to
be discovered and fixed. As I mainly use CCL it is something to keep in mind.&lt;/li&gt;
  &lt;li&gt;The documentation proposes that lisp be started as a subprocess of Qlot,
e.g. &lt;code class="language-plaintext highlighter-rouge"&gt;$ qlot exec ros emacs&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;$ qlot exec ccl&lt;/code&gt;. This is to ensure that the
lisp is properly configured to use the project local Quicklisp. This is a
brittle solution because the standard lisp REPL is then no longer
sufficient. When you forget to start lisp this way Quicklisp will load the
wrong libraries without any indication, potentially causing subtle bugs which
are normally absent.&lt;/li&gt;
  &lt;li&gt;Using &lt;code class="language-plaintext highlighter-rouge"&gt;QLOT:quickload&lt;/code&gt; rather than &lt;code class="language-plaintext highlighter-rouge"&gt;QL:quickload&lt;/code&gt; always bugged me for
similar reasons. It is a non-standard way to do a standard thing. Forgetting
to do it is easy and then you have the wrong libraries loaded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I evaluated many of the other version management tools and came to the
conclusion that Qlot is the closest to what I wanted and I set off trying to
find a workflow that will adhere to my requirements listed here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Does not involve Roswell&lt;/li&gt;
  &lt;li&gt;Works with CCL&lt;/li&gt;
  &lt;li&gt;Works inside the REPL of a lisp loaded without any funny requirements&lt;/li&gt;
  &lt;li&gt;Gives me 100% certainty that the correct versions of all libraries are
loaded, without having to interrogate &lt;code class="language-plaintext highlighter-rouge"&gt;(asdf:system-relative-pathname)&lt;/code&gt; for
each.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After some fiddling with Qlot I learned that:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Qlot installs a complete and independent Quicklisp inside your project
directory. This Quicklisp has no dependence, relation or knowledge about the
global Quicklisp.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;$ qlot exec ccl&lt;/code&gt; mostly arranges things such that Quicklisp is loaded from
the project local installation. If you can arrange for that to happen
without using Qlot then you can use start your lisp normally.&lt;/li&gt;
  &lt;li&gt;When the project local Quicklisp is loaded it doesn't know about the global
or any other project local Quicklisps. This gives the 100% certainty of
where libraries were loaded from.&lt;/li&gt;
  &lt;li&gt;When the project local Quicklisp is loaded you use it exactly like you would
the global one. That is, &lt;code class="language-plaintext highlighter-rouge"&gt;QL:quickload&lt;/code&gt; and not &lt;code class="language-plaintext highlighter-rouge"&gt;QLOT:quickload&lt;/code&gt;, nor do you
use any other Qlot wrappers.&lt;/li&gt;
  &lt;li&gt;Qlot does not need to be present in your lisp image at all.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Loading Qlot inside you current REPL doesn't work well because:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;The Qlot REPL API is still in development.&lt;/li&gt;
      &lt;li&gt;Doing this loads Qlot from the global Quicklisp instead of the project
local one. Distribution version mismatches between the two Quicklisps
could trigger issues with Qlot itself. It also voids the certainty of
library location.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Having Qlot as a standalone executable outside of your lisp image puts it on
the same plane as other tools used for development such as make or git. It
then doesn't matter which implementation it prefers because it doesn't
affect your choices.&lt;/li&gt;
  &lt;li&gt;Qlot has gained the ability to bundle libraries in case you want to go the
vendoring route.&lt;/li&gt;
  &lt;li&gt;Roswell is not needed for any of the above.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id="workflow"&gt;New workflow&lt;/h2&gt;

&lt;p&gt;Combining my requirements and my new understanding of Qlot, I modified my
workflow for pinning library versions to be:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Qlot executable must be available in your path.&lt;/li&gt;
  &lt;li&gt;Specify the distribution and library versions in &lt;code class="language-plaintext highlighter-rouge"&gt;qlfile&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;qlot install&lt;/code&gt; at the CLI.&lt;/li&gt;
  &lt;li&gt;Load lisp without executing init scripts, e.g. &lt;code class="language-plaintext highlighter-rouge"&gt;ccl -n&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Load Quicklisp from the project local installation.&lt;/p&gt;

    &lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;(load "PROJECT-PATH/.qlot/setup.lisp")&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use Quicklisp as before.&lt;/p&gt;

    &lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;(ql:quickload :alexandria)&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you would like to vendor your libraries then:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Specify the distribution and library versions in &lt;code class="language-plaintext highlighter-rouge"&gt;qlfile&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;qlot install&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;qlot bundle&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;git commit&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;ccl&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;(load PROJECT-PATH/.bundle-libs/setup.lisp)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="qlot-tasks"&gt;Qlot tasks&lt;/h3&gt;

&lt;p&gt;All Qlot related tasks such as initialising a project, installing libraries,
upgrading libraries, etc must be performed in the CLI using the Qlot
executable. These happen relatively infrequently and inside the REPL Qlot does
not feature.&lt;/p&gt;</description>
	
	<pubDate>Sun, 12 Apr 2026 00:00:00 GMT</pubDate>
</item>

<item>
	<title>Tim Bradshaw: Rules for Lisp programs</title>
	<guid isPermaLink="true">https://www.tfeb.org/fragments/2026/04/08/rules-for-lisp-programs/?utm_source=lisp&amp;utm_medium=RSS</guid>
	<link>https://www.tfeb.org/fragments/2026/04/08/rules-for-lisp-programs/?utm_source=lisp&amp;utm_medium=RSS</link>
	
	<description>&lt;p&gt;Some very serious rules. Very serious.&lt;/p&gt;
&lt;!-- more--&gt;

&lt;p&gt;&lt;strong&gt;The essential rule.&lt;/strong&gt; If you are not building languages in Lisp why are you even here?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesser rules.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
 &lt;li&gt;If you write a program which uses &lt;code&gt;defclass&lt;/code&gt; you are probably making a mistake.&lt;/li&gt;
 &lt;li&gt;If you write a program which uses the CLOS MOP you are making a mistake.&lt;/li&gt;
 &lt;li&gt;If you write a program which uses LOOP for any purpose other than creating a better iteration construct you are making a mistake.&lt;/li&gt;
 &lt;li&gt;If you write a program which uses LOOP only to create a better iteration construct you are probably making a mistake.&lt;/li&gt;
 &lt;li&gt;If you write a program which uses explicit package-qualified names more than very infrequently you will be cast into the outer darkness along with your program.&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;I will not be taking questions.&lt;/p&gt;</description>
	
	<pubDate>Wed, 08 Apr 2026 10:48:26 GMT</pubDate>
</item>


</channel>
</rss>
