<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<feed xmlns="http://www.w3.org/2005/Atom">

	<title>Planet Clojure</title>
	<link rel="self" href="http://planet.clojure.in/atom.xml"/>
	<link href="http://planet.clojure.in/"/>
	<id>http://planet.clojure.in/atom.xml</id>
	<updated>2026-07-10T22:08:28+00:00</updated>
	<generator uri="http://www.planetplanet.org/">http://intertwingly.net/code/venus/</generator>

	<entry>
		<title type="html">Finding The Next Working Day, With Clojure</title>
		<link href="https://clojure-diary.gitlab.io/2026/07/10/finding-the-next-working-day-with-clojure.html"/>
		<id>https://clojure-diary.gitlab.io/2026/07/10/finding-the-next-working-day-with-clojure</id>
		<updated>2026-07-10T08:53:00+00:00</updated>
		<content type="html">&lt;div class=&quot;responsive-video-container&quot;&gt;
    
  &lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gitlab.com/clojure-diary/code/miscellaneous/-/raw/master/next_working_day.clj&quot;&gt;next_working_day.clj&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gitlab.com/clojure-diary/code/miscellaneous/-/raw/master/next_working_day_2.clj&quot;&gt;next_working_day_2.clj&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
		<author>
			<name>Karthikeyan A K.</name>
			<uri>https://clojure-diary.gitlab.io/</uri>
		</author>
		<source>
			<title type="html">Clojure Diary</title>
			<subtitle type="html">Hello, I am Karthikeyan A K, a software architect, for some reason I am getting hooked up with Clojure, and these writings are my experience with this language. One may contact me @ +91 8428050777 for Clojure consolation in weekends.</subtitle>
			<link rel="self" href="https://clojure-diary.gitlab.io/feed.xml"/>
			<id>https://clojure-diary.gitlab.io/feed.xml</id>
		</source>
	</entry>

	<entry>
		<title type="html" xml:lang="en-us">Q3 2026 Funding Survey</title>
		<link href="https://www.clojuriststogether.org/news/q3-2026-funding-survey/"/>
		<id>https://www.clojuriststogether.org/news/q3-2026-funding-survey/</id>
		<updated>2026-07-08T02:00:00+00:00</updated>
		<content type="html" xml:lang="en-us">&lt;p&gt;Greetings Clojurists!&lt;/p&gt;
&lt;p&gt;Please take a moment to complete the Q3 2026 Funding Survey which helps inform our Q3 project awards. It is not a heavy lift - maybe 5 minutes of your time.  Your input is invaluable! A link to the survey was sent to your email in the last few weeks - and just in case it made its way to spam, you can look for “We Need Your Input - Q3 2026 Funding”. The survey closes midnight PST on July 15, 2026.&lt;/p&gt;
&lt;p&gt;Thanks as always for your support of Clojurists Together and for being a part of this awesome community.&lt;/p&gt;
&lt;p&gt;Any questions, please email me at &lt;a href=&quot;mailto:kdavis@clojuriststogether.org&quot;&gt;kdavis@clojuriststogether.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Kathy Davis
Program Manager
Clojurists Together Foundation&lt;/p&gt;</content>
		<author>
			<name>Clojurists Together</name>
			<uri>https://www.clojuriststogether.org/</uri>
		</author>
		<source>
			<title type="html">Clojurists Together</title>
			<subtitle type="html">Recent content on Clojurists Together</subtitle>
			<link rel="self" href="https://www.clojuriststogether.org/index.xml"/>
			<id>https://www.clojuriststogether.org/</id>
		</source>
	</entry>

	<entry>
		<title type="html">Destructuring with computed keys</title>
		<link href="https://blog.ambrosebs.com"/>
		<id>https://blog.ambrosebs.com</id>
		<updated>2026-07-07T23:55:00+00:00</updated>
		<content type="html">&lt;p&gt;In Clojure you can destructure a map using an arbitrary expression as the key.
For example, here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kw&lt;/code&gt; is a local binding.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kw&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;=&amp;gt; 1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Usually this syntax is demonstrated as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{sym0 :kw0 sym1 :kw1 ...}&lt;/code&gt;,
which doesn’t reveal that the keywords are actually in &lt;a href=&quot;https://en.wikipedia.org/wiki/Operational_semantics#Big-step_semantics&quot;&gt;expression position&lt;/a&gt;,
or an &lt;a href=&quot;https://en.wikipedia.org/wiki/Operational_semantics#Reduction_semantics&quot;&gt;evaluation context&lt;/a&gt;.
The reason why this more recognizable syntax works is because keyword literals
are self-evaluating.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;=&amp;gt; 1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The basic rule for expanding these expressions is:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So that code is equivalent to:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Symbols are not self-evaluating syntax in Clojure, so they must be quoted:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;=&amp;gt; 1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Applying the rule makes the need more obvious:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Destructuring is pleasingly compositional. This ability to drop down to
computed keys makes destructuring available in many more situations
than if all keys were required to be statically declared.
I found a few examples in my own code and other libraries where this flexibility has been useful.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/typedclojure/typedclojure/blob/a16fef836e103676af8fba63345c7da8dda34b6d/typed/clj.checker/src/typed/clj/checker/check.cljc#L286&quot;&gt;An example&lt;/a&gt; that dereferences the var &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u/expr-type&lt;/code&gt; to compute the key:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cargs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u/expr-type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr-noinline&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ana2/unmark-top-level&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ana2/unmark-eval-top-level&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;check-expr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/typedclojure/typedclojure/blob/a16fef836e103676af8fba63345c7da8dda34b6d/typed/clj.checker/src/typed/clj/checker/check/type_hints.clj#L26-L36&quot;&gt;Another example&lt;/a&gt; that uses three class literals as computed keys:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reflect-u/reflect&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clojure.reflect.Method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clojure.reflect.Field&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctors&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clojure.reflect.Constructor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;members&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group-by&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;constructor-call&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;instance?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clojure.reflect.Constructor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m-or-f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:members&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/typedclojure/typedclojure/blob/a16fef836e103676af8fba63345c7da8dda34b6d/typed/clj.checker/test/typed_test/cljc/checker/cs_gen.clj#L195-L202&quot;&gt;A snippet of code&lt;/a&gt; 
that destructures nested maps using a mix of keywords, quoted symbols and computed vectors-of-locals as keys.
Notice that the vectors are in binding position sometimes to introduce names, then
in expression position to perform lookups.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{{[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:fv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remap&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{{{[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_x1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_x2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_y1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_y2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_y3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_y4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z1_x1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z1_y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z2_x1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z2_y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:idx-context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:fv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:idx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'z&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:idx-context&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_x1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_x2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                              &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_y1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_y2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_y3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1_y4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z1_x1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                 &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z1_y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z2_x1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                 &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z2_y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/typedclojure/typedclojure/blob/a16fef836e103676af8fba63345c7da8dda34b6d/typed/clj.runtime/src/clojure/core/typed/impl.cljc#L110&quot;&gt;You’ve probably seen code like this&lt;/a&gt;
that destructures booleans from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;group-by&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inits&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group-by&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normalised-bindings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/metosin/malli/blob/a74e3b45efa30b3bcdb2e997f337c71614eba3c5/src/malli/transform.cljc#L411C1-L413C72&quot;&gt;This Malli snippet&lt;/a&gt; nests &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:keys&lt;/code&gt; destructuring under a local binding key, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-value-transformer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qname&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transformers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/typedclojure/typedclojure/blob/main/typed/clj.checker/src/typed/cljc/checker/check/cache.cljc#L228&quot;&gt;And this example&lt;/a&gt; elegantly destructures a nested map using keywords and locals, supporting the common pattern
of updating a nested value in an atom then destructuring the swapped-in value’s relevant parts.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove-stale-cache-entries&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nsym&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns-form-str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sforms&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slurped&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:pre&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;simple-symbol?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nsym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns-form-str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{{{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forms-cache&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns-form-str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nsym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::check-form-cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env/swap-checker!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env/checker&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update-in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::check-form-cache&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nsym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;some-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;select-keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns-form-str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;not-empty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ns-form-str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;select-keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sforms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content>
		<author>
			<name>Ambrose Bonnaire-Sergeant</name>
			<uri>https://blog.ambrosebs.com</uri>
		</author>
		<source>
			<title type="html">dissoc</title>
			<link rel="self" href="https://blog.ambrosebs.com/atom.xml"/>
			<id>https://blog.ambrosebs.com/</id>
		</source>
	</entry>

	<entry>
		<title type="html">biff.graph: structure your data model as a queryable graph</title>
		<link href="https://biffweb.com/p/graph/"/>
		<id>https://biffweb.com/p/graph/</id>
		<updated>2026-07-07T17:45:00+00:00</updated>
		<content type="html">&lt;p&gt;The next Biff 2 library is ready to go:
&lt;a href=&quot;https://github.com/jacobobryant/biff/tree/v2.x/libs/graph&quot;&gt;biff.graph&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;biff.graph makes your codebase more understandable/maintainable by helping you split up your code
for reading/deriving data into small independent chunks. &quot;Data-oriented dependency injection&quot; is a
term I've used to describe the approach.&lt;/p&gt;
&lt;p&gt;biff.graph is a lightweight clone/variant of &lt;a href=&quot;https://pathom3.wsscode.com/&quot;&gt;Pathom&lt;/a&gt;. I'm a huge fan
of Pathom and wanted to include it in Biff by default, however it is a bit of a heavy abstraction
and I was concerned if the benefits would be worth the learning effort for people working on small
projects.&lt;/p&gt;
&lt;p&gt;So biff.graph is an attempt to provide something similar to Pathom in as few lines of code as
possible (~600 to be specific, with ~200 for &lt;a href=&quot;https://github.com/jacobobryant/biff/blob/95da468c8c54324ccd4f44f04b808ad0c850513d/libs/graph/src/com/biffweb/graph/impl/query.clj&quot;&gt;the query
engine&lt;/a&gt;).
You still have to learn the same conceptual model, but learning what exactly it's doing under the
hood should be easier. The tradeoff is that biff.graph has less functionality--most significantly
there's no query planner, so biff.graph isn't as intelligent about how it executes your queries as
Pathom is.&lt;/p&gt;</content>
		<author>
			<name>Jacob O'Bryant</name>
			<uri>https://biffweb.com</uri>
		</author>
		<source>
			<title type="html">Biff</title>
			<link rel="self" href="https://biffweb.com/feed.xml"/>
			<id>https://biffweb.com/feed.xml</id>
		</source>
	</entry>

	<entry>
		<title type="html">libl.in, &amp;amp; Injee, Thanks To Clojure Community. Updates in Clojure Book.</title>
		<link href="https://clojure-diary.gitlab.io/2026/07/07/libl-in-injee-thanks-to-clojure-community-updates-in-clojure-book.html"/>
		<id>https://clojure-diary.gitlab.io/2026/07/07/libl-in-injee-thanks-to-clojure-community-updates-in-clojure-book</id>
		<updated>2026-07-07T14:59:00+00:00</updated>
		<content type="html">&lt;div class=&quot;responsive-video-container&quot;&gt;
    
  &lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Injee
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://tossconf26.kaniyam.com/&quot;&gt;https://tossconf26.kaniyam.com/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Tamil Linux
        &lt;ul&gt;
          &lt;li&gt;Telegram
            &lt;ul&gt;
              &lt;li&gt;&lt;a href=&quot;https://t.me/tamillinux&quot;&gt;https://t.me/tamillinux&lt;/a&gt;&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;ilugc
            &lt;ul&gt;
              &lt;li&gt;&lt;a href=&quot;https://ilugc.in/&quot;&gt;https://ilugc.in/&lt;/a&gt;&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;https://libl.in
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://clojure-book.gitlab.io/book.html#_installing_stuff&quot;&gt;https://clojure-book.gitlab.io/book.html#_installing_stuff&lt;/a&gt;
        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://yu7.in/clj-installing-stuff&quot;&gt;https://yu7.in/clj-installing-stuff&lt;/a&gt;
            &lt;ul&gt;
              &lt;li&gt;Now it’s fixed :D&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Clojure Book
    &lt;ul&gt;
      &lt;li&gt;Proof read with A.I
        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://gitlab.com/clojure-book/clojure-book.gitlab.io/-/tree/proof_read&quot;&gt;https://gitlab.com/clojure-book/clojure-book.gitlab.io/-/tree/proof_read&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Release in all human languages&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;-ve’s
    &lt;ul&gt;
      &lt;li&gt;Video series about ring is stalled&lt;/li&gt;
      &lt;li&gt;Stats with Clojure book is stalled
        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://scicloj.github.io/stats_with_clojure/&quot;&gt;https://scicloj.github.io/stats_with_clojure/&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content>
		<author>
			<name>Karthikeyan A K.</name>
			<uri>https://clojure-diary.gitlab.io/</uri>
		</author>
		<source>
			<title type="html">Clojure Diary</title>
			<subtitle type="html">Hello, I am Karthikeyan A K, a software architect, for some reason I am getting hooked up with Clojure, and these writings are my experience with this language. One may contact me @ +91 8428050777 for Clojure consolation in weekends.</subtitle>
			<link rel="self" href="https://clojure-diary.gitlab.io/feed.xml"/>
			<id>https://clojure-diary.gitlab.io/feed.xml</id>
		</source>
	</entry>

	<entry>
		<title type="html" xml:lang="en-gb">Clojure 1.13.0-alpha3</title>
		<link href="https://clojure.org/news/2026/07/07/clojure-1-13-alpha3"/>
		<id>https://clojure.org/news/2026/07/07/clojure-1-13-alpha3</id>
		<updated>2026-07-07T00:00:00+00:00</updated>
		<content type="html">&lt;div id=&quot;preamble&quot;&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Clojure 1.13.0-alpha3 is now available! Find download and usage information on the &lt;a href=&quot;https://clojure.org/../../../../releases/downloads&quot;&gt;Downloads&lt;/a&gt; page.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;_select_directive_in_map_destructuring&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;https://clojure.org/feed.xml#_select_directive_in_map_destructuring&quot;&gt;&lt;/a&gt;:select directive in map destructuring&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;The :select directive binds a name to a subset of the map being destructured containing only the keys mentioned (anywhere) in the binding form.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2964&quot;&gt;CLJ-2964&lt;/a&gt; :select directive in map destructuring&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2963&quot;&gt;CLJ-2963&lt;/a&gt; Update specs for :select in destructuring&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;_other_changes_since_clojure_1_13_0_alpha2&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;https://clojure.org/feed.xml#_other_changes_since_clojure_1_13_0_alpha2&quot;&gt;&lt;/a&gt;Other changes since Clojure 1.13.0-alpha2&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;RT.map, and thus reader, tracks new PAM thresholds&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-1789&quot;&gt;CLJ-1789&lt;/a&gt; select-keys - improve performance (transients, etc)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2958&quot;&gt;CLJ-2958&lt;/a&gt; ILookup on sets&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2902&quot;&gt;CLJ-2902&lt;/a&gt; pprint - prints arbitary objects in unreadable form&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2801&quot;&gt;CLJ-2801&lt;/a&gt; TaggedLiteral - doesn’t define print-dup&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2269&quot;&gt;CLJ-2269&lt;/a&gt; definterface - does not resolve parameter type hints&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2781&quot;&gt;CLJ-2781&lt;/a&gt; clojure.test/report - docstring has broken references&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2929&quot;&gt;CLJ-2929&lt;/a&gt; zipper - docstring typo&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2901&quot;&gt;CLJ-2901&lt;/a&gt; bytes, shorts, chars - docstring typos&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2811&quot;&gt;CLJ-2811&lt;/a&gt; scalb - docstring links to the documentation for nextDown&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clojure.atlassian.net/browse/CLJ-2809&quot;&gt;CLJ-2809&lt;/a&gt; clojure.math/floor - docstring has line that should be on ceil docstring&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;_try_it_out&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;https://clojure.org/feed.xml#_try_it_out&quot;&gt;&lt;/a&gt;Try it out&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Update your deps.edn :deps with:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;org.clojure/clojure {:mvn/version &quot;1.13.0-alpha3&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Start a REPL with the Clojure CLI (any version) with:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;clj -Sdeps '{:deps {org.clojure/clojure {:mvn/version &quot;1.13.0-alpha3&quot;}}}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</content>
		<author>
			<name>Clojure.org</name>
			<uri>https://clojure.org</uri>
		</author>
		<source>
			<title type="html">Clojure News</title>
			<subtitle type="html">Clojure News</subtitle>
			<link rel="self" href="https://clojure.org/feed.xml"/>
			<id>https://clojure.org</id>
		</source>
	</entry>

	<entry>
		<title type="html">OSS updates May and June 2026</title>
		<link href="https://blog.michielborkent.nl/oss-updates-may-jun-2026.html"/>
		<id>https://blog.michielborkent.nl/oss-updates-may-jun-2026.html</id>
		<updated>2026-07-06T23:59:59+00:00</updated>
		<content type="html">&lt;div&gt;&lt;p&gt;In this post I&amp;amp;aposll give updates about open source I worked on during May and June 2026.&lt;/p&gt;&lt;p&gt;To see previous OSS updates, go &lt;a href=&quot;https://blog.michielborkent.nl/tags/oss-updates.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;sponsors&quot;&gt;Sponsors&lt;/h2&gt;&lt;p&gt;I&amp;amp;aposd like to thank all the sponsors and contributors that make this work possible. Without you, the below projects would not be as mature or wouldn&amp;amp;apost exist or be maintained at all! So a sincere thank you to everyone who contributes to the sustainability of these projects.&lt;/p&gt;&lt;img alt=&quot;gratitude&quot; src=&quot;https://emoji.slack-edge.com/T03RZGPFR/gratitude/f8716bb6fb7e5249.png&quot; width=&quot;50px&quot; /&gt;&lt;p&gt;Current top tier sponsors:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://clojuriststogether.org/&quot;&gt;Clojurists Together&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://roamresearch.com/&quot;&gt;Roam Research&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nextjournal.com/&quot;&gt;Nextjournal&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nubank.com.br&quot;&gt;Nubank&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Open the details section for more info about sponsoring.&lt;/p&gt;&lt;details&gt;
Sponsor info&lt;p&gt;If you want to ensure that the projects I work on are sustainably maintained, you can sponsor this work in the following ways. If you work for a company that uses my OSS, please ask your employer, that would be even better. Thank you!&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/sponsors/borkdude&quot;&gt;Github Sponsors&lt;/a&gt;&lt;/li&gt;&lt;li&gt;The &lt;a href=&quot;https://opencollective.com/babashka&quot;&gt;Babashka&lt;/a&gt; or &lt;a href=&quot;https://opencollective.com/clj-kondo&quot;&gt;Clj-kondo&lt;/a&gt; OpenCollective&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ko-fi.com/borkdude&quot;&gt;Ko-fi&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.patreon.com/borkdude&quot;&gt;Patreon&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.clojuriststogether.org/&quot;&gt;Clojurists Together&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;h2 id=&quot;updates&quot;&gt;Updates&lt;/h2&gt;&lt;p&gt;A lot happened in the past two months! Not just coding but also...&lt;/p&gt;&lt;h3 id=&quot;babashka-conf-2026-and-dutch-clojure-days&quot;&gt;Babashka Conf 2026 and Dutch Clojure Days&lt;/h3&gt;&lt;p&gt;Three years after the initial installment, &lt;a href=&quot;https://babashka.org/conf/&quot;&gt;Babashka Conf 2026&lt;/a&gt; happened on May 8th at the OBA Oosterdok library in Amsterdam, with David Nolen, primary maintainer of ClojureScript, as our keynote speaker. Thanks to our sponsors Nubank, Exoscale, &lt;a href=&quot;https://github.com/bobisageek&quot;&gt;Bob&lt;/a&gt;, &lt;a href=&quot;https://flexiana.com&quot;&gt;Flexiana&lt;/a&gt; and &lt;a href=&quot;https://itonomi.com&quot;&gt;Itonomi&lt;/a&gt;, to Wendy Randolph for hosting, and to all the speakers, volunteers and attendees who made it such an inspiring day. You can watch all the videos &lt;a href=&quot;https://www.youtube.com/watch?v=c5RTAtodh3M&amp;amp;list=PLaN-rC-CjQqCClhXmwzE7XhGWbrbB-A7u&quot;&gt;here&lt;/a&gt;. Thanks to Ray for recording! The day after, &lt;a href=&quot;https://clojuredays.org/&quot;&gt;Dutch Clojure Days 2026&lt;/a&gt; rounded out a full weekend of Clojure in Amsterdam, where I did a presentation about &lt;a href=&quot;https://clojurescript.org/reference/async-functions&quot;&gt;ClojureScript and async/await&lt;/a&gt;. The video of that is hopefully coming soon.&lt;/p&gt;&lt;img align=&quot;center&quot; alt=&quot;Babashka Conf 2026 speakers and organizers&quot; src=&quot;https://blog.michielborkent.nl/assets/babashka-conf-2026.jpg&quot; width=&quot;70%&quot; /&gt;&lt;p&gt;&lt;em&gt;Babashka Conf 2026. From left to right: David Nolen, Jen Myers, Adrian Smith, Josh Glover, Rahul Dé, Arne Brasseur, Christoph Neumann, Timo Kramer, Jynn Nelson, Wendy Randolph.&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;upcoming:-babashka-workshop-at-the-clojure/conj&quot;&gt;Upcoming: babashka workshop at the Clojure/conj&lt;/h3&gt;&lt;p&gt;I&amp;amp;aposm pleased to announce that Rahul Dé and I will be hosting a babashka workshop at the &lt;a href=&quot;https://2026.clojure-conj.org/workshops&quot;&gt;Clojure Conj 2026&lt;/a&gt;. The workshop will showcase various use cases of babashka. This hands-on workshop covers the whole lifecycle of a babashka tool, from a quick script to a published, installable CLI app. We assume you know the basics of Clojure and won&amp;amp;apost explain the language itself. Topics include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Setting up your dev environment&lt;/li&gt;&lt;li&gt;Managing projects with babashka tasks (&lt;code&gt;bb.edn&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;A tour of built-in libraries (fs, process, http-client, and more)&lt;/li&gt;&lt;li&gt;Writing and running tests&lt;/li&gt;&lt;li&gt;Building a CLI with subcommands and automatic help&lt;/li&gt;&lt;li&gt;Programming a terminal UI (TUI)&lt;/li&gt;&lt;li&gt;Producing a small web app&lt;/li&gt;&lt;li&gt;Publishing via GitHub or as an installable tool with &lt;code&gt;bbin&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Every concept comes with an exercise, building toward one culminating CLI app. There will be lots of interaction and fun!&lt;/p&gt;&lt;h3 id=&quot;blog-posts&quot;&gt;Blog posts&lt;/h3&gt;&lt;p&gt;Besides this update I published two blog posts in the past two months:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://blog.michielborkent.nl/babashka-cli-help-and-completions.html&quot;&gt;Babashka CLI: automatic &lt;code&gt;--help&lt;/code&gt; and shell completions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://blog.michielborkent.nl/clj-kondo-call-graph-metadata.html&quot;&gt;Finding transitive var usages with clj-kondo&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;and a ClojureScript reference on async functions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://clojurescript.org/reference/async-functions&quot;&gt;Async functions&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;projects&quot;&gt;Projects&lt;/h3&gt;&lt;p&gt;Babashka CLI got the most attention this cycle. I added automatic &lt;code&gt;--help&lt;/code&gt; generation for &lt;code&gt;dispatch&lt;/code&gt;-based CLIs and shell tab completion for bash, zsh, fish, PowerShell and Nushell. There&amp;amp;aposs a dedicated post with a &quot;build your own git&quot; walkthrough linked above. I also made Babashka CLI &lt;a href=&quot;https://github.com/squint-cljs/squint&quot;&gt;Squint&lt;/a&gt; compatible, so CLIs built with it run on Node.js and in the browser, published as the &lt;code&gt;@babashka/cli&lt;/code&gt; npm package. Also ClojureDart support for Babashka CLI got added.&lt;/p&gt;&lt;p&gt;Squint saw a large amount of work that kept going right into early July: a &lt;a href=&quot;https://github.com/squint-cljs/squint#browser-repl&quot;&gt;browser nREPL&lt;/a&gt;, dynamic vars and &lt;code&gt;binding&lt;/code&gt; that survive across separately-compiled ESM modules, an EDN reader, cached lazy seqs, &lt;code&gt;defrecord&lt;/code&gt; and a wide set of core protocols, and a big compatibility push to make it pass jank&amp;amp;aposs clojure-test-suite. &lt;a href=&quot;https://github.com/squint-cljs/squint/tree/main/examples/replicant&quot;&gt;Replicant&lt;/a&gt; now runs on Squint too. I added key diffing to &lt;a href=&quot;https://github.com/borkdude/reagami&quot;&gt;Reagami&lt;/a&gt; and did some &lt;a href=&quot;https://github.com/borkdude/reagami#benchmarks&quot;&gt;benchmarks&lt;/a&gt;, showing that Reagami on squint performs in the ballpark of React. The benchmark also shows that Replicant on Squint performs even a tad better than on ClojureScript. Not that this makes a huge difference in practice, but it&amp;amp;aposs nice to validate the idea that Squint, for typical apps, can be a valid CLJS replacement while not giving up that much in terms of Clojure features.&lt;/p&gt;&lt;p&gt;A security issue in SCI deserves a callout. A string type-hint could bypass the &lt;code&gt;:classes&lt;/code&gt; allowlist and statically initialize any class on the classpath at analysis time. If you sandbox untrusted code with SCI, upgrade to 0.13.53. ClojureDart support and fine-grained interop control (which was needed for cljd support since it has no reflection) also got added. You can now make REPLs for your mobile apps!&lt;/p&gt;&lt;p&gt;Since porting was a theme these past months, I&amp;amp;aposll mention another one: &lt;a href=&quot;https://github.com/babashka/fs&quot;&gt;babashka.fs&lt;/a&gt; now runs on Node.js via ClojureScript and squint, published as the &lt;code&gt;@babashka/fs&lt;/code&gt; npm package.&lt;/p&gt;&lt;p&gt;Here are some highlights per project. See each project&amp;amp;aposs &lt;code&gt;CHANGELOG.md&lt;/code&gt; for the full list.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/babashka/cli&quot;&gt;babashka CLI&lt;/a&gt;: Turn Clojure functions into CLIs!&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Automatic &lt;code&gt;--help&lt;/code&gt; generation for &lt;code&gt;dispatch&lt;/code&gt; CLIs, plus shell completions for bash, zsh, fish, PowerShell and Nushell (&lt;a href=&quot;https://github.com/babashka/cli/issues/112&quot;&gt;#112&lt;/a&gt;, &lt;a href=&quot;https://github.com/babashka/cli/issues/24&quot;&gt;#24&lt;/a&gt;, &lt;a href=&quot;https://github.com/babashka/cli/pull/95&quot;&gt;#95&lt;/a&gt;). I wrote a full post on it with a &quot;write your own git&quot; walkthrough: &lt;a href=&quot;https://blog.michielborkent.nl/babashka-cli-help-and-completions.html&quot;&gt;babashka CLI: automatic --help and shell completions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Exposed the underlying building blocks so you can roll your own custom CLI parsing: &lt;code&gt;parse-opts*&lt;/code&gt;, &lt;code&gt;coerce-opts&lt;/code&gt;, &lt;code&gt;validate-opts&lt;/code&gt;, &lt;code&gt;apply-defaults&lt;/code&gt;, &lt;code&gt;table-&amp;gt;tree&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;dispatch&lt;/code&gt; now accepts a tree directly (as returned by &lt;code&gt;table-&amp;gt;tree&lt;/code&gt;), and subcommand order is preserved in printed help and completions&lt;/li&gt;&lt;li&gt;Squint support and a new &lt;code&gt;@babashka/cli&lt;/code&gt; npm package&lt;/li&gt;&lt;li&gt;ClojureDart support (&lt;a href=&quot;https://github.com/babashka/cli/issues/182&quot;&gt;#182&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;code&gt;opts-&amp;gt;table&lt;/code&gt; accepts &lt;code&gt;:columns&lt;/code&gt; to override the auto-detected columns (&lt;a href=&quot;https://github.com/babashka/cli/issues/148&quot;&gt;#148&lt;/a&gt;, thanks Jan Seeger)&lt;/li&gt;&lt;li&gt;Better error messages: negation errors now name the base option, &lt;code&gt;--no-foo&lt;/code&gt; on a non-boolean option errors instead of silently coercing, and &lt;code&gt;:edn&lt;/code&gt; &lt;code&gt;:coerce&lt;/code&gt; now requires an explicit value (&lt;a href=&quot;https://github.com/babashka/cli/issues/166&quot;&gt;#166&lt;/a&gt;, &lt;a href=&quot;https://github.com/babashka/cli/issues/174&quot;&gt;#174&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Thanks to &lt;a href=&quot;https://github.com/lread&quot;&gt;@lread&lt;/a&gt; for a lot of documentation review and general maintenance during this cycle&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/cli/blob/master/CHANGELOG.md&quot;&gt;Full changelog&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/squint-cljs/squint&quot;&gt;Squint&lt;/a&gt;: CLJS &lt;em&gt;syntax&lt;/em&gt; to JS compiler&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Browser nREPL support landed, followed by a number of REPL/nREPL fixes: &lt;a href=&quot;https://github.com/squint-cljs/squint/issues/815&quot;&gt;#815&lt;/a&gt; (&lt;code&gt;str&lt;/code&gt; wrapping tripping esbuild), &lt;a href=&quot;https://github.com/squint-cljs/squint/issues/819&quot;&gt;#819&lt;/a&gt; (macro changes not picked up in watch mode), &lt;a href=&quot;https://github.com/squint-cljs/squint/issues/820&quot;&gt;#820&lt;/a&gt; (&lt;code&gt;:macros&lt;/code&gt; option ignored from JS callers) and &lt;a href=&quot;https://github.com/squint-cljs/squint/issues/832&quot;&gt;#832&lt;/a&gt; (nREPL server hanging on advertised-but-unimplemented ops)&lt;/li&gt;&lt;li&gt;The CLI now gets its &lt;code&gt;--help&lt;/code&gt;, usage and error handling from babashka.cli&amp;amp;aposs &lt;code&gt;dispatch&lt;/code&gt;, plus shell tab completion&lt;/li&gt;&lt;li&gt;Dynamic vars and &lt;code&gt;binding&lt;/code&gt; now work via a mutable box, safe across separately-compiled ESM modules; syntax-quote resolves symbols through the current namespace and aliases like Clojure. &lt;code&gt;defprotocol&lt;/code&gt; got &lt;code&gt;:extend-via-metadata&lt;/code&gt; support.&lt;/li&gt;&lt;li&gt;&lt;code&gt;reify&lt;/code&gt; added&lt;/li&gt;&lt;li&gt;&lt;code&gt;clojure.walk&lt;/code&gt; added&lt;/li&gt;&lt;li&gt;Added &lt;code&gt;squint.edn&lt;/code&gt;/&lt;code&gt;clojure.edn&lt;/code&gt; with a ~300-line EDN reader&lt;/li&gt;&lt;li&gt;Printing is now done through &lt;code&gt;*print-fn*&lt;/code&gt;, &lt;code&gt;print&lt;/code&gt;, &lt;code&gt;pr&lt;/code&gt; and &lt;code&gt;with-out-str&lt;/code&gt;, like CLJS&lt;/li&gt;&lt;li&gt;Lazy seqs are now cached instead of recomputed on every consumption, matching CLJS&amp;amp;aposs chunked-seq behavior&lt;/li&gt;&lt;li&gt;A big push for compatibility with jank&amp;amp;aposs clojure-test-suite: dozens of core functions (&lt;code&gt;sorted-map&lt;/code&gt;, &lt;code&gt;hash-map&lt;/code&gt;, &lt;code&gt;subvec&lt;/code&gt;, &lt;code&gt;pop&lt;/code&gt;, &lt;code&gt;merge&lt;/code&gt;, &lt;code&gt;keys&lt;/code&gt;/&lt;code&gt;vals&lt;/code&gt;, &lt;code&gt;peek&lt;/code&gt;, transducers, &lt;code&gt;=&lt;/code&gt; on dates/regexes/lazy seqs, and more) now throw or behave exactly like CLJS instead of the old loose JS semantics, alongside full built-in &lt;code&gt;cljs.test&lt;/code&gt; support&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/squint-cljs/squint/issues/771&quot;&gt;#771&lt;/a&gt;: dead-code elimination for varargs/multi-arity functions, now emitted via &lt;code&gt;...&lt;/code&gt; spread&lt;/li&gt;&lt;li&gt;Replicant support landed, with an example&lt;/li&gt;&lt;li&gt;Added &lt;code&gt;defrecord&lt;/code&gt;, &lt;code&gt;record?&lt;/code&gt; and the &lt;code&gt;IRecord&lt;/code&gt; marker protocol. Records store their fields as own string-keyed properties and implement the map-facing protocols, so keyword lookup, &lt;code&gt;keys&lt;/code&gt;, &lt;code&gt;seq&lt;/code&gt;, &lt;code&gt;assoc&lt;/code&gt;, &lt;code&gt;conj&lt;/code&gt; and &lt;code&gt;=&lt;/code&gt; all work through the regular core functions; the generated implementations are shared runtime functions imported only by files that use &lt;code&gt;defrecord&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Added a large set of core protocols so custom types participate in the standard functions: &lt;code&gt;ILookup&lt;/code&gt;, &lt;code&gt;IAssociative&lt;/code&gt;, &lt;code&gt;IMap&lt;/code&gt;, &lt;code&gt;ICounted&lt;/code&gt;, &lt;code&gt;ICollection&lt;/code&gt;, &lt;code&gt;IEquiv&lt;/code&gt;, &lt;code&gt;ISet&lt;/code&gt;, the transient protocols, and &lt;code&gt;IAtom&lt;/code&gt;/&lt;code&gt;IDeref&lt;/code&gt;/&lt;code&gt;IReset&lt;/code&gt;/&lt;code&gt;ISwap&lt;/code&gt;/&lt;code&gt;IWatchable&lt;/code&gt; (so a reagent-style reactive atom can be a plain &lt;code&gt;deftype&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;Compile-time namespace resolution: &lt;code&gt;cljs.analyzer.api/resolve&lt;/code&gt; now sees vars of built-in library namespaces like &lt;code&gt;clojure.string&lt;/code&gt;, plus &lt;code&gt;:squint/compile-time&lt;/code&gt; forms and fixes for macro self-use&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/squint-cljs/squint/blob/main/CHANGELOG.md&quot;&gt;Full changelog&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo&quot;&gt;clj-kondo&lt;/a&gt;: static analyzer and linter for Clojure code that sparks joy.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;NEW: macros from source. A &lt;code&gt;defmacro&lt;/code&gt; (plus any supporting &lt;code&gt;defn&lt;/code&gt;/&lt;code&gt;defn-&lt;/code&gt;/&lt;code&gt;def&lt;/code&gt;) tagged with &lt;code&gt;{:clj-kondo/macroexpand-hook true}&lt;/code&gt; is automatically extracted into &lt;code&gt;.clj-kondo/&lt;/code&gt; and registered as a &lt;code&gt;:macroexpand&lt;/code&gt; hook on the next run. See &lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/blob/master/doc/hooks.md#macros-from-source&quot;&gt;doc/hooks.md&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Support for &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; in ClojureScript: bumped built-in CLJS analysis to 1.12.145 and added the &lt;code&gt;:await-without-async-fn&lt;/code&gt; and &lt;code&gt;:misplaced-async-metadata&lt;/code&gt; linters&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/issues/2822&quot;&gt;#2822&lt;/a&gt;: NEW linter &lt;code&gt;:alias-same-as-ns&lt;/code&gt;, warns when an alias equals the namespace it aliases (default &lt;code&gt;:off&lt;/code&gt;) (&lt;a href=&quot;https://github.com/tomdl89&quot;&gt;@tomdl89&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/issues/2807&quot;&gt;#2807&lt;/a&gt;: NEW linter &lt;code&gt;:conditional-build-up&lt;/code&gt;, warns on successive &lt;code&gt;(if pred (assoc m ...) m)&lt;/code&gt; rebinding and suggests &lt;code&gt;cond-&amp;gt;&lt;/code&gt; (default &lt;code&gt;:off&lt;/code&gt;) (&lt;a href=&quot;https://github.com/walber-araujo&quot;&gt;@walber-araujo&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/issues/2062&quot;&gt;#2062&lt;/a&gt;: NEW linter &lt;code&gt;:if-x-x-y&lt;/code&gt;, suggests &lt;code&gt;(or x y)&lt;/code&gt; instead of &lt;code&gt;(if x x y)&lt;/code&gt; (default &lt;code&gt;:off&lt;/code&gt;) (&lt;a href=&quot;https://github.com/jramosg&quot;&gt;@jramosg&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/issues/2818&quot;&gt;#2818&lt;/a&gt;: fix &lt;code&gt;:redefined-var&lt;/code&gt; false positive across files declaring the same namespace&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/issues/2814&quot;&gt;#2814&lt;/a&gt;: fix &lt;code&gt;:protocol-method-arity-mismatch&lt;/code&gt; false positive for &lt;code&gt;definterface&lt;/code&gt; declaring the same method with multiple arities (&lt;a href=&quot;https://github.com/jramosg&quot;&gt;@jramosg&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/issues/2817&quot;&gt;#2817&lt;/a&gt;: warn on &lt;code&gt;recur&lt;/code&gt; inside a vector, map or set literal, since &lt;code&gt;recur&lt;/code&gt; is never in tail position there&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/issues/2854&quot;&gt;#2854&lt;/a&gt;: fix &lt;code&gt;:invalid-arity&lt;/code&gt; false positive when an inner binding or fn param shadows a local function name (&lt;a href=&quot;https://github.com/yuhan0&quot;&gt;@yuhan0&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Performance work on the rewrite-clj parser and analysis internals: efficient &lt;code&gt;get-in&lt;/code&gt;/&lt;code&gt;select-keys&lt;/code&gt;, faster &lt;code&gt;sexpr&lt;/code&gt;, leaner node allocation (&lt;a href=&quot;https://github.com/alexander-yakushev&quot;&gt;@alexander-yakushev&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Deprecation notice: 2026.05.25 is the last release to include the clj-kondo LSP server and VS Code extension; use &lt;a href=&quot;https://clojure-lsp.io/&quot;&gt;clojure-lsp&lt;/a&gt; instead, which embeds clj-kondo&lt;/li&gt;&lt;li&gt;Queued for the next release: early support for the Clojure 1.13 map destructuring keys (&lt;code&gt;:keys!&lt;/code&gt;/&lt;code&gt;:syms!&lt;/code&gt;/&lt;code&gt;:strs!&lt;/code&gt;), including inferring required keys and reporting them at call sites (&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/pull/2870&quot;&gt;#2870&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md&quot;&gt;Full changelog&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/babashka/sci&quot;&gt;SCI&lt;/a&gt;: Configurable Clojure/Script interpreter suitable for scripting&lt;/p&gt;&lt;ul&gt;&lt;li&gt;ClojureDart support, with a &lt;a href=&quot;https://github.com/babashka/sci/tree/master/examples/cljd-flutter-repl&quot;&gt;Flutter REPL example&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Instance/static method and field overrides plus a &lt;code&gt;:closed&lt;/code&gt; allowlist for &lt;code&gt;:classes&lt;/code&gt;, giving fine-grained control over host interop; see the &lt;a href=&quot;https://github.com/babashka/sci/blob/master/doc/interop-control.md&quot;&gt;interop control docs&lt;/a&gt;. Also 1.6x faster instance-method interop on babashka&lt;/li&gt;&lt;li&gt;Security fix (sandbox escape): a string type-hint (e.g. &lt;code&gt;^&quot;some.Class&quot; x&lt;/code&gt;) bypassed the &lt;code&gt;:classes&lt;/code&gt; allowlist, loading and static-initializing any class on the classpath at analysis time. Only affects sandboxing of untrusted code via &lt;code&gt;:classes&lt;/code&gt;; upgrade to 0.13.53&lt;/li&gt;&lt;li&gt;Add an &lt;code&gt;:interrupt-fn&lt;/code&gt; option: a zero-arg function called on every interpreted fn entry, so host code can interrupt or cancel a running SCI eval (thanks &lt;a href=&quot;https://github.com/whilo&quot;&gt;@whilo&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Add &lt;code&gt;sci.interrupt/interrupt!&lt;/code&gt; to throw an interrupt that sandboxed &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; cannot catch, and gate &lt;code&gt;finally&lt;/code&gt; and the regex functions (&lt;code&gt;re-matches&lt;/code&gt;/&lt;code&gt;re-find&lt;/code&gt;/&lt;code&gt;re-seq&lt;/code&gt;, JVM) through &lt;code&gt;:interrupt-fn&lt;/code&gt; too, closing off ways to mask an interrupt and escape the sandbox &lt;a href=&quot;https://github.com/babashka/sci/issues/1044&quot;&gt;#1044&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Fix &lt;code&gt;copy-var&lt;/code&gt; incorrectly marking a function as inlined when its unqualified name collided with a &lt;code&gt;clojure.core&lt;/code&gt;/&lt;code&gt;cljs.core&lt;/code&gt; inlined var (e.g. a custom &lt;code&gt;get&lt;/code&gt;), silently breaking &lt;code&gt;with-redefs&lt;/code&gt; (&lt;a href=&quot;https://github.com/verberktstan&quot;&gt;@verberktstan&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Fix cross-namespace &lt;code&gt;defrecord&lt;/code&gt;/&lt;code&gt;deftype&lt;/code&gt; type symbol resolution via alias (e.g. &lt;code&gt;(instance? r/Foo x)&lt;/code&gt;), fixing &lt;a href=&quot;https://github.com/babashka/nbb/issues/410&quot;&gt;nbb#410&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Fix a self-require (a namespace requiring itself) being reported as a cyclic load dependency&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/sci/blob/master/CHANGELOG.md&quot;&gt;Full changelog&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/babashka/fs&quot;&gt;fs&lt;/a&gt;: file system utility library for Clojure&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Released 0.5.34 with Node.js support (&lt;a href=&quot;https://github.com/babashka/fs/issues/265&quot;&gt;#265&lt;/a&gt;): fs now runs on Node.js via ClojureScript and Squint / JavaScript, published as the &lt;code&gt;@babashka/fs&lt;/code&gt; npm package. Most functions are supported. The JVM behavior is the reference implementation so all operations are synchronous, and the glob syntax is reimplemented from scratch to match the JVM. File times are BigInt nanoseconds to preserve sub-millisecond precision. &lt;code&gt;zip&lt;/code&gt; is left out since Node.js has no native support for it&lt;/li&gt;&lt;li&gt;Added &lt;code&gt;spit&lt;/code&gt; and &lt;code&gt;slurp&lt;/code&gt; on both the JVM and Node.js&lt;/li&gt;&lt;li&gt;&lt;code&gt;exec-paths&lt;/code&gt; returns &lt;code&gt;[]&lt;/code&gt; when &lt;code&gt;PATH&lt;/code&gt; is unset or blank instead of throwing&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/lread&quot;&gt;@lread&lt;/a&gt; did a thorough review pass making the return values of &lt;code&gt;copy&lt;/code&gt;, &lt;code&gt;copy-tree&lt;/code&gt;, &lt;code&gt;delete-tree&lt;/code&gt;, &lt;code&gt;zip&lt;/code&gt;/&lt;code&gt;unzip&lt;/code&gt;, &lt;code&gt;gunzip&lt;/code&gt; and the setters explicit and documented/tested (&lt;a href=&quot;https://github.com/babashka/fs/issues/197&quot;&gt;#197&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/babashka/babashka&quot;&gt;Babashka&lt;/a&gt;: native, fast starting Clojure interpreter for scripting.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Working towards a new release integrating all the newest updates in Babashka CLI and babashka.fs. Most importantly I&amp;amp;aposm working on autocompletions added for tasks defined in &lt;code&gt;bb.edn&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/babashka/issues/1979&quot;&gt;#1979&lt;/a&gt;: fix &lt;code&gt;with-redefs&lt;/code&gt; on copied vars (e.g. &lt;code&gt;org.httpkit.client/get&lt;/code&gt;) incorrectly treated as inlined&lt;/li&gt;&lt;li&gt;Add &lt;code&gt;org.jline.keymap.BindingReader&lt;/code&gt; for reading key bindings in terminal applications, completing the input side of the bundled JLine API&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/babashka/issues/1982&quot;&gt;#1982&lt;/a&gt;: add &lt;code&gt;clojure.lang.ChunkedCons&lt;/code&gt;, &lt;code&gt;clojure.lang.APersistentVector$SubVector&lt;/code&gt;, &lt;code&gt;clojure.lang.ArraySeq&lt;/code&gt;, &lt;code&gt;clojure.lang.PersistentVector$ChunkedSeq&lt;/code&gt;, &lt;code&gt;java.util.AbstractCollection&lt;/code&gt; and &lt;code&gt;java.util.Queue&lt;/code&gt; to &lt;code&gt;:instance-checks&lt;/code&gt; (&lt;a href=&quot;https://github.com/paintparty&quot;&gt;@paintparty&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Added a terminal tetris example (&lt;code&gt;examples/tetris.clj&lt;/code&gt;) built on JLine&amp;amp;aposs &lt;code&gt;Display&lt;/code&gt; and &lt;code&gt;AttributedString&lt;/code&gt;, showing off the new terminal APIs&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/babashka/blob/master/CHANGELOG.md&quot;&gt;Full changelog&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/borkdude/reagami&quot;&gt;Reagami&lt;/a&gt;: A minimal zero-deps Reagent-like for Squint and CLJS&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Added keyed reconciliation (&lt;a href=&quot;https://github.com/borkdude/reagami/issues/40&quot;&gt;#40&lt;/a&gt;): support &lt;code&gt;:key&lt;/code&gt; on children for stable node identity, so diffing reuses nodes instead of recreating them&lt;/li&gt;&lt;li&gt;Fixed CLJS &lt;code&gt;:lite-mode&lt;/code&gt; compatibility and added it to CI (&lt;a href=&quot;https://github.com/borkdude/reagami/issues/41&quot;&gt;#41&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Added a benchmarks page comparing reagami against CLJS React wrappers and React-free solutions, with mermaid charts to visualize the results (&lt;a href=&quot;https://github.com/borkdude/reagami/issues/42&quot;&gt;#42&lt;/a&gt;, &lt;a href=&quot;https://github.com/borkdude/reagami/issues/43&quot;&gt;#43&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Expanded the README with an ADR on the unkeyed reconciliation algorithm&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/borkdude/cream&quot;&gt;Cream&lt;/a&gt;: Clojure + GraalVM &lt;a href=&quot;https://github.com/oracle/graal/issues/11327&quot;&gt;Crema&lt;/a&gt; native binary&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I was finally able to reproduce an issue with core.async and filed this &lt;a href=&quot;https://github.com/oracle/graal/issues/13925&quot;&gt;upstream&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Once this is fixed I&amp;amp;aposm going to consider crema more seriously and play with the thought that this could be a substrate for &quot;Babashka next&quot;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/borkdude/html&quot;&gt;html&lt;/a&gt;: Html generation library inspired by squint&amp;amp;aposs html tag&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Fixed inline &lt;code&gt;style&lt;/code&gt; maps emitting a literal &lt;code&gt;\n&lt;/code&gt; between declarations via &lt;code&gt;pr-str&lt;/code&gt;, which produced invalid CSS and dropped every declaration after the first (&lt;a href=&quot;https://github.com/cycl1st&quot;&gt;@cycl1st&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Only render a map attribute value as CSS when the key is &lt;code&gt;style&lt;/code&gt;; other map-like values (e.g. records) now render via &lt;code&gt;str&lt;/code&gt; (&lt;a href=&quot;https://github.com/telekid&quot;&gt;@telekid&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Fixed a symbol-valued attribute resolving to its runtime value instead of its literal name&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/borkdude/edamame&quot;&gt;Edamame&lt;/a&gt;: configurable EDN and Clojure parser with location metadata and more&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Added &lt;a href=&quot;https://github.com/tensegritics/ClojureDart&quot;&gt;ClojureDart&lt;/a&gt; support (non-indexing plain readers matching tools.reader, zero-literal parsing fix, and more)&lt;/li&gt;&lt;li&gt;With &lt;code&gt;:auto-resolve-ns&lt;/code&gt;, bare syntax-quoted symbols now resolve to the current namespace, matching Clojure&amp;amp;aposs behavior&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/babashka/neil&quot;&gt;Neil&lt;/a&gt;: A CLI to add common aliases and features to deps.edn-based projects&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/neil/issues/261&quot;&gt;#261&lt;/a&gt;: &lt;code&gt;neil dep upgrade&lt;/code&gt; now upgrades unstable deps (e.g. release candidates) to a newer unstable version when no newer stable version exists&lt;/li&gt;&lt;li&gt;Added a README note on &lt;code&gt;brew trust&lt;/code&gt; for users who installed neil before Homebrew introduced tap trust&lt;/li&gt;&lt;li&gt;The next neil version will make use of the new Babashka CLI features which is already prepared in a PR&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/babashka/nbb&quot;&gt;Nbb&lt;/a&gt;: Scripting in Clojure on Node.js using SCI&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/nbb/issues/410&quot;&gt;#410&lt;/a&gt;: fixed a regression, introduced by the async/await work in &lt;a href=&quot;https://github.com/babashka/nbb/issues/408&quot;&gt;#408&lt;/a&gt;, where a &lt;code&gt;defrecord&lt;/code&gt;/&lt;code&gt;deftype&lt;/code&gt; type symbol referenced through a namespace alias (e.g. &lt;code&gt;(instance? r/Foo x)&lt;/code&gt;) failed to resolve&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/borkdude/deps.clj&quot;&gt;deps.clj&lt;/a&gt;: a faithful port of the clojure CLI bash script to Clojure&lt;/p&gt;&lt;ul&gt;&lt;li&gt;As always, catching up with the most recent Clojure CLI versions&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/babashka/pod-babashka-gozxing&quot;&gt;Pod-babashka-gozxing&lt;/a&gt;: a babashka pod for QR code and barcode decoding/encoding, backed by &lt;a href=&quot;https://github.com/makiuchi-d/gozxing&quot;&gt;gozxing&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Initial release 0.0.1, installable via the pod registry&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/clj-easy/graal-build-time&quot;&gt;Graal-build-time&lt;/a&gt;: initialize Clojure classes at build time for GraalVM native-image&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-easy/graal-build-time/pull/55&quot;&gt;#55&lt;/a&gt;: munge package names for namespaces with special characters&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Contributions to third party projects:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clojure/clojurescript&quot;&gt;ClojureScript&lt;/a&gt;: documented the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; support from last cycle on the ClojureScript site, including an enhanced reference (&lt;a href=&quot;https://github.com/clojure/clojurescript-site/pull/423&quot;&gt;#423&lt;/a&gt;, &lt;a href=&quot;https://github.com/clojure/clojurescript-site/pull/424&quot;&gt;#424&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/cjohansen/nexus&quot;&gt;Nexus&lt;/a&gt;: a data-driven state management library by Christian Johansen. I ported the core engine and test suite to run under squint and added a cljs test runner alongside the existing kaocha setup, so both babashka and squint stay covered in CI (&lt;a href=&quot;https://github.com/cjohansen/nexus/pull/15&quot;&gt;#15&lt;/a&gt;, &lt;a href=&quot;https://github.com/cjohansen/nexus/pull/16&quot;&gt;#16&lt;/a&gt;, merged)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/cjohansen/replicant&quot;&gt;Replicant&lt;/a&gt;: a data-driven DOM rendering library by Christian Johansen. I made Replicant itself run under Squint (converting &lt;code&gt;dom.cljs&lt;/code&gt; to &lt;code&gt;.cljc&lt;/code&gt;, adjusting &lt;code&gt;core.cljc&lt;/code&gt; for portability), added babashka/squint test runners and wired them into CI, and fixed a multi-root render bug under squint by switching DOM state tracking to a node-map (&lt;a href=&quot;https://github.com/cjohansen/replicant/pull/71&quot;&gt;#71&lt;/a&gt;, &lt;a href=&quot;https://github.com/cjohansen/replicant/pull/72&quot;&gt;#72&lt;/a&gt;, merged)&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;other-projects&quot;&gt;Other projects&lt;/h2&gt;&lt;p&gt;These are (some of the) other projects I&amp;amp;aposm involved with but little to no activity happened in the past two months.&lt;/p&gt;&lt;details&gt;
Click for more details&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/scittle&quot;&gt;scittle&lt;/a&gt;: Execute Clojure(Script) directly from browser script tags via SCI&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/squint-cljs/cherry&quot;&gt;cherry&lt;/a&gt;: Experimental ClojureScript to ES6 module compiler&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/quickblog&quot;&gt;quickblog&lt;/a&gt;: light-weight static blog engine for Clojure and babashka&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/quickdoc&quot;&gt;quickdoc&lt;/a&gt;: Quick and minimal API doc generation for Clojure&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/nextjournal/clerk&quot;&gt;clerk&lt;/a&gt;: Moldable Live Programming for Clojure&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/pod-babashka-go-sqlite3&quot;&gt;pod-babashka-go-sqlite3&lt;/a&gt;: A babashka pod for interacting with sqlite3&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/unused-deps&quot;&gt;unused-deps&lt;/a&gt;: Find unused deps in a clojure project&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/pod-babashka-fswatcher&quot;&gt;pod-babashka-fswatcher&lt;/a&gt;: babashka filewatcher pod&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/sci.nrepl&quot;&gt;sci.nrepl&lt;/a&gt;: nREPL server for SCI projects that run in the browser&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/nrepl-client&quot;&gt;babashka.nrepl-client&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/http-server&quot;&gt;http-server&lt;/a&gt;: serve static assets&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/sci.configs&quot;&gt;sci.configs&lt;/a&gt;: A collection of ready to be used SCI configs.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/rewrite-edn&quot;&gt;rewrite-edn&lt;/a&gt;: Utility lib on top of rewrite-clj&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-commons/rewrite-clj&quot;&gt;rewrite-clj&lt;/a&gt;: Rewrite Clojure code and edn&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/tools-deps-native&quot;&gt;tools-deps-native&lt;/a&gt; and &lt;a href=&quot;https://github.com/babashka/tools.bbuild&quot;&gt;tools.bbuild&lt;/a&gt;: use tools.deps directly from babashka&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/bbin&quot;&gt;bbin&lt;/a&gt;: Install any Babashka script or project with one command&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/qualify-methods&quot;&gt;qualify-methods&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Initial release of experimental tool to rewrite instance calls to use fully qualified methods (Clojure 1.12 only)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/tools&quot;&gt;tools&lt;/a&gt;: a set of &lt;a href=&quot;https://github.com/babashka/bbin/&quot;&gt;bbin&lt;/a&gt; installable scripts&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/json&quot;&gt;babashka.json&lt;/a&gt;: babashka JSON library/adapter&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/speculative&quot;&gt;speculative&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/squint-cljs/squint-macros&quot;&gt;squint-macros&lt;/a&gt;: a couple of macros that stand-in for &lt;a href=&quot;https://github.com/applied-science/js-interop&quot;&gt;applied-science/js-interop&lt;/a&gt; and &lt;a href=&quot;https://github.com/funcool/promesa&quot;&gt;promesa&lt;/a&gt; to make CLJS projects compatible with squint and/or cherry.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/clj-kondo/lein-clj-kondo&quot;&gt;lein-clj-kondo&lt;/a&gt;: a leiningen plugin for clj-kondo&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/http-kit/http-kit&quot;&gt;http-kit&lt;/a&gt;: Simple, high-performance event-driven HTTP client+server for Clojure.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/jet&quot;&gt;jet&lt;/a&gt;: CLI to transform between JSON, EDN, YAML and Transit using Clojure&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/lein2deps&quot;&gt;lein2deps&lt;/a&gt;: leiningen to deps.edn converter&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/cljs-showcase&quot;&gt;cljs-showcase&lt;/a&gt;: Showcase CLJS libs using SCI&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/book&quot;&gt;babashka.book&lt;/a&gt;: Babashka manual&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/pod-babashka-buddy&quot;&gt;pod-babashka-buddy&lt;/a&gt;: A pod around buddy core (Cryptographic Api for Clojure).&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/gh-release-artifact&quot;&gt;gh-release-artifact&lt;/a&gt;: Upload artifacts to Github releases idempotently&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/carve&quot;&gt;carve&lt;/a&gt; - Remove unused Clojure vars&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/oxalorg/4ever-clojure&quot;&gt;4ever-clojure&lt;/a&gt; - Pure CLJS version of 4clojure, meant to run forever!&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/pod-babashka-lanterna&quot;&gt;pod-babashka-lanterna&lt;/a&gt;: Interact with clojure-lanterna from babashka&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/BetterThanTomorrow/joyride&quot;&gt;joyride&lt;/a&gt;: VSCode CLJS scripting and REPL (via &lt;a href=&quot;https://github.com/babashka/sci&quot;&gt;SCI&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://borkdude.github.io/clj2el/&quot;&gt;clj2el&lt;/a&gt;: transpile Clojure to elisp&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/deflet&quot;&gt;deflet&lt;/a&gt;: make let-expressions REPL-friendly!&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/deps.add-lib&quot;&gt;deps.add-lib&lt;/a&gt;: Clojure 1.12&amp;amp;aposs add-lib feature for leiningen and/or other environments without a specific version of the clojure CLI&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/babashka/process&quot;&gt;process&lt;/a&gt;: Clojure library for shelling out / spawning sub-processes&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/borkdude/parmezan&quot;&gt;parmezan&lt;/a&gt;: fixes unbalanced or unexpected parens or other delimiters in Clojure files&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;&lt;/div&gt;</content>
		<author>
			<name>Michiel Borkent</name>
			<uri>https://blog.michielborkent.nl/</uri>
		</author>
		<source>
			<title type="html">REPL adventures</title>
			<link rel="self" href="https://blog.michielborkent.nl/atom.xml"/>
			<id>https://blog.michielborkent.nl/</id>
		</source>
	</entry>

	<entry>
		<title type="html">Clojurists Together Update: May and June 2026</title>
		<link href="https://metaredux.com/posts/2026/07/05/clojurists-together-update-may-june-2026.html"/>
		<id>https://metaredux.com/posts/2026/07/05/clojurists-together-update-may-june-2026</id>
		<updated>2026-07-05T08:57:30+00:00</updated>
		<content type="html">&lt;p&gt;Some of you might know that &lt;a href=&quot;https://www.clojuriststogether.org/&quot;&gt;Clojurists Together&lt;/a&gt; are supporting my work on &lt;a href=&quot;https://nrepl.org&quot;&gt;nREPL&lt;/a&gt;, &lt;a href=&quot;https://cider.mx&quot;&gt;CIDER&lt;/a&gt; and friends
this year. Normally I send them a bi-monthly progress report, but I saw some other people who got
funding for their OSS work publish those reports as blog posts for the broader public and I thought
to try this for a change.&lt;/p&gt;

&lt;p&gt;The past two months were super productive. I had a lot of inspiration during this period and I managed
to tackle a lot of long-standing ideas and issues across the entire nREPL/CIDER ecosystem. Funnily enough,
I also managed to grow the ecosystem with a couple of brand new projects, but more about those later.&lt;/p&gt;

&lt;p&gt;The big highlights from my perspective:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;CIDER 1.22 is out&lt;/li&gt;
  &lt;li&gt;CIDER 2.0 is essentially ready and needs more user testing&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/sayid&quot;&gt;Sayid&lt;/a&gt; is reborn&lt;/li&gt;
  &lt;li&gt;Two brand new projects saw the light of day: &lt;a href=&quot;https://github.com/clojure-emacs/port&quot;&gt;port&lt;/a&gt; and &lt;a href=&quot;https://github.com/nrepl/neat&quot;&gt;neat&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/nrepl/piggieback&quot;&gt;Piggieback&lt;/a&gt; 0.7.0 is out (and &lt;a href=&quot;https://github.com/nrepl/weasel&quot;&gt;Weasel&lt;/a&gt; got modernized while I was in the area)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/clj-refactor.el&quot;&gt;clj-refactor&lt;/a&gt; and &lt;a href=&quot;https://github.com/clojure-emacs/refactor-nrepl&quot;&gt;refactor-nrepl&lt;/a&gt; got some love as well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below you’ll find more details about the work I did, project by project.&lt;/p&gt;

&lt;h2 id=&quot;cider&quot;&gt;CIDER&lt;/h2&gt;

&lt;p&gt;CIDER 1.22 (“São Miguel”) landed in mid-June, wrapping up the 1.x series. Its
main features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a registry for jack-in tools, so third parties can plug new build tools and
Clojure dialects into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cider-jack-in&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;a “default session” escape hatch from sesman’s project-based dispatch&lt;/li&gt;
  &lt;li&gt;keyword-argument versions of the low-level request APIs, alongside a proper
decoupling of the nREPL client layer from CIDER’s UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also fixed a long list of small annoyances: severe editor lag in unlinked
buffers, several TRAMP and SSH tunnel problems, request id leaks, and a bunch
of broken menu entries.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/cider/releases/tag/v1.22.0&quot;&gt;CIDER 1.22.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/cider/releases/tag/v1.22.2&quot;&gt;CIDER 1.22.2&lt;/a&gt; (plus a quick 1.22.1 fixing a docs site problem)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right after that I switched the development version to 2.0 and most of the
planned work is already done. The headline items so far:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.cider.mx/cider/usage/cider_mode.html#command-menus&quot;&gt;transient menus&lt;/a&gt; for all the command groups (plus new menus for the debugger
and the inspector)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.cider.mx/cider/debugging/macroexpansion.html#inline&quot;&gt;inline stepwise macroexpansion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;SLIME-style &lt;a href=&quot;https://docs.cider.mx/cider/usage/misc_features.html#browsing-the-call-graph&quot;&gt;call graph browsers&lt;/a&gt; (who-calls, who-implements and friends)&lt;/li&gt;
  &lt;li&gt;source-based &lt;a href=&quot;https://docs.cider.mx/cider/usage/misc_features.html#find-references&quot;&gt;find-references&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tap&amp;gt;&lt;/code&gt; buffer and a dedicated &lt;a href=&quot;https://docs.cider.mx/cider/debugging/tracing.html&quot;&gt;trace buffer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;namespace &lt;a href=&quot;https://docs.cider.mx/cider/usage/code_evaluation.html#load-state-indicators&quot;&gt;load-state indicators&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;the revival of &lt;a href=&quot;https://docs.cider.mx/cider/repl/configuration.html#displaying-rich-content-in-the-repl&quot;&gt;rich content in the REPL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one deserves a special mention: evaluation
results that are images now &lt;a href=&quot;https://docs.cider.mx/cider/usage/code_evaluation.html#rich-results&quot;&gt;render inline out of the box&lt;/a&gt;, and file/URL results
offer their content on demand, six years after the feature had to be disabled
over its safety problems. There was also a big cleanup pass: consolidated
configuration options, the REPL history browser renamed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cider-history&lt;/code&gt; to
end a long-standing naming clash, theme-aware faces instead of hardcoded colors,
refreshed docs and a regenerated refcard. CIDER 2.0 is available from MELPA
snapshots and I’d love for more people to take it for a spin before the final
release.&lt;/p&gt;

&lt;h2 id=&quot;cider-nrepl&quot;&gt;cider-nrepl&lt;/h2&gt;

&lt;p&gt;Lots of &lt;a href=&quot;https://github.com/clojure-emacs/cider-nrepl&quot;&gt;cider-nrepl&lt;/a&gt; releases, driving the CIDER work above:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/cider-nrepl/releases/tag/v0.60.0&quot;&gt;cider-nrepl 0.60.0&lt;/a&gt; added the ops backing the new protocol exploration commands (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cider/who-implements&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cider/type-protocols&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cider/protocols-with-method&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/cider-nrepl/releases/tag/v0.61.0&quot;&gt;cider-nrepl 0.61.0&lt;/a&gt; brought ClojureScript test support, a ClojureScript macroexpansion fix, formatting that honors the project’s cljfmt configuration, and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pprint&lt;/code&gt; backed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orchard.pp&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/cider-nrepl/releases/tag/v0.62.0-alpha1&quot;&gt;cider-nrepl 0.62.0-alpha1&lt;/a&gt; and &lt;a href=&quot;https://github.com/clojure-emacs/cider-nrepl/releases/tag/v0.62.0-alpha2&quot;&gt;0.62.0-alpha2&lt;/a&gt; hardened the content-type and slurp middleware (URL scheme allowlist, size caps, graceful fetch errors) and cleaned up the response protocol, which is what made it safe to turn rich content on by default in CIDER 2.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Along the way the project’s build was migrated from Leiningen to tools.deps, which required a new &lt;a href=&quot;https://github.com/benedekfazekas/mranderson&quot;&gt;MrAnderson&lt;/a&gt; release (see the blog posts below).&lt;/p&gt;

&lt;h2 id=&quot;orchard&quot;&gt;Orchard&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/clojure-emacs/orchard&quot;&gt;Orchard&lt;/a&gt;, the library that powers much of cider-nrepl’s functionality, kept pace:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/orchard/releases/tag/v0.42.0&quot;&gt;Orchard 0.42.0&lt;/a&gt; and &lt;a href=&quot;https://github.com/clojure-emacs/orchard/releases/tag/v0.43.0&quot;&gt;Orchard 0.43.0&lt;/a&gt; continued the inspector polish, added symbol classification to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orchard.meta&lt;/code&gt;, a programmatic listener API for the tracer, and protocol/multimethod introspection in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orchard.xref&lt;/code&gt;. The project also moved to tools.deps and its CI now covers JDK 26.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;sayid&quot;&gt;Sayid&lt;/h2&gt;

&lt;p&gt;Sayid, the omniscient Clojure debugger, had been dormant for years and I finally gave it the revival it deserved:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/sayid/releases/tag/v0.2.0&quot;&gt;Sayid 0.2.0&lt;/a&gt; was the big modernization pass: new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mx.cider/sayid&lt;/code&gt; coordinates, a documented nREPL middleware API, a consolidated op surface (37 ops down to 26) and fixes for the most annoying Emacs client breakages.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/sayid/releases/tag/v0.3.0&quot;&gt;Sayid 0.3.0&lt;/a&gt; followed with usability work: no more frozen Emacs during the reload workflow, simpler query commands and help buffers generated from the keymaps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;port&quot;&gt;port&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/clojure-emacs/port&quot;&gt;port&lt;/a&gt; is a brand new project I started in May: a minimalist Clojure interactive programming environment for Emacs, built on prepl instead of nREPL. It went from nothing to three releases in the course of the month:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/port/releases/tag/v0.1.0&quot;&gt;port 0.1.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/port/releases/tag/v0.2.0&quot;&gt;port 0.2.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/clojure-emacs/port/releases/tag/v0.3.0&quot;&gt;port 0.3.0&lt;/a&gt;, which added eldoc with active argument highlighting, a wire-level message log for debugging and a roughly 10x speedup in handling large prepl responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t have any particular plans for the future of this project - it was just something that I wanted to experiment with for a while.
I see it as an interesting option for people looking for some middle ground between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inf-clojure&lt;/code&gt; and CIDER.&lt;/p&gt;

&lt;h2 id=&quot;neat&quot;&gt;neat&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/nrepl/neat&quot;&gt;neat&lt;/a&gt; is the other new arrival: a small, language-agnostic nREPL client for Emacs. &lt;a href=&quot;https://github.com/nrepl/neat/releases/tag/v0.1.0&quot;&gt;neat 0.1.0&lt;/a&gt; has the essentials in place: a pure-elisp bencode codec, a comint-based REPL, and a source-buffer minor mode with eval, completion, eldoc, xref and doc lookup, tested against Clojure, Babashka and Basilisp. It’s early days, but it’s a nice testbed for exercising the nREPL protocol outside CIDER.&lt;/p&gt;

&lt;p&gt;This project also means I’ve dropped any plans to try to make CIDER a language-agnostic development environment. Going forward CIDER will focus only on
Clojure-like languages, and everything else will be covered by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;neat&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;piggieback-and-weasel&quot;&gt;Piggieback and Weasel&lt;/h2&gt;

&lt;p&gt;The nREPL org saw some ClojureScript-flavored action:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/nrepl/piggieback/releases/tag/0.6.2&quot;&gt;Piggieback 0.6.2&lt;/a&gt; and &lt;a href=&quot;https://github.com/nrepl/piggieback/releases/tag/0.7.0&quot;&gt;Piggieback 0.7.0&lt;/a&gt;. The 0.7.0 release makes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load-file&lt;/code&gt; evaluate the editor’s buffer contents instead of re-reading from disk, tears down ClojureScript REPLs when their sessions close (no more leaked Node processes) and surfaces ClojureScript status in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;describe&lt;/code&gt; response.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/nrepl/weasel/releases/tag/0.8.0&quot;&gt;Weasel 0.8.0&lt;/a&gt; modernized the WebSocket REPL: the client now uses the platform’s native &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WebSocket&lt;/code&gt;, so it runs in any modern JavaScript runtime (browsers, Node 22+, Deno, Bun, workers), and the minimum requirements moved to Clojure/ClojureScript 1.12.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also backfilled proper GitHub releases for the historic tags of both projects, so their release history is finally browsable.&lt;/p&gt;

&lt;p&gt;Improving the ClojureScript support in CIDER has long been a major objective for me, and these small changes were some initial steps in that direction.&lt;/p&gt;

&lt;h2 id=&quot;refactor-nrepl-and-clj-refactor&quot;&gt;refactor-nrepl and clj-refactor&lt;/h2&gt;

&lt;p&gt;refactor-nrepl got three releases: &lt;a href=&quot;https://github.com/clojure-emacs/refactor-nrepl/releases/tag/v3.12.0&quot;&gt;3.12.0&lt;/a&gt;, &lt;a href=&quot;https://github.com/clojure-emacs/refactor-nrepl/releases/tag/v3.13.0&quot;&gt;3.13.0&lt;/a&gt; and &lt;a href=&quot;https://github.com/clojure-emacs/refactor-nrepl/releases/tag/v3.14.0&quot;&gt;3.14.0&lt;/a&gt;, the last one making the AST-based indexing much faster and more reliable. clj-refactor.el received a round of maintenance on master as well, and will get a new release after I wrap up the work on CIDER 2.0.&lt;/p&gt;

&lt;p&gt;I’m still pondering the future of both projects, as I plan to move the most useful refactor-nrepl features (those that don’t carry a lot of complexity) to CIDER
and cider-nrepl eventually, and I’m not sure that the flagship AST-powered refactorings are very competitive these days (compared to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clojure-lsp&lt;/code&gt; and static project-wide analysis a la &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clj-kondo&lt;/code&gt; in general).&lt;/p&gt;

&lt;p&gt;I’ll write a bit more about this and I’d certainly appreciate more feedback from the users of clj-refactor on the subject. It’s funny that
I’ve been maintaining the project for ages, but I’ve never really used it (mostly due to its brittleness in the past). I think I managed to address
some of the biggest problems recently, but perhaps this happened too late and the project has lost its relevance by now.&lt;/p&gt;

&lt;h2 id=&quot;blog-posts&quot;&gt;Blog posts&lt;/h2&gt;

&lt;p&gt;I wrote a few articles related to the work above:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://batsov.com/articles/2026/05/12/port-a-minimalist-prepl-client-for-emacs/&quot;&gt;Port: a minimalist prepl client for Emacs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://batsov.com/articles/2026/05/20/neat-a-language-agnostic-nrepl-client-for-emacs/&quot;&gt;neat: a language-agnostic nREPL client for Emacs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://batsov.com/articles/2026/05/20/nrepl-forever/&quot;&gt;nREPL Forever&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://metaredux.com/posts/2026/06/16/cider-1-22.html&quot;&gt;CIDER 1.22 (“São Miguel”)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://metaredux.com/posts/2026/06/22/mranderson-0-6.html&quot;&gt;MrAnderson 0.6&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://metaredux.com/posts/2026/06/30/cider-2-0-is-brewing.html&quot;&gt;CIDER 2.0 is Brewing…&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://metaredux.com/posts/2026/07/01/demystifying-piggieback.html&quot;&gt;nREPL and ClojureScript: Demystifying Piggieback&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://metaredux.com/posts/2026/07/01/sayid-redux.html&quot;&gt;Sayid Redux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;Big thanks to Clojurists Together, Nubank and the other organizations and people supporting my Clojure OSS work! I love you and none of this would have happened without you.
Sadly, the amount of financial support my projects receive has eroded massively over the past 4 years and I’ve kind of lost hope that this negative trend will eventually be reversed. It was never easy to maintain many popular OSS projects, but the job certainly hasn’t got any easier or more rewarding in recent years…&lt;/p&gt;

&lt;p&gt;Overall, a super productive two months. Hopefully the next two are going to be just as productive, although I have to admit I’ve plucked most of the low-hanging fruit already. Then again, I’ve said this many times in the past, so one never knows…&lt;/p&gt;</content>
		<author>
			<name>Bozhidar Batsov</name>
			<uri>https://metaredux.com/</uri>
		</author>
		<source>
			<title type="html">Meta Redux</title>
			<subtitle type="html">Writings on programming and open-source by Bozhidar Batsov, creator of RuboCop, CIDER, and Prelude.</subtitle>
			<link rel="self" href="https://metaredux.com/feed.xml"/>
			<id>https://metaredux.com/feed.xml</id>
		</source>
	</entry>

	<entry>
		<title type="html" xml:lang="en-gb">Clojure 1.13.0-alpha2</title>
		<link href="https://clojure.org/news/2026/07/02/clojure-1-13-alpha2"/>
		<id>https://clojure.org/news/2026/07/02/clojure-1-13-alpha2</id>
		<updated>2026-07-02T00:00:00+00:00</updated>
		<content type="html">&lt;div id=&quot;preamble&quot;&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Clojure 1.13.0-alpha2 is now available! Find download and usage information on the &lt;a href=&quot;https://clojure.org/../../../../releases/downloads&quot;&gt;Downloads&lt;/a&gt; page.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;ulist&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Fixes a build problem from new Maven plugins by reverting the change&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
&lt;h2 id=&quot;_try_it_out&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;https://clojure.org/feed.xml#_try_it_out&quot;&gt;&lt;/a&gt;Try it out&lt;/h2&gt;
&lt;div class=&quot;sectionbody&quot;&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Update your deps.edn :deps with:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;org.clojure/clojure {:mvn/version &quot;1.13.0-alpha2&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
&lt;p&gt;Start a REPL with the Clojure CLI (any version) with:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;listingblock&quot;&gt;
&lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;prettyprint highlight&quot;&gt;&lt;code&gt;clj -Sdeps '{:deps {org.clojure/clojure {:mvn/version &quot;1.13.0-alpha2&quot;}}}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</content>
		<author>
			<name>Clojure.org</name>
			<uri>https://clojure.org</uri>
		</author>
		<source>
			<title type="html">Clojure News</title>
			<subtitle type="html">Clojure News</subtitle>
			<link rel="self" href="https://clojure.org/feed.xml"/>
			<id>https://clojure.org</id>
		</source>
	</entry>

	<entry>
		<title type="html">Sayid Redux</title>
		<link href="https://metaredux.com/posts/2026/07/01/sayid-redux.html"/>
		<id>https://metaredux.com/posts/2026/07/01/sayid-redux</id>
		<updated>2026-07-01T13:01:00+00:00</updated>
		<content type="html">&lt;p&gt;I have a soft spot for &lt;a href=&quot;https://github.com/clojure-emacs/sayid&quot;&gt;Sayid&lt;/a&gt; - it’s one of the most ingenious Clojure tools
ever built, and also one of the most neglected. It’s an &lt;em&gt;omniscient debugger&lt;/em&gt;:
instead of stopping your program at a breakpoint, it quietly records every call
to the functions you’ve traced and lets you rummage through the recording
afterwards. It’s the kind of thing you demo to people and watch their jaw drop.
And it had been sitting practically unmaintained for the better part of a decade.&lt;/p&gt;

&lt;p&gt;Here’s the awkward part: that neglect is largely on me. &lt;a href=&quot;https://github.com/bpiel&quot;&gt;Bill Piel&lt;/a&gt;,
Sayid’s original author, handed me the keys ages ago, and I’ve been… let’s say
a less than exemplary steward. I’d merge the occasional patch to keep the lights
on, but until very recently I’d done precious little to actually move the project
forward.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The best time to maintain your open-source project was six years ago. The
second best time is now.&lt;/p&gt;

  &lt;p&gt;– Ancient proverb, lightly adapted&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So what finally lit a fire under me? Blame &lt;a href=&quot;https://metaredux.com/posts/2026/06/30/cider-2-0-is-brewing.html&quot;&gt;CIDER 2.0&lt;/a&gt;. I’ve been
reworking CIDER’s debugging and tracing story, and at some point I sat down to
make the built-in tracer smarter. A few hours in it hit me: nothing I could
realistically bolt onto the built-in tracing would come anywhere close to what
Sayid already does. So instead of building a worse Sayid, I dusted off the real
one, gave it a good scrub, and here we are - &lt;a href=&quot;https://github.com/clojure-emacs/sayid/blob/master/CHANGELOG.md&quot;&gt;Sayid 0.4&lt;/a&gt;.&lt;/p&gt;



&lt;h2 id=&quot;what-is-sayid-anyway&quot;&gt;What is Sayid, anyway?&lt;/h2&gt;

&lt;p&gt;For the uninitiated: Sayid records the arguments, return value, timing and full
call tree of the functions you trace, so you can go back and inspect exactly what
happened - no breakpoints, no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;println&lt;/code&gt;, no re-running the thing five times.&lt;/p&gt;

&lt;p&gt;While we’re on the subject of embarrassing confessions: I’ve been the maintainer
of this thing for years and I &lt;em&gt;still&lt;/em&gt; have no idea what “Sayid” actually means or
what it’s a reference to. If you happen to know, please, put me out of my
misery - I’d love to finally get the joke.&lt;/p&gt;

&lt;p&gt;Trace a namespace, run your code, and pop open the workspace with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C-c s w&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;▾ (demo.coins/can-afford? [:quarter :dime :nickel :penny] 45) =&amp;gt; true
    (demo.coins/total-cents [:quarter :dime :nickel :penny]) =&amp;gt; 45
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;can-afford?&lt;/code&gt; says &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;, but four coins worth 41 cents shouldn’t cover a
45-cent tab. Something’s off, and the bug lives inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;total-cents&lt;/code&gt;. This is
where Sayid really shines - flip on an &lt;em&gt;inner&lt;/em&gt; trace and it records every
expression &lt;em&gt;inside&lt;/em&gt; the function:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;▾ (demo.coins/can-afford? [:quarter :dime :nickel :penny] 45) =&amp;gt; true
  ▾ (demo.coins/total-cents [:quarter :dime :nickel :penny]) =&amp;gt; 45
    ▾ (apply + (map coin-values coins)) =&amp;gt; 45
        (map coin-values coins) =&amp;gt; (25 10 5 5)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There it is, staring right at us: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(25 10 5 5)&lt;/code&gt;, when the last value should be
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;. A penny is worth five cents in our coin map. We never wrote a single
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;println&lt;/code&gt;, and we never had to guess where to put a breakpoint - we just looked
at what actually happened.&lt;/p&gt;

&lt;p&gt;That’s the pitch, and it’s a great one. So why was such a cool tool gathering
dust?&lt;/p&gt;

&lt;h2 id=&quot;why-an-omniscient-debugger-in-2026&quot;&gt;Why an omniscient debugger, in 2026?&lt;/h2&gt;

&lt;p&gt;Honestly, part of it is that Clojure folks are spoiled. We have the REPL, so the
reflex for most of us is to just re-run a form with some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tap&amp;gt;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;println&lt;/code&gt;
sprinkled in and eyeball the output. That works right up until it doesn’t -
until the bug is three layers deep in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; over a lazy seq, or only shows up
on the 400th call, or lives in code you didn’t write and don’t feel like
instrumenting by hand.&lt;/p&gt;

&lt;p&gt;A traditional stepping debugger stops the world and makes you drive. Sayid does
the opposite: it lets the program run to completion and then hands you the entire
execution history as something you can navigate at your own pace.
&lt;a href=&quot;https://github.com/clojure/tools.trace&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tools.trace&lt;/code&gt;&lt;/a&gt; is in the same spirit, but it dumps text to
stdout - Sayid keeps &lt;em&gt;structured data&lt;/em&gt; and gives you a query DSL to slice it. That’s a much better fit for how we
actually work in Clojure: run it, capture everything, explore the data.&lt;/p&gt;

&lt;p&gt;And here’s the thing that made me want to revive it rather than reinvent it -
“capture everything as data and explore it later” is exactly the workflow that’s
becoming &lt;em&gt;more&lt;/em&gt; relevant, not less. Structured execution traces are gold, whether
the thing doing the exploring is you, a data-inspection tool like &lt;a href=&quot;https://github.com/djblue/portal&quot;&gt;Portal&lt;/a&gt;, or
an AI assistant trying to understand why your code misbehaved.&lt;/p&gt;

&lt;h2 id=&quot;out-with-the-old-coordinates&quot;&gt;Out with the old coordinates&lt;/h2&gt;

&lt;p&gt;First order of business was dragging the project into the present.&lt;/p&gt;

&lt;p&gt;Sayid used to live under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.billpiel/sayid&lt;/code&gt; on Clojars, with namespaces like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.billpiel.sayid.core&lt;/code&gt;. The new home is &lt;a href=&quot;https://github.com/clojure-emacs&quot;&gt;clojure-emacs&lt;/a&gt;, so the artifact is
now published as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mx.cider/sayid&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:plugins&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mx.cider/sayid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.4.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also dropped the personal-domain prefix from every namespace - it’s plain
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sayid.core&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sayid.trace&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sayid.nrepl-middleware&lt;/code&gt; and so on now. The old
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.billpiel/sayid&lt;/code&gt; coordinates still get the same releases for the time being,
so nobody’s dependency breaks overnight, but the future is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mx.cider&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;data-first-strings-later&quot;&gt;Data first, strings later&lt;/h2&gt;

&lt;p&gt;Here’s the change I’m most excited about. Sayid’s nREPL middleware used to hand
the Emacs client a pre-rendered blob of text plus a pile of text properties for
colouring. In other words, the server did all the rendering and the client was a
dumb terminal. That single decision is a big part of why there was exactly &lt;em&gt;one&lt;/em&gt;
Sayid client.&lt;/p&gt;

&lt;p&gt;So the middleware now speaks data. There’s a family of new ops -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sayid-get-workspace-data&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sayid-query-data&lt;/code&gt; and friends - that return the
recorded call tree as honest, navigable data instead of a wall of text:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4793&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;demo.coins/can-afford?&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[:quarter :dime :nickel :penny]&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;45&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;return&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;demo/coins.clj&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;children&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The structural bits (ids, names, timings, source location, the tree shape) come
across as real nested maps and lists you can walk by key. The captured values are
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pr-str&lt;/code&gt;‘d, since an arbitrary Clojure value can’t always round-trip over the
wire - but that’s the only place strings sneak in, and it’s exactly where you’d
expect them.&lt;/p&gt;

&lt;p&gt;The upshot: any editor or tool that speaks nREPL can now fetch a workspace and
render it however it likes, and a REPL one-liner or a Portal tap gets you the
same data with zero Sayid-specific machinery. The whole thing is written up in
&lt;a href=&quot;https://github.com/clojure-emacs/sayid/blob/master/doc/nrepl-api.md&quot;&gt;doc/nrepl-api.md&lt;/a&gt;, so you don’t have to reverse-engineer the wire
format from the Emacs client the way you would have before.&lt;/p&gt;

&lt;h2 id=&quot;a-ui-that-doesnt-feel-like-2015&quot;&gt;A UI that doesn’t feel like 2015&lt;/h2&gt;

&lt;p&gt;With the data ops in place, I rebuilt the Emacs UI on top of them. The workspace
and the “what’s traced” views are now proper foldable trees, built on &lt;a href=&quot;https://www.cider.mx&quot;&gt;CIDER&lt;/a&gt;’s
new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cider-tree-view&lt;/code&gt;. You get real folding (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TAB&lt;/code&gt;), navigation (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt;),
jump-to-source (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RET&lt;/code&gt;), and - my favourite - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c i&lt;/code&gt; hands the actual captured
value straight to CIDER’s inspector, so you can drill into an argument or a
return value as a live, navigable object rather than squinting at its printed
form.&lt;/p&gt;

&lt;p&gt;There’s also a query layer wired into the tree: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt; narrows to every recorded
call of the function at point, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; focuses a single call and its subtree. On a
big trace that’s the difference between “wall of text” and “actually finding the
thing”.&lt;/p&gt;

&lt;p&gt;The best part is that the client is now &lt;em&gt;smaller&lt;/em&gt;, not bigger - all the tree
rendering, folding and value inspection are handled by mature components instead
of bespoke code painting text properties by hand. That’s the payoff of moving
rendering to the client: the server ships data, and the client is free to be as
fancy or as plain as it wants.&lt;/p&gt;

&lt;h2 id=&quot;wait-you-broke-everything&quot;&gt;Wait, you broke everything?&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Backwards compatibility is a promise you make to the users you have. Sayid had
exactly one, and reader, I &lt;em&gt;am&lt;/em&gt; that user.&lt;/p&gt;

  &lt;p&gt;– Me, rationalising&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yeah, I did. I went pretty wild with the breaking changes this time around - new
artifact coordinates, new namespaces, a reworked nREPL API, a bumped minimum
CIDER version. Normally I’d bend over backwards to keep old clients working, but
here I made a deliberate call: as far as I can tell, the bundled Emacs client was
the &lt;em&gt;only&lt;/em&gt; client Sayid ever had. Dancing around imaginary third parties to
preserve compatibility nobody was relying on would have just made the project
harder to adopt and harder to maintain.&lt;/p&gt;

&lt;p&gt;So I opted for sweeping changes that leave Sayid in a much better place to build
on, rather than a museum of backwards-compatible cruft. If it turns out I was
wrong and you were quietly depending on the old coordinates - I’m sorry, and do
let me know, because that’s genuinely useful information.&lt;/p&gt;

&lt;h2 id=&quot;take-it-for-a-spin&quot;&gt;Take it for a spin&lt;/h2&gt;

&lt;p&gt;That’s the gist of it. Sayid is alive again, it’s leaner, it speaks data, and it
has a UI I’m not embarrassed to demo. What I’d love now is for more people to
actually &lt;em&gt;use&lt;/em&gt; it and tell me whether the new direction resonates.&lt;/p&gt;

&lt;p&gt;So please - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[mx.cider/sayid &quot;0.4.0&quot;]&lt;/code&gt;, trace something gnarly, and pop open the
tree. Then head over to the &lt;a href=&quot;https://github.com/clojure-emacs/sayid/issues&quot;&gt;issue tracker&lt;/a&gt; and tell me what you think:
what feels great, what feels rough, what’s missing. I have ideas for where to
take it next (bounding the recording so you can safely trace a whole namespace
under a test suite is high on the list), but I’d rather steer by what people
actually want out of it.&lt;/p&gt;

&lt;p&gt;Big thanks to Bill Piel for building such a wonderful tool in the first place -
I’m merely standing on the shoulders of a giant here. And thanks in advance to
everyone willing to kick the tyres on the revival!&lt;/p&gt;

&lt;p&gt;That’s all from me for now. Keep hacking!&lt;/p&gt;

&lt;h2 id=&quot;ps-one-more-thing&quot;&gt;P.S. One more thing…&lt;/h2&gt;

&lt;p&gt;Well, that didn’t take long. Remember that “high on the list” bit a few
paragraphs up - bounding the recording so you can safely trace a whole namespace
under a test suite? Turns out I couldn’t leave it alone. A short burst of small
improvements after this post went up, and it’s done: &lt;a href=&quot;https://github.com/clojure-emacs/sayid/blob/master/CHANGELOG.md&quot;&gt;Sayid 0.5&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That was &lt;em&gt;the&lt;/em&gt; thing that made Sayid feel like a toy - point it at a real
workload and it would cheerfully eat your whole heap and fall over. The recording
now has a set of tunable bounds instead: a cap on how many top-level calls it
keeps, a depth limit, 1-in-N sampling for hot paths, a per-function cap, and a
keep-only-the-last-N mode for when what you care about is whatever happened right
before things went sideways. Fat and infinite values no longer hang the data ops
either, and the traced-functions view got its enable/disable/remove actions back.&lt;/p&gt;

&lt;p&gt;The upshot for you: you can finally point Sayid at real code under real load
without babysitting it. Same ask as before - give it a spin and tell me how it
feels.&lt;/p&gt;</content>
		<author>
			<name>Bozhidar Batsov</name>
			<uri>https://metaredux.com/</uri>
		</author>
		<source>
			<title type="html">Meta Redux</title>
			<subtitle type="html">Writings on programming and open-source by Bozhidar Batsov, creator of RuboCop, CIDER, and Prelude.</subtitle>
			<link rel="self" href="https://metaredux.com/feed.xml"/>
			<id>https://metaredux.com/feed.xml</id>
		</source>
	</entry>

	<entry>
		<title type="html" xml:lang="en">Compilers, Editors, Typefaces and More From the Ground Up</title>
		<link href="https://luisthiamnye.substack.com/p/compilers-editors-typefaces-and-more"/>
		<id>https://luisthiamnye.substack.com/p/compilers-editors-typefaces-and-more</id>
		<updated>2026-06-30T20:59:12+00:00</updated>
		<content type="html" xml:lang="en">&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;figure&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!nOBZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff49ec220-034d-4d8e-888f-48ae56eb8dd5_1971x1359.png&quot; target=&quot;_blank&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;source type=&quot;image/webp&quot; /&gt;&lt;img alt=&quot;&quot; class=&quot;sizing-normal&quot; height=&quot;1004&quot; src=&quot;https://substackcdn.com/image/fetch/$s_!nOBZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff49ec220-034d-4d8e-888f-48ae56eb8dd5_1971x1359.png&quot; width=&quot;1456&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot; tabindex=&quot;0&quot; type=&quot;button&quot;&gt;&lt;svg fill=&quot;none&quot; height=&quot;20&quot; stroke=&quot;var(--color-fg-primary)&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;1.5&quot; viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;g&gt;&lt;title&gt;&lt;/title&gt;&lt;path d=&quot;M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/button&gt;&lt;button class=&quot;pencraft pc-reset pencraft icon-container view-image&quot; tabindex=&quot;0&quot; type=&quot;button&quot;&gt;&lt;svg class=&quot;lucide lucide-maximize2 lucide-maximize-2&quot; fill=&quot;none&quot; height=&quot;20&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt;&lt;polyline points=&quot;9 21 3 21 3 15&quot;&gt;&lt;/polyline&gt;&lt;line x1=&quot;21&quot; x2=&quot;14&quot; y1=&quot;3&quot; y2=&quot;10&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;3&quot; x2=&quot;10&quot; y1=&quot;21&quot; y2=&quot;14&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;In the four years since my previous progress-update article, I have been working on a series of experimental projects and learning about the low-level fundamentals of software—down to the finicky particulars of things like text rendering and x86 machine code generation.&lt;/p&gt;&lt;p&gt;Here is a brief, incomplete listing of these projects:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;(Late 2022) Structural editor for Clojure, with code stored non-hierarchically in a database. (Early prototyping phase.)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(First half of 2023) Compiler, structural editor and debugger for a custom language with a bytecode interpreter and x64 machine code backend. The language is imperative, strongly typed and assumes manual memory management, but has a Clojure-like syntax.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(Mid 2023) &lt;a href=&quot;https://github.com/LuisThiamNye/ButteryTaskbar2&quot;&gt;ButteryTaskbar2&lt;/a&gt;: a reimplementation of my original utility for Windows, improved to be simpler and more efficient.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Basic GPU-powered 2D rendering via Direct3D 12.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;OLKCH colour picker.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(Second half of 2023) Two different attempts at a structural editor for the Jai programming language.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A simple alternative language to Markdown.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A tool for viewing and editing notes using a graph-based structure similar to Roam Research. Notes are written in my Markdown alternative.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Static website generator using my Markdown alternative for webpage content.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A programmatic generator for my six-segment logo, using &lt;a href=&quot;https://tinyvg.tech/&quot;&gt;TinyVG&lt;/a&gt; for rendering.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;XML parser.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Implementation of Myer’s and Dijkstra’s algorithms for diffing a source code AST, together with a fun (but vitally useful) visualisation of the diffing algorithm.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(First half of 2024) A text editor for code, featuring syntax highlighting, &lt;a href=&quot;https://tree-sitter.github.io/tree-sitter/&quot;&gt;Tree-sitter&lt;/a&gt; integration, adjustable panels, Regular Expression text search, modal editing, macros and undo history.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://libs.suckless.org/libgrapheme/&quot;&gt;libgrapheme&lt;/a&gt; ported to Jai.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(Second half of 2024) Compiler and bytecode interpreter for a custom modern C-like language.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(Late 2024) A simple bespoke tool for tracking deadlines and reminders. Uses SQLite and a minimal browser-based UI. I continue to use this tool to this day.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Win32 API bindings generator, which converts the JSON data from &lt;a href=&quot;https://github.com/marlersoft/win32json&quot;&gt;win32json&lt;/a&gt; into a serialised binary form, which is then selectively converted into code declarations.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(2025 onwards) A blank-slate code repository with a bespoke build system and custom implementations of core libraries for purposes like memory management, file system access, hash tables, etc. This is the basis of all projects that follow.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(Early 2025) A utility to provide various comforts on Windows, including navigation key bindings, synchronised display brightness control, and sticky window edges (like macOS).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A more ergonomic and flexible alternative to JSON.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Basic SIMD-accelerated software renderer written in assembly for AVX2.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(Mid 2025) Initial prototyping work on a new design of a structural code editor, inspired by &lt;a href=&quot;https://onatto.github.io/lang.html&quot;&gt;the Kyra language and editor&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Glyph rasteriser (CPU-based), capable of directly rasterising both linear and quadratic edges. Inspired by &lt;a href=&quot;https://nothings.org/gamedev/rasterize&quot;&gt;the approach in stb_truetype&lt;/a&gt;, but designed to be SIMD-friendly.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A new programmatically-defined quasi-proportionally-spaced typeface designed to be both legible and fast to render, by minimising vertices and preferring quadratic Bézier curves over cubics. A simple bespoke text layout system replaces the dependence on Harfbuzz.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(Late 2025) Another attempt at a UI toolkit, which includes a more powerful layout system, better support for incremental updates, and dependency analysis to optimise GPU draw calls.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Vulkan bindings generator, which sources data from &lt;a href=&quot;https://registry.khronos.org/vulkan/specs/latest/registry.html&quot;&gt;the official XML-based specification&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;(2026) A revised text editor, bringing together my latest work on UI systems and text rendering. It is powerful enough that I now use it as my primary code editor for my Jai-based projects. Like &lt;a href=&quot;https://4coder.net/&quot;&gt;4coder&lt;/a&gt;, it implements virtual whitespace, which greatly improves the experience of working with code indentation.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;All of these projects constitute an extensive volume of research and time spent in deep thought. This article does not aim to give exhaustive coverage of all the ideas and implementation details across these four years, but merely to serve as an overview of some of the most interesting points.&lt;/p&gt;&lt;h1&gt;Reinventing Software Development Tooling&lt;/h1&gt;&lt;p&gt;Recurring themes in my work include compilers, innovative code editors, and novel programming language design. I feel like there is opportunity to bring substantial improvements to the development experience by reducing superfluous sources of friction, cutting out bureaucratic busywork, and tightening feedback loops. My main points of focus have been:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Structural code editors to give a more refined editing experience with more powerful and robust code intelligence and transformations.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Rethinking code organisation to make it easier to scale, manage, and adapt complex codebases. This means storing code declarations in some kind of graph database instead of a tree of files.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A more interactive debugging experience leveraging tightly integrated code introspection along with dynamic code compilation and execution.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;My research in this area is still early and highly experimental, though I now have a better idea of what probably doesn’t work, and what could work.&lt;/p&gt;&lt;p&gt;Whilst searching for prior art, I encountered &lt;a href=&quot;https://vimeo.com/485177664&quot;&gt;this video&lt;/a&gt; which is the best demonstration I’ve found of a structural editor and its benefits.&lt;/p&gt;&lt;h2&gt;2023H1 Programming System&lt;/h2&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;figure&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!ARa4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67236725-35e3-4184-b0ff-c7d738505366_2078x1249.png&quot; target=&quot;_blank&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;source type=&quot;image/webp&quot; /&gt;&lt;img alt=&quot;&quot; class=&quot;sizing-normal&quot; height=&quot;875&quot; src=&quot;https://substackcdn.com/image/fetch/$s_!ARa4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67236725-35e3-4184-b0ff-c7d738505366_2078x1249.png&quot; width=&quot;1456&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot; tabindex=&quot;0&quot; type=&quot;button&quot;&gt;&lt;svg fill=&quot;none&quot; height=&quot;20&quot; stroke=&quot;var(--color-fg-primary)&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;1.5&quot; viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;g&gt;&lt;title&gt;&lt;/title&gt;&lt;path d=&quot;M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/button&gt;&lt;button class=&quot;pencraft pc-reset pencraft icon-container view-image&quot; tabindex=&quot;0&quot; type=&quot;button&quot;&gt;&lt;svg class=&quot;lucide lucide-maximize2 lucide-maximize-2&quot; fill=&quot;none&quot; height=&quot;20&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt;&lt;polyline points=&quot;9 21 3 21 3 15&quot;&gt;&lt;/polyline&gt;&lt;line x1=&quot;21&quot; x2=&quot;14&quot; y1=&quot;3&quot; y2=&quot;10&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;3&quot; x2=&quot;10&quot; y1=&quot;21&quot; y2=&quot;14&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div class=&quot;native-video-embed&quot;&gt;&lt;/div&gt;&lt;p&gt;I started off by implementing a programming language with a syntax very much like Clojure, because I was most comfortable with that at the time. The simplicity of Clojure’s syntax makes it very powerful, and makes it a particularly good fit for a structural code editor.&lt;/p&gt;&lt;p&gt;In addition to a structural editor, this project implements a primitive compiler that emits both bytecode and (to a limited extent) x64 machine code. Initially, I implemented a tree-walking interpreter, but that was awfully slow. The bytecode interpreter was a significant improvement, but still orders of magnitude slower than machine code. The x64 backend is capable of directly generating a valid Windows executable binary without needing to depend on an assembler. The most sophisticated program I tested is a program that counts the total number of lines of all text files in a directory.&lt;/p&gt;&lt;p&gt;I also implemented a primitive debugger that integrates with the bytecode interpreter. Unfortunately, I do not have any images of this and now I cannot easily compile the project. This debugger had a GUI that displayed a listing of the bytecode instructions as well as the current contents of virtual registers and stack memory. You could set breakpoints, step through the program, and watch the program state change in real time.&lt;/p&gt;&lt;p&gt;I was initially using Skia for rendering, but later switched to doing basic software rendering with FreeType and Harfbuzz handling text rasterisation and layout. In the bytecode interpreter, I used &lt;a href=&quot;https://dyncall.org&quot;&gt;Dyncall&lt;/a&gt; for dynamically calling procedures in dynamically-loaded libraries.&lt;/p&gt;&lt;h2&gt;2023H2 Programming System&lt;/h2&gt;&lt;p&gt;My subsequent experiments involved building structural editors for the Jai programming language. The problem with a Lisp-like syntax is that its simplicity means it can be verbose when expressing the same amount of information that a statically-typed language needs to express. In such a statically-typed language like Jai, it pays off to have more complex syntax in return for brevity.&lt;/p&gt;&lt;p&gt;At first, I tried an editor with four types of node: token, string, block and newline. A block corresponds to a pair of brackets—&lt;code&gt;()&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt; or &lt;code&gt;{}&lt;/code&gt;—and contains a list of nodes. However, this did not feel powerful enough, so I proceeded to implement an alternative design with a more heterogeneous AST that more closely reflects what the compiler is working with. For example, declarations, binary operations, &lt;code&gt;if&lt;/code&gt; statements and procedures are each their own distinct type of node in the editor.&lt;/p&gt;&lt;div class=&quot;native-video-embed&quot;&gt;&lt;/div&gt;&lt;p&gt;The result was a fun, visually unique demonstration, but it revealed a challenge in the design space of structural editors: increasing the heterogeneity of the editor AST greatly increases complexity and makes editing operations similarly heterogeneous. In order to create nodes of a certain type, my solution was to use special key bindings, text commands, automatically converting a typed-out keyword into its corresponding type of node. This does not feel as fluid as the experience of editing a uniform array of characters in a traditional text file. Manipulating the tree becomes more difficult since it requires a larger set of more specialised operations, which imposes a greater cognitive burden.&lt;/p&gt;&lt;p&gt;There are also questions like:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;How specialised should the editor’s AST be?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Should you be able to insert AST nodes of a certain type into a slot that is invalid for its type?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Should you be allowed to partially select the contents of a node in the same selection that contains its siblings? How should selection ranges on a tree structure work?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Having done this experiment, I am now leaning towards a structure that is more uniform, but that needs further research.&lt;/p&gt;&lt;h1&gt;Graphics Rendering&lt;/h1&gt;&lt;p&gt;When implementing GUIs from scratch, there is a question of how to render graphical elements to the screen (after everything is laid out). A few options:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Use a library (like Skia or Blend2D) that abstracts away all the difficult parts.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Build a CPU-based software renderer.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Use the GPU by directly interfacing with an API like OpenGL, Direct3D or Vulkan.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Initially, I was using Skia because it is very capable, cross-platform, and was familiar to me. If I recall correctly, I was having performance issues rendering text in Skia. Almost certainly, this was because I was using it wrong and probably there was a lack of glyph caching, but I took this as a good excuse to write an extremely simple software renderer and handle text myself using FreeType and Harfbuzz.&lt;/p&gt;&lt;p&gt;My original software renderer was slow and I needed more performance, so I looked around. For a brief moment, I used &lt;a href=&quot;https://blend2d.com/&quot;&gt;Blend2D&lt;/a&gt;, a 2D CPU-based graphics rasterisation library, but performance was still not satisfactory. Furthermore, Blend2D is such a large, complex project that I would rather not depend on.&lt;/p&gt;&lt;p&gt;In 2023, I eventually gave in to GPU rendering and spend over two weeks learning how to use Direct3D 12 to produce the most basic 2D graphics. This was some serious drudgery, but in the end I had working shaders for drawing rectangles, images and glyph runs of text. &lt;a href=&quot;https://github.com/sebbbi/OffsetAllocator&quot;&gt;OffsetAllocator&lt;/a&gt; was used for managing GPU heap memory. The speed gain from GPU rendering was especially apparent in the OKLCH colour picker I made.&lt;/p&gt;&lt;div class=&quot;native-video-embed&quot;&gt;&lt;/div&gt;&lt;p&gt;Later, in 2025, I explored writing a CPU-based software renderer written in x86 assembly language. Because this used AVX2 instructions and could operate on eight pixels at a time, it was much faster than my prior software rendering attempt. I experimented with an implementation that operates on 8-byte integer colour channels, and another that operates on 16-byte floating-point colour channels for better blending accuracy (using special FP16 x86 SIMD instructions). My aim with this effort was to win some simplicity and avoid having to interface with a GPU graphics API which all have significant start-up latency.&lt;/p&gt;&lt;p&gt;This software renderer was initially inspired by the design in &lt;a href=&quot;https://guide.handmadehero.org/&quot;&gt;Handmade Hero&lt;/a&gt;, which approximates gamma correction by the square and square root operations. Unfortunately, this approximation results in a very noticeable loss of accuracy and attempting to do anything more accurate is prohibitively expensive. Even without the accurate gamma correction, the software render is still much slower than the GPU. Therefore, I had to conclude that GPU was the only feasible option for performant and accurate 2D graphics, which sadly meant enduring more of the misery of working with complex graphics APIs.&lt;/p&gt;&lt;p&gt;Approaching the end of 2025, my faith in the Windows operating system was nearing zero. I did not want to tie myself to this rapidly decaying platform if I could avoid it. Thus, I started learning Vulkan and used it to implement a basic 2D render. &lt;a href=&quot;https://vkguide.dev&quot;&gt;Vulkan Guide&lt;/a&gt; helped me get set up and I used the &lt;a href=&quot;https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator&quot;&gt;VulkanMemoryAllocator&lt;/a&gt; library because apparently it’s silly not to. &lt;a href=&quot;https://github.com/SaschaWillems/HowToVulkan&quot;&gt;HowToVulkan&lt;/a&gt; is a newer guide that I haven’t looked at but seems promising.&lt;/p&gt;&lt;p&gt;Vulkan has the benefit of giving you access to the latest features in GPU technology, whereas OpenGL is antiquated and better avoided nowadays. Vulkan is not fun to work with, but that’s where the future is headed, and that’s not a decision I get to make. The bindless APIs in the newer Vulkan versions makes things easier and enables substantial simplifications such as eliminating the need for a glyph atlas.&lt;/p&gt;&lt;h1&gt;Designing a Typeface and Text Layout System&lt;/h1&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;figure&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!ndDP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56a498ec-6e32-4213-bead-f750f4b9802f_1539x1257.png&quot; target=&quot;_blank&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;source type=&quot;image/webp&quot; /&gt;&lt;img alt=&quot;&quot; class=&quot;sizing-normal&quot; height=&quot;1189&quot; src=&quot;https://substackcdn.com/image/fetch/$s_!ndDP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56a498ec-6e32-4213-bead-f750f4b9802f_1539x1257.png&quot; width=&quot;1456&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot; tabindex=&quot;0&quot; type=&quot;button&quot;&gt;&lt;svg fill=&quot;none&quot; height=&quot;20&quot; stroke=&quot;var(--color-fg-primary)&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;1.5&quot; viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;g&gt;&lt;title&gt;&lt;/title&gt;&lt;path d=&quot;M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/button&gt;&lt;button class=&quot;pencraft pc-reset pencraft icon-container view-image&quot; tabindex=&quot;0&quot; type=&quot;button&quot;&gt;&lt;svg class=&quot;lucide lucide-maximize2 lucide-maximize-2&quot; fill=&quot;none&quot; height=&quot;20&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt;&lt;polyline points=&quot;9 21 3 21 3 15&quot;&gt;&lt;/polyline&gt;&lt;line x1=&quot;21&quot; x2=&quot;14&quot; y1=&quot;3&quot; y2=&quot;10&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;3&quot; x2=&quot;10&quot; y1=&quot;21&quot; y2=&quot;14&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;In designing UI systems, it has become clear to me that text rendering is by far the most complex and computationally-expensive part of rendering simple 2D UIs. This is because glyphs first have to be rasterised from a vector format into a bitmap image, then a potentially very complex set of rules need to be applied to compute the positions of each glyph in a string of text.&lt;/p&gt;&lt;p&gt;Glyph caching helps improves performance by saving the bitmap of a glyph, but the bitmap is only valid for a specific sub-pixel offset, and glyphs can be positioned at any non-integer coordinate. If you round the glyphs to the nearest pixel boundary, the text becomes noticeably unevenly spaced.&lt;/p&gt;&lt;p&gt;In my prior text rendering solution, I instead rounded each glyph to the nearest 1/3-pixel boundary which reduces the visual error, but requires up to three cached bitmaps per glyph.&lt;/p&gt;&lt;p&gt;Another solution I tried was to instead use a word cache: split up the text into short segments (words) and rasterise each word as a single bitmap image. This means that the spacing between glyphs in a single word is perfect, at the expense of more memory spent on storing bitmaps.&lt;/p&gt;&lt;p&gt;But I wanted something better. Following good engineering principles, I considered my specific situation and considered—from first principles—what an optimal text rendering design would look like. Here are some of the constraints and assumptions I came with:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Support only for left-to-right scripts like Latin, Cyrillic and Greek.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Glyphs must always be pixel-aligned (so there is one bitmap per glyph with no need for sub-pixel variants).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Support for characters of various widths (not just monospaced typefaces).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Kerning is applied to each pair of graphemes, with no further context allowed.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Font size is determined by cap height which must be an integer.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;No sub-pixel anti-aliasing like ClearType; it complicates colour blending, only works well in limited cases, and results in colour fringing. I would prefer using high-DPI displays so that sub-pixel anti-aliasing can be a forgotten artefact of the past.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;By constraining the problem I am attempting to solve, I can search for opportunities to make the targeted solution significantly superior to general solutions like TrueType and Harfbuzz. And indeed, I concluded that I would be better off throwing out TrueType/OpenType, Harfbuzz (or equivalent) and FreeType (or equivalent).&lt;/p&gt;&lt;p&gt;Firstly, I needed a glyph rasteriser to replace FreeType. The goal of this is to have a simple piece of code that does exactly what I need in a way I control. I learned about the mathematics of glyph rasterisation, giving careful consideration to floating-point precision limitations, and proceeded to implement a rasteriser in less than 800 lines. This rasteriser directly supports quadratic Bézier curves, unlike &lt;a href=&quot;https://github.com/nothings/stb/blob/master/stb_truetype.h&quot;&gt;stb_truetype&lt;/a&gt;, which has to approximate such curves by a series of straight line segments.&lt;/p&gt;&lt;p&gt;Secondly, I needed a typeface. Since I had chosen to reject the design of TrueType, I had shut myself out of the ecosystem of pre-existing typefaces—I had to now create my own. This process took much longer than I initially anticipated, and so I spent months swimming a laborious mathematical soup of twists, turns, and dead ends.&lt;/p&gt;&lt;p&gt;TrueType fonts work by representing each glyph as a collection of contours, where each contour consists of segments of either quadratic Bézier curves or straight lines (which is just a degenerate case of a quadratic Bézier curve). It gets further complicated by font hinting, which requires the use of an interpreter to read TrueType bytecode instructions which make adjustments to the vertices for better alignment with the pixel boundaries at a certain font size.&lt;/p&gt;&lt;p&gt;I also wanted to support hinting (where the glyph shape is optimised for the pixel boundaries at a certain size) but I wanted to implement it in a simpler way. Instead of representing my typeface in a bespoke data format (something like a &lt;code&gt;ttf&lt;/code&gt; or &lt;code&gt;otf&lt;/code&gt; file), I decided that my typeface would be defined in executable code for maximum flexibility. With this design, the process of obtaining a set of glyph contours involves calling a function with three key parameters: the typeface, the font size, and the glyph number. The code then directly computes the optimal glyph vectors depending on the font size, without needing a complicated bytecode interpreter. It’s all just regular imperative code.&lt;/p&gt;&lt;p&gt;Two main principles informed the design of my typeface:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Legibility: characters should be reasonably distinct and readable, even at rather small font sizes.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Performance: there should be minimal computational cost involved in generating the glyph vectors and rasterising them. This meant that I aimed to use as few curve segments as possible, including minimising the use of cubic Bézier curves (which must be broken up into quadratics prior to rasterisation).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To make the glyphs look as good as possible, I studied quite a bit of mathematical theory as I needed to be able to generate curves that ideally maintain G2 continuity, but these curves need to be cheap to generate. For weeks, I chased after mathematical derivations in &lt;a href=&quot;https://www.sagemath.org/&quot;&gt;SageMath&lt;/a&gt; trying to find formulae for computing curves that satisfied various invariants. This turned out to be much less fruitful that I had hoped; often, the sets of equations would be too complex to solve, or no solution would exist at all. I had also taken plenty of time to check the prior research, including the work done by &lt;a href=&quot;https://raphlinus.github.io/&quot;&gt;Raph Levien&lt;/a&gt;, but I wanted something as simple as possible and much of the research involved mathematical expressions that lack an analytical solution and thus are not trivial to compute (which would violate my performance constraints).&lt;/p&gt;&lt;p&gt;Eventually, I completed the designs for the ASCII range of characters. You may think, as I did, that the small range of ASCII characters wouldn’t take that much work to design. But I was waiting for the day that it would end, and I felt like it would never come.&lt;/p&gt;&lt;p&gt;Putting this all together into a text rendering system, I was definitely surprised by how well the result turned out. The text rendering system is now much simpler, and I can now comfortably use my own proportionally-spaced font in my own code editor.&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;figure&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!MP4h!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffad834ee-2315-435f-a8ce-bdf2db49ce27_2169x1183.png&quot; target=&quot;_blank&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;source type=&quot;image/webp&quot; /&gt;&lt;img alt=&quot;&quot; class=&quot;sizing-normal&quot; height=&quot;794&quot; src=&quot;https://substackcdn.com/image/fetch/$s_!MP4h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffad834ee-2315-435f-a8ce-bdf2db49ce27_2169x1183.png&quot; width=&quot;1456&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot; tabindex=&quot;0&quot; type=&quot;button&quot;&gt;&lt;svg fill=&quot;none&quot; height=&quot;20&quot; stroke=&quot;var(--color-fg-primary)&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;1.5&quot; viewBox=&quot;0 0 20 20&quot; width=&quot;20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;g&gt;&lt;title&gt;&lt;/title&gt;&lt;path d=&quot;M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/button&gt;&lt;button class=&quot;pencraft pc-reset pencraft icon-container view-image&quot; tabindex=&quot;0&quot; type=&quot;button&quot;&gt;&lt;svg class=&quot;lucide lucide-maximize2 lucide-maximize-2&quot; fill=&quot;none&quot; height=&quot;20&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;polyline points=&quot;15 3 21 3 21 9&quot;&gt;&lt;/polyline&gt;&lt;polyline points=&quot;9 21 3 21 3 15&quot;&gt;&lt;/polyline&gt;&lt;line x1=&quot;21&quot; x2=&quot;14&quot; y1=&quot;3&quot; y2=&quot;10&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;3&quot; x2=&quot;10&quot; y1=&quot;21&quot; y2=&quot;14&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;h1&gt;From Clojure to Jai&lt;/h1&gt;&lt;p&gt;This section presents some of my reasoning as to why my style of programming dramatically shifted away from the dynamic, functional, GC-powered land of Clojure and towards embracing a more imperative, statically-typed, systems-level kind of programming.&lt;/p&gt;&lt;h2&gt;Clojure Is Too Slow, and Performance Matters&lt;/h2&gt;&lt;p&gt;Having become reasonably experienced using the Clojure programming language for over two years, I repeatedly found myself running into the same insurmountable obstacle: performance. Put simply, Clojure is too inefficient for high-performance software, and the program start-up time is unacceptable. I have spent a lot of time optimising performance in Clojure programs but nothing is ever enough.&lt;/p&gt;&lt;p&gt;In &lt;a href=&quot;https://luisthiamnye.substack.com/p/adventures-in-dynamic-software-visualisations&quot;&gt;my previous article&lt;/a&gt;, I discussed my attempt at building a compiler for a custom JVM-based language which aimed to be more performant, but eventually I had to face reality: the Java Virtual Machine is too slow and limiting.&lt;/p&gt;&lt;p&gt;There is a good reason the JVM has not taken over the world of performance-critical software (operating systems, codecs, game engines). Even in cases where performance is less critical, choosing to build your software with inefficient technology like the JVM still does a disservice to your users, since the result is often:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Less responsive and more sluggish (even for simple UIs, the slightest difference matters).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Less power efficient (especially important for laptops).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;More complex (larger binary sizes, possibly requiring a separate JVM installation).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To illustrate the importance of performance, if a compiler takes two seconds to compile instead of one, the added delay further breaks the flow of development and decreases dampens one’s spirit. Billions of people use software every day, so each minor delay quickly adds up to an unfathomably great waste of human hours spent waiting for software to respond.&lt;/p&gt;&lt;p&gt;Performance also affects the user’s pattern of behaviour. For example, the slow file search in Windows File Explorer means that I never use that feature. If the performance of the search were much better, then superior workflows could be enabled that take advantage of that search feature. This is not merely hypothetical: nowadays, with &lt;a href=&quot;https://filepilot.tech/&quot;&gt;FilePilot&lt;/a&gt;, I use text search to rapidly navigate the file system all the time.&lt;/p&gt;&lt;p&gt;A common dogma in computer programming is that you should not be optimising for performance until you know your performance requirements and bottlenecks. To a large extent that is true, but it is not an excuse for things that are slow for no good reason; these things, polluted with junk, can be made faster without sacrificing a good programming experience, and in fact the programmer’s experience will often be improved by prioritising performance and simplicity.&lt;/p&gt;&lt;p&gt;As soon as you choose to use something like the JVM, you have already accepted a significant performance and memory penalty. If people don’t think about performance from the beginning, we end up with a proliferation of slow software (the world we live in now). You may take steps to optimise the program (which naturally makes it more brittle), but you will always have the foundational issue of running on a suboptimal, managed VM.&lt;/p&gt;&lt;p&gt;Programs should be fast by default—and the language should push the programmer in the direction of something that is not horribly inefficient. At the very least, there should be a pathway to transform the program into something that makes reasonably efficient use of the hardware.&lt;/p&gt;&lt;p&gt;Also, if you are developing a library, you should assume highly strict performance criteria, since you do not know in which contexts your library may be used. Pouring a huge volume of effort into a fundamentally slow technology may waste the time of your users. But it will also cut off those users for whom the library is too slow, thus their time is wasted by not being able to do what they want to do without reimplementing the library features.&lt;/p&gt;&lt;p&gt;In the case of a desktop applications, it is imperative that they be fast and responsive, out of respect of the user. It should be normal that it be fast and responsive, without any special effort. Computers are incredibly fast—fast enough to drive interactive 3D worlds with complicated graphics—yet even simple programs struggle to meet reasonable performance expectations. Even if you do make the program feel responsive, its inefficiencies can still impact the user experience. For instance, you may consume an absurd amount of memory that reduces the user’s multitasking potential. Additionally, through excessive CPU usage, you could bring real discomfort to the user due to increased fan noise.&lt;/p&gt;&lt;p&gt;Performance aside, the JVM’s class loader system was difficult to work with, and it was impossible to do the things I wanted in a non-clunky way. The JVM is drowned in unnecessary complexity, which I found apparent when working with its bytecode and flawed object model.&lt;/p&gt;&lt;p&gt;I think good engineering is important, and that means valuing efficient and simple solutions. The JVM is not that, and nor is it something that can be fixed; its problems stretch down to the roots.&lt;/p&gt;&lt;h2&gt;Finding a Good Programming Language&lt;/h2&gt;&lt;p&gt;At the dawn of 2023, I departed from the sweet comforts of JVM-enforced memory safety and escaped into the wilderness of systems-level languages with manual memory management.&lt;/p&gt;&lt;p&gt;I had briefly tried Rust in late 2021, but never again. The misguided design of this language creates tremendous friction and imposes artificial puzzles that get in the way of solving the actual problem at hand. The costs outweigh the benefits, at least in most cases, and the compile times are torturous.&lt;/p&gt;&lt;p&gt;So, I began to learn Zig as I set out to create a new compiler. However, it didn’t take long before my frustration with Zig’s language design reached a critical threshold. For instance, the Zig compiler treats any unused variable as an error, and that is just not the way I want to program.&lt;/p&gt;&lt;p&gt;I promptly adopted &lt;a href=&quot;https://odin-lang.org&quot;&gt;Odin&lt;/a&gt;, which has a far superior design philosophy. There is still a lot I don’t like about it—like the restrictive namespacing system—but it worked well enough for my compiler, interpreter and debugger project of the first half of 2023. Eventually, the growing project size (albeit still modest) meant that the LLVM compile times were unfortunately becoming a significant source of friction.&lt;/p&gt;&lt;p&gt;In the middle of 2023, I began using the Jai programming language. It is, by an order of magnitude, more well-designed and powerful than any other language in its class, thanks to a unique combination of fast compile times, powerful polymorphism, meta-programming powered by a bytecode interpreter, helpful error messages and ergonomic syntax.&lt;/p&gt;&lt;p&gt;Despite being rather complex, Jai actually feels very simple, uniform, and almost Lisp-like in its design. Unlike other languages, Jai spends it complexity budget wisely—in the places where the added complexity pays off the most. I am using Jai to this day and that is not changing any time soon.&lt;/p&gt;&lt;h2&gt;But the Libraries! The Ecosystem!&lt;/h2&gt;&lt;p&gt;One of the benefits of being tapped into the Clojure and JVM ecosystem is the access to a vast range of software libraries. However, I value that much less now than I used to in the past.&lt;/p&gt;&lt;p&gt;Building upon layers of abstraction can often be harmful due to the additional constraints of that abstraction. The API of a library may push you towards a design that is not optimal for the problem you are trying to solve. Also, libraries are usually designed to support a variety of use-cases, which probably means that there is more abstraction (and more features) than you truly need.&lt;/p&gt;&lt;p&gt;Often, I have run into trouble when my specific use-case does not fit the exact mould of the library. As a result, when going to do something specific, the library’s design may outright prevent me from doing what I want without having to fork and modify the source code—an undesirable solution that partially defeats the point of using a library in the first place.&lt;/p&gt;&lt;p&gt;Furthermore, I don’t tend to use a large number of third-party libraries, and many of the ones I do use could be feasibly reimplemented myself. I’d say writing that extra code is worth it in many cases, especially where the library in question is simple or only a small subset of it is used. As a bonus, the code you write is tailored to your specific needs.&lt;/p&gt;&lt;p&gt;In any case, I’m of the philosophy that you shouldn’t be afraid to throw away code often. An implication of this is that code should be easy to write and refactor, and a reason for this practice is that the more you work on a project, the better of an idea you get about what your program should ultimately look like. Rather than being held back by old, uninformed code, you should have the freedom to rebuild substantial pieces from a simpler foundation backed by experience. A library is analogous to the old informed code you wrote before you fully understood your needs.&lt;/p&gt;&lt;p&gt;Remember: Source code you don’t own or understand is a liability; it’s good to keep things minimal.&lt;/p&gt;&lt;h2&gt;Dynamic Code Execution&lt;/h2&gt;&lt;p&gt;For me, the most compelling feature of the JVM was the ability to dynamically modify and inspect the live program. That said, with some work, simple hot code loading can be implemented in an unmanaged language. By not having to deal with the class loader system, it is possible to design a programming language with a much better system for dynamically loading code, tailored and fine-tuned according to how I want it. In fact, it may turn out that the simpler solution overall is to implement this stuff from scratch and bypass the complexity put forth by the JVM.&lt;/p&gt;&lt;p&gt;Another consideration: how dynamic do you need to be? By using self-describing objects everywhere, the JVM is highly dynamic, but at the great expense of performance. Is that trade-off worth it? I don’t think so, after having gained experience in modern unmanaged languages.&lt;/p&gt;&lt;p&gt;I believe it is preferable to opt-in to these dynamic runtime features in the places and times you need them. Ideally, no changes to the source code should be necessary to compile a more dynamic and introspective version of the program compared to a completely static version. Additionally, in the spirit of Mouldable Development, it should be cheap to create ad-hoc tools for live visualisation, debugging, and modification of the program. The video games industry has some good examples of bespoke in-house tooling.&lt;/p&gt;&lt;h1&gt;Conclusion&lt;/h1&gt;&lt;p&gt;At a very bird’s-eye view, I have presented some hopefully interesting aspects of my projects. This skips over many details like the mathematics involved in typeface design as well as entire topics including the compiler implementations. Also, I haven’t touched on foundational systems such as the memory allocators and the arena allocator system that I use throughout my codebase. There is also a lot yet to talk about relating to UI event handling, caching, and layout. Maybe another day.&lt;/p&gt;&lt;p&gt;To wrap up, I would like to emphasise a piece of truth that I repeatedly discovered as I set down these paths of learning unfamiliar topics: the low-level details are not as frightening as they may seem at first.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;When I was writing Clojure, the idea of working with JVM bytecode seemed daunting. Then, I spent time learning about it and found it was not so difficult after all.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Coming from garbage-collected languages, the idea of using a language with manual memory management sounded very tricky and error-prone, but it’s not actually that bad with the right techniques.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Learning assembly language and manually generating x64 machine code was intimidating at first, but it just takes perseverance to read through the vast Intel documentation before being able to create something simple that works. Assembly language itself is actually very simple to understand.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Learning to use a graphics API, particularly one like D3D12 or Vulkan, seemed like a monumental task due to the vast amount of prerequisite knowledge. And indeed it is, because there is no happiness to be found in modern graphics APIs. However, once you dip a foot in and start learning, you start to pick things up, things get more familiar, and thus things get a bit easier.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Not everything is like this—some things are just really difficult and may even take a lifetime to master. For instance, I’ve made no attempt at an optimising compiler because there is no limit to how complicated that gets when taken to the fullest extent (see LLVM). Also, when working with modern graphics APIs like D3D12, even industry professionals are prone to make subtle memory management mistakes thanks to the overly convoluted nature of these APIs.&lt;/p&gt;&lt;p&gt;But for many things, all it takes is the initial step of dipping your toes in the water, then as you begin uncovering new territory, everything becomes a lot clearer and less daunting.&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;div class=&quot;subscription-widget-wrap-editor&quot;&gt;&lt;div class=&quot;subscription-widget show-subscribe&quot;&gt;&lt;div class=&quot;preamble&quot;&gt;&lt;p class=&quot;cta-caption&quot;&gt;Thanks for reading. To possibly receive future updates, you may choose to subscribe via email.&lt;/p&gt;&lt;/div&gt;&lt;form class=&quot;subscription-widget-subscribe&quot;&gt;&lt;input class=&quot;email-input&quot; name=&quot;email&quot; tabindex=&quot;-1&quot; type=&quot;email&quot; /&gt;&lt;input class=&quot;button primary&quot; type=&quot;submit&quot; value=&quot;Subscribe&quot; /&gt;&lt;div class=&quot;fake-input-wrapper&quot;&gt;&lt;div class=&quot;fake-input&quot;&gt;&lt;/div&gt;&lt;div class=&quot;fake-button&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/form&gt;&lt;/div&gt;&lt;/div&gt;</content>
		<author>
			<name>Luis Thiam-Nye</name>
			<uri>https://luisthiamnye.substack.com</uri>
		</author>
		<source>
			<title type="html">Luis</title>
			<subtitle type="html">Software adventures.</subtitle>
			<link rel="self" href="https://luisthiamnye.substack.com/feed"/>
			<id>https://luisthiamnye.substack.com</id>
			<rights type="html">Luis Thiam-Nye</rights>
		</source>
	</entry>

	<entry>
		<title type="html" xml:lang="en-US">From Prototype to Production: The Business Case for Chachaml Part 3</title>
		<link href="https://flexiana.com/news/clojure/from-prototype-to-production-the-business-case-for-chachaml-part-3"/>
		<id>https://flexiana.com/?p=18200</id>
		<updated>2026-06-29T11:31:48+00:00</updated>
		<content type="html" xml:lang="en-US">&lt;p&gt;This is the final article in our series, &lt;strong&gt;Clojure Meets Production MLOps: How &lt;/strong&gt;&lt;strong&gt;chachaml &lt;/strong&gt;&lt;strong&gt;Delivers AI-Native Workflows&lt;/strong&gt;.&lt;/p&gt;



&lt;p&gt;If you’re joining here, you may want to start with the earlier posts:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://flexiana.com/news/clojure/clojure-meets-production-mlops-how-chachaml-delivers-ai-native-workflows-part-1&quot;&gt;Part 1, &lt;em&gt;Why Machine Learning Projects Fail in Production—and How &lt;/em&gt;chachaml &lt;em&gt;Solves the MLOps Gap&lt;/em&gt;&lt;/a&gt;&lt;/strong&gt;, explained why production operations have become a bigger challenge than model development and how &lt;strong&gt;chachaml &lt;/strong&gt;addresses that problem.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://flexiana.com/news/inside-chachaml-core-capabilities-for-ai-native-workflows-in-clojure&quot;&gt;Part 2, &lt;em&gt;Inside &lt;/em&gt;chachaml&lt;em&gt;: Core Capabilities for AI-Native Workflows in Clojure&lt;/em&gt;&lt;/a&gt;&lt;/strong&gt;, covered the platform’s architecture and the features that support AI-native development.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;In this final article, we’ll focus on the business side. We’ll look at what adopting &lt;strong&gt;chachaml &lt;/strong&gt;means for engineering teams, how it helps shorten the path from prototype to production, and why that matters when you’re building AI systems that need to last.&lt;/p&gt;



&lt;h1 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;Real-World Use Case #1: End-to-End Model Development Inside a Clojure System.&lt;/strong&gt;&lt;/h1&gt;



&lt;p&gt;A team builds a recommendation engine for a Clojure app. It uses data from their existing databases and services. That data moves through feature pipelines and into model training.&lt;/p&gt;



&lt;p&gt;With &lt;strong&gt;chachaml&lt;/strong&gt;, every training run is tracked automatically. Everything—parameters, metrics, artifacts, and results—is gathered in one place, making it easy to review and contrast your experiments. &lt;/p&gt;



&lt;p&gt;Once the model looks good, the team registers it, handles versioning, and promotes it through the model registry. After that, deployment is just part of the flow, and they monitor it performance from there. &lt;/p&gt;



&lt;p&gt;The whole process happens in a single environment: &lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Data Ingestion → Training → Tracking → Registry → Deployment&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Example: End-to-end run in a single Clojure form&lt;/strong&gt;&lt;/p&gt;



&lt;div class=&quot;wp-block-sourcecode&quot; id=&quot;block_bdd055cfd127d40124bf1dcbd89dde49&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; 30-second quickstart — full end-to-end tracking
(ml/with-run {:experiment &quot;quickstart&quot;}
  (ml/log-params {:lr 0.01 :epochs 50})
  (ml/log-metric :accuracy 0.94)
  (ml/log-artifact &quot;model&quot; {:weights [1.0 2.0] :bias 0.3}))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;/p&gt;



&lt;p&gt;There’s no need to piece together multiple tools or move between different systems.&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;Benefits&lt;/strong&gt;&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Single technology stack.&lt;/li&gt;



&lt;li&gt;Less operational complexity.&lt;/li&gt;



&lt;li&gt;Faster iteration.&lt;/li&gt;



&lt;li&gt;Easier team collaboration.&lt;/li&gt;



&lt;li&gt;Better visibility across the ML lifecycle.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;For teams already using Clojure, this makes it much easier to take machine learning projects from development to production.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;



&lt;h1 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;Real-World Use Case #2: Hybrid Python + Clojure ML Infrastructure.&lt;/strong&gt;&lt;/h1&gt;



&lt;p&gt;For machine learning, many teams don’t use just one language.&lt;/p&gt;



&lt;p&gt;A common setup is a Python data science team working alongside a Clojure production platform.&lt;/p&gt;



&lt;p&gt;Data scientists train and evaluate models in Python. The production application runs in Clojure.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Chachaml &lt;/strong&gt;helps connect both sides.&lt;/p&gt;



&lt;p&gt;Teams can send runs, metrics, and artifacts using simple HTTP APIs. &lt;/p&gt;



&lt;p&gt;If they are working in Python, they can connect with &lt;strong&gt;chachaml &lt;/strong&gt;using scikit-learn or libpython-clj2. All artifacts are stored in a shared S3-compatible storage, so everyone—across different teams and environments—can access what they require.&lt;/p&gt;



&lt;p&gt;Here’s the general flow: &lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Python Training → chachaml Tracking → Shared Artifact Storage → Clojure Production Platform&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Example: Hybrid Python + Clojure workflow&lt;/strong&gt;&lt;/p&gt;



&lt;div class=&quot;wp-block-sourcecode&quot; id=&quot;block_1fb73e156c1723b5fa4a16815d978fbd&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-clojure&quot;&gt;;; Python side: push run via HTTP (shown above)
;; Clojure side: load artifact and serve
(let [model (reg/load-model &quot;iris-classifier&quot;)]
  (predict model new-features))

;; Shared S3 artifact storage available to both sides
(artifact/store! run-id &quot;model&quot; model {:backend :s3})
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;/p&gt;



&lt;p&gt;This setup comes with some real perks: &lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Python tooling for data science.&lt;/li&gt;



&lt;li&gt;Clojure for production systems.&lt;/li&gt;



&lt;li&gt;Shared experiment tracking.&lt;/li&gt;



&lt;li&gt;Centralized artifact management.&lt;/li&gt;



&lt;li&gt;Less duplication across teams.&lt;/li&gt;



&lt;li&gt;A consistent view of the ML lifecycle.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Teams don’t have to replace their preferred tools; all components are centralized, making ML operations much easier to handle. &lt;/p&gt;



&lt;p&gt;&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;



&lt;h1 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;What This Means for CTOs and Engineering Leaders&lt;/strong&gt;&lt;/h1&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;➜ Strategic Benefits&lt;/strong&gt;&lt;/h3&gt;



&lt;p&gt;For engineering leaders, machine learning isn’t just about building models anymore—it’s become all about keeping operations running smoothly. &lt;/p&gt;



&lt;p&gt;These days, as teams adopt more AI systems, they manage a bunch of tools, workflows, and deployment setups. Before the team knows it, every aspect quickly becomes complicated and hard to handle. &lt;/p&gt;



&lt;p&gt;A unified approach changes the game. &lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Decrease tool sprawl.&lt;/li&gt;



&lt;li&gt;Improve governance.&lt;/li&gt;



&lt;li&gt;Increase reproducibility.&lt;/li&gt;



&lt;li&gt;Lower operational complexity.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;And as machine learning becomes part of core business systems, those benefits become more important.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;➜ Evaluating Long-Term ML Platform Decisions&lt;/strong&gt;&lt;/h3&gt;



&lt;p&gt;Choosing an MLOps platform is a long-term architecture decision. It guides teams on building and deploying models. It also helps them with how they run and troubleshoot them. &lt;/p&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;&lt;table class=&quot;has-fixed-layout&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Factor&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Python-Centric&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;chachaml + Clojure&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Stack consistency&lt;/td&gt;&lt;td&gt;Low&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Operational complexity&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;td&gt;Lower&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;REPL workflow&lt;/td&gt;&lt;td&gt;Limited&lt;/td&gt;&lt;td&gt;Native&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JVM integration&lt;/td&gt;&lt;td&gt;Moderate&lt;/td&gt;&lt;td&gt;Excellent&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;



&lt;p&gt;&lt;/p&gt;



&lt;p&gt;If a team already works with Clojure and the JVM, sticking with that ecosystem reduces complexity. &lt;/p&gt;



&lt;p&gt;Teams can reduce the challenges of managing multiple platforms, so workflows move faster and the infrastructure remains less complex. &lt;/p&gt;



&lt;p&gt;The idea isn’t to avoid Python completely—it’s mainly about simplifying workflows by using one solid platform instead of many. &lt;/p&gt;



&lt;p&gt;&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;



&lt;h1 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;Flexiana’s View on the Future of MLOps in Clojure&lt;/strong&gt;&lt;/h1&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;➜ The Ecosystem Is Entering a New Phase&lt;/strong&gt;&lt;/h3&gt;



&lt;p&gt;Machine learning in Clojure isn’t just an experiment anymore. It used to be all about building models, managing data, and doing research, but that’s changed. &lt;/p&gt;



&lt;p&gt;Now, more teams are actually putting machine learning systems in place and handling everything that comes with keeping them up and running. &lt;/p&gt;



&lt;p&gt;AI is emerging in everyday business tools, too, which means teams need better ways to track experimental progress, manage their models, assess results, and collaborate across projects. &lt;/p&gt;



&lt;p&gt;As adoption grows, operational tooling becomes just as important as model development. That’s usually a sign that an ecosystem is maturing.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;➜ Why &lt;/strong&gt;&lt;strong&gt;chachaml &lt;/strong&gt;&lt;strong&gt;Matters Beyond One Library&lt;/strong&gt;&lt;/h3&gt;



&lt;p&gt;&lt;strong&gt;chachaml &lt;/strong&gt;is part of a larger shift in the Clojure ecosystem.&lt;/p&gt;



&lt;p&gt;The challenge for most organizations is no longer training a model. It’s managing machine learning systems over time. That requires tracking, governance, deployment workflows, monitoring, and collaboration.&lt;/p&gt;



&lt;p&gt;These are operational problems, and they need operational tools.&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;&lt;em&gt;At &lt;/em&gt;&lt;a href=&quot;https://flexiana.com/&quot;&gt;&lt;strong&gt;&lt;em&gt;Flexiana&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt;&lt;em&gt; we’ve spent years building production systems, working with machine learning workloads in Clojure, and helping teams manage hybrid ML environments that combine Clojure services with Python-based tooling. Those experiences highlighted a common gap: Teams could build models but lacked the infrastructure to run them in production.&lt;/em&gt;&lt;/h4&gt;



&lt;p&gt;As machine learning becomes a standard part of software architecture, Clojure teams will need tools that support the full lifecycle of those systems.&lt;/p&gt;



&lt;p&gt;That’s why &lt;strong&gt;chachaml &lt;/strong&gt;matters. It’s not only about machine learning. It brings production ML to Clojure with lasting infrastructure. &lt;/p&gt;



&lt;p&gt;&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;



&lt;h1 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;To Sum Up&lt;/strong&gt;&lt;/h1&gt;



&lt;p&gt;&lt;strong&gt;chachaml &lt;/strong&gt;really steps up Clojure’s MLOps game. This platform puts everything in one place. Teams can track their experiments, manage pipelines, monitor models, and stay on top of everything—all without the usual hassle. Collaboration actually feels easy here. &lt;/p&gt;



&lt;p&gt;The built-in MCP support is a solid perk, as it lets AI agents work directly with the ML data and operations.&lt;/p&gt;



&lt;p&gt;At &lt;a href=&quot;https://flexiana.com/&quot;&gt;&lt;strong&gt;&lt;em&gt;Flexiana&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;, we’re excited about this. It feels like a big step toward making machine learning in Clojure more reliable and ready for serious use. &lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;Get started with &lt;/strong&gt;&lt;strong&gt;chachaml&lt;/strong&gt;&lt;strong&gt;. Talk to Flexiana.&lt;/strong&gt;&lt;/h3&gt;



&lt;p&gt;Review the source code, documentation, and examples on &lt;a href=&quot;https://github.com/flexiana/chachaml/blob/master/doc/MCP.md&quot;&gt;&lt;strong&gt;GitHub &lt;/strong&gt;&lt;/a&gt;to see how chachaml works in practice. Connect the &lt;strong&gt;chachaml &lt;/strong&gt;MCP server to tools like Claude Code or Continue and explore machine learning data through natural language queries. &lt;/p&gt;



&lt;p&gt;If you’re evaluating machine learning infrastructure or planning a production ML system, we’d be happy to talk.&lt;/p&gt;



&lt;p&gt;We help teams with:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Clojure ML architecture&lt;/li&gt;



&lt;li&gt;Production MLOps platforms&lt;/li&gt;



&lt;li&gt;Hybrid JVM/Python systems&lt;/li&gt;



&lt;li&gt;AI infrastructure strategy&lt;/li&gt;



&lt;li&gt;Enterprise software development&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Whether you’re building a new ML platform or improving an existing one, we can help you design an approach that fits your systems, workflows, and long-term goals.&lt;/p&gt;
&lt;p&gt;The post &lt;a href=&quot;https://flexiana.com/news/clojure/from-prototype-to-production-the-business-case-for-chachaml-part-3&quot;&gt;From Prototype to Production: The Business Case for Chachaml Part 3&lt;/a&gt; appeared first on &lt;a href=&quot;https://flexiana.com&quot;&gt;Flexiana&lt;/a&gt;.&lt;/p&gt;</content>
		<author>
			<name>Jiri Knesl</name>
			<uri>https://flexiana.com/</uri>
		</author>
		<source>
			<title type="html">Flexiana</title>
			<link rel="self" href="https://flexiana.com/feed"/>
			<id>https://flexiana.com/</id>
		</source>
	</entry>

	<entry>
		<title type="html" xml:lang="en-US">I am sorry, but everyone is getting syntax highlighting wrong</title>
		<link href="https://tonsky.me/blog/syntax-highlighting/"/>
		<id>https://tonsky.me/blog/syntax-highlighting/</id>
		<updated>2026-06-28T21:53:35+00:00</updated>
		<content type="html" xml:lang="en-US">&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/cover.webp&quot; /&gt;&lt;p&gt;&lt;em&gt;Translations: &lt;a href=&quot;https://habr.com/ru/articles/957024/&quot;&gt;Russian&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Syntax highlighting is a tool. It can help you read code faster. Find things quicker. Orient yourself in a large file.&lt;/p&gt;
&lt;p&gt;Like any tool, it can be used correctly or incorrectly. Let’s see how to use syntax highlighting to help you work.&lt;/p&gt;
&lt;h1 id=&quot;christmas-lights-diarrhea&quot;&gt;Christmas Lights Diarrhea&lt;/h1&gt;
&lt;p&gt;Most color themes have a unique bright color for literally everything: one for variables, another for language keywords, constants, punctuation, functions, classes, calls, comments, etc.&lt;/p&gt;
&lt;p&gt;Sometimes it gets so bad one can’t see the base text color: everything is highlighted. What’s the base text color here?&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/diarrhea.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;The problem with that is, if everything is highlighted, nothing stands out. Your eye adapts and considers it a new norm: everything is bright and shiny, and instead of getting separated, it all blends together.&lt;/p&gt;
&lt;p&gt;Here’s a quick test. Try to find the function definition here:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/definitions_bad.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;and here:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/definitions_good.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;See what I mean?&lt;/p&gt;
&lt;p&gt;So yeah, unfortunately, you can’t just highlight everything. You have to make decisions: what is more important, what is less. What should stand out, what shouldn’t.&lt;/p&gt;
&lt;p&gt;Highlighting everything is like assigning “top priority” to every task in Linear. It only works if most of the tasks have lesser priorities.&lt;/p&gt;
&lt;p class=&quot;loud&quot;&gt;If everything is highlighted, nothing is highlighted.&lt;/p&gt;
&lt;h1 id=&quot;enough-colors-to-remember&quot;&gt;Enough colors to remember&lt;/h1&gt;
&lt;p&gt;There are two main use-cases you want your color theme to address:&lt;/p&gt;
&lt;ol start=&quot;1&quot;&gt;
  &lt;li&gt;Look at something and tell what it is by its color (you can tell by reading text, yes, but why do you need syntax highlighting then?)&lt;/li&gt;
  &lt;li&gt;Search for something. You want to know what to look for (which color).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;1 is a direct index lookup: color → type of thing.&lt;/p&gt;
&lt;p&gt;2 is a reverse lookup: type of thing → color.&lt;/p&gt;
&lt;p&gt;Truth is, most people don’t do these lookups at all. They might think they do, but in reality, they don’t.&lt;/p&gt;
&lt;p&gt;Let me illustrate. Before:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/change_before.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;After:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/change_after.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Can you see it? I misspelled &lt;code&gt;return&lt;/code&gt; for &lt;code&gt;retunr&lt;/code&gt; and its color switched from red to purple.&lt;/p&gt;
&lt;p&gt;I can’t.&lt;/p&gt;
&lt;p&gt;Here’s another test. Close your eyes (not yet! Finish this sentence first) and try to remember what color your color theme uses for class names?&lt;/p&gt;
&lt;p&gt;Can you?&lt;/p&gt;
&lt;p&gt;If the answer for both questions is “no”, then your color theme is &lt;em&gt;not functional&lt;/em&gt;. It might give you comfort (as in—I feel safe. If it’s highlighted, it’s probably code) but you can’t use it as a tool. It doesn’t &lt;em&gt;help&lt;/em&gt; you.&lt;/p&gt;
&lt;p&gt;What’s the solution? Have an absolute minimum of colors. So little that they all fit in your head at once. For example, my color theme, Alabaster, only uses four:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Green for strings&lt;/li&gt;
  &lt;li&gt;Purple for constants&lt;/li&gt;
  &lt;li&gt;Yellow for comments&lt;/li&gt;
  &lt;li&gt;Light blue for top-level definitions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s it! And I was able to type it all from memory, too. This minimalism allows me to actually do lookups: if I’m looking for a string, I know it will be green. If I’m looking at something yellow, I know it’s a comment.&lt;/p&gt;
&lt;p class=&quot;loud&quot;&gt;Limit the number of different colors to what you can remember.&lt;/p&gt;
&lt;p&gt;If you swap green and purple in my editor, it’ll be a catastrophe. If somebody swapped colors in yours, would you even notice?&lt;/p&gt;
&lt;h1 id=&quot;what-should-you-highlight&quot;&gt;What should you highlight?&lt;/h1&gt;
&lt;p&gt;Something there isn’t a lot of. Remember—we want highlights to stand out. That’s why I don’t highlight variables or function calls—they are everywhere, your code is probably 75% variable names and function calls.&lt;/p&gt;
&lt;p&gt;I do highlight constants (numbers, strings). These are usually used more sparingly and often are reference points—a lot of logic paths start from constants.&lt;/p&gt;
&lt;p&gt;Top-level definitions are another good idea. They give you an idea of a structure quickly.&lt;/p&gt;
&lt;p&gt;Punctuation: it helps to separate names from syntax a little bit, and you care about names first, especially when quickly scanning code.&lt;/p&gt;
&lt;p&gt;Please, please don’t highlight language keywords. &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;function&lt;/code&gt;, &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else&lt;/code&gt;stuff like this. You rarely look for them: “where’s that if” is a valid question, but you will be looking not at the &lt;code&gt;if&lt;/code&gt; the keyword, but at the condition after it. The condition is the important, distinguishing part. The keyword is not.&lt;/p&gt;
&lt;p class=&quot;loud&quot;&gt;Highlight names and constants. Grey out punctuation. Don’t highlight language keywords.&lt;/p&gt;
&lt;h1 id=&quot;comments-are-important&quot;&gt;Comments are important&lt;/h1&gt;
&lt;p&gt;The tradition of using grey for comments comes from the times when people were paid by line. If you have something like&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/javadoc.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;of course you would want to grey it out! This is bullshit text that doesn’t add anything and was written to be ignored.&lt;/p&gt;
&lt;p&gt;But for good comments, the situation is opposite. Good comments ADD to the code. They explain something that couldn’t be expressed directly. They are &lt;em&gt;important&lt;/em&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/yellow_comments.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;So here’s another controversial idea:&lt;/p&gt;
&lt;p class=&quot;loud&quot;&gt;Comments should be highlighted, not hidden away.&lt;/p&gt;
&lt;p&gt;Use bold colors, draw attention to them. Don’t shy away. If somebody took the time to tell you something, then you want to read it.&lt;/p&gt;
&lt;h1 id=&quot;two-types-of-comments&quot;&gt;Two types of comments&lt;/h1&gt;
&lt;p&gt;Another secret nobody is talking about is that there are two types of comments:&lt;/p&gt;
&lt;ol start=&quot;1&quot;&gt;
  &lt;li&gt;Explanations&lt;/li&gt;
  &lt;li&gt;Disabled code&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most languages don’t distinguish between those, so there’s not much you can do syntax-wise. Sometimes there’s a convention (e.g. &lt;code&gt;--&lt;/code&gt; vs &lt;code&gt;/* */&lt;/code&gt; in SQL), then use it!&lt;/p&gt;
&lt;p&gt;Here’s a real example from Clojure codebase that makes perfect use of two types of comments:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/two_types_of_comments.webp&quot; /&gt;Disabled code is gray, explanation is bright yellow&lt;/figure&gt;
&lt;h1 id=&quot;light-or-dark&quot;&gt;Light or dark?&lt;/h1&gt;
&lt;p&gt;Per statistics, 70% of developers prefer dark themes. Being in the other 30%, that question always puzzled me. Why?&lt;/p&gt;
&lt;p&gt;And I think I have an answer. Here’s a typical dark theme:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/vscode_default_dark@2x.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;and here’s a light one:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/vscode_default_light@2x.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;On the latter one, colors are way less vibrant. Here, I picked them out for you:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/vscode_colors@2x.png&quot; /&gt;Notice how many colors there are. No one can remember that many.&lt;/figure&gt;
&lt;p&gt;This is because dark colors are in general less distinguishable and more muddy. Look at Hue scale as we move brightness down:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/brightness_hue@2x.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Basically, in the dark part of the spectrum, you just get fewer colors to play with. There’s no “dark yellow” or good-looking “dark teal”.&lt;/p&gt;
&lt;p&gt;Nothing can be done here. There are no magic colors hiding somewhere that have both good contrast on a white background and look good at the same time. By choosing a light theme, you are dooming yourself to a very limited, bad-looking, barely distinguishable set of dark colors.&lt;/p&gt;
&lt;p&gt;So it makes sense. Dark themes do look better. Or rather: light ones can’t look good. Science ¯\_(ツ)_/¯&lt;/p&gt;
&lt;p&gt;But!&lt;/p&gt;
&lt;p&gt;But.&lt;/p&gt;
&lt;p&gt;There is one trick you can do, that I don’t see a lot of. Use background colors! Compare:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/bg_highlight@2x.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;The first one has nice colors, but the contrast is too low: letters become hard to read.&lt;/p&gt;
&lt;p&gt;The second one has good contrast, but you can barely see colors.&lt;/p&gt;
&lt;p&gt;The last one has &lt;em&gt;both&lt;/em&gt;: high contrast and clean, vibrant colors. Lighter colors are readable even on a white background since they fill a lot more area. Text is the same brightness as in the second example, yet it gives the impression of clearer color. It’s all upside, really.&lt;/p&gt;
&lt;p&gt;UI designers know about this trick for a while, but I rarely see it applied in code editors:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/badge.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;If your editor supports choosing background color, give it a try. It might open light themes for you.&lt;/p&gt;
&lt;h1 id=&quot;bold-and-italics&quot;&gt;Bold and italics&lt;/h1&gt;
&lt;p&gt;Don’t use. This goes into the same category as too many colors. It’s just another way to highlight something, and you don’t need too many, because you can’t highlight everything.&lt;/p&gt;
&lt;p&gt;In theory, you might try to &lt;em&gt;replace&lt;/em&gt; colors with typography. Would that work? I don’t know. I haven’t seen any examples.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/typography.png&quot; /&gt;Using italics and bold instead of colors&lt;/figure&gt;
&lt;h1 id=&quot;myth-of-number-based-perfection&quot;&gt;Myth of number-based perfection&lt;/h1&gt;
&lt;p&gt;Some themes pay too much attention to be scientifically uniform. Like, all colors have the same exact lightness, and hues are distributed evenly on a circle.&lt;/p&gt;
&lt;p&gt;This could be nice (to know if you have OCD), but in practice, it doesn’t work as well as it sounds:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/balanced.png&quot; /&gt;OkLab l=0.7473 c=0.1253 h=0, 45, 90, 135, 180, 225, 270, 315&lt;/figure&gt;
&lt;p&gt;The idea of highlighting is to make things stand out. If you make all colors the same lightness and chroma, they will look very similar to each other, and it’ll be hard to tell them apart.&lt;/p&gt;
&lt;p&gt;Our eyes are way more sensitive to differences in lightness than in color, and we should use it, not try to negate it.&lt;/p&gt;
&lt;h1 id=&quot;lets-design-a-color-theme-together&quot;&gt;Let’s design a color theme together&lt;/h1&gt;
&lt;p&gt;Let’s apply these principles step by step and see where it leads us. We start with the theme from the start of this post:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi00.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;First, let’s remove highlighting from language keywords and re-introduce base text color:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi10.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Next, we remove color from variable usage:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi20.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;and from function/method invocation:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi30.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;The thinking is that your code is mostly references to variables and method invocation. If we highlight those, we’ll have to highlight more than 75% of your code.&lt;/p&gt;
&lt;p&gt;Notice that we’ve kept variable declarations. These are not as ubiquitous and help you quickly answer a common question: where does thing thing come from?&lt;/p&gt;
&lt;p&gt;Next, let’s tone down punctuation:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi40.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;I prefer to dim it a little bit because it helps names stand out more. Names alone can give you the general idea of what’s going on, and the exact configuration of brackets is rarely equally important.&lt;/p&gt;
&lt;p&gt;But you might roll with base color punctuation, too:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi40_alt.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Okay, getting close. Let’s highlight comments:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi50.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;We don’t use red here because you usually need it for squiggly lines and errors.&lt;/p&gt;
&lt;p&gt;This is still one color too many, so I unify numbers and strings to both use green:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi60.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Finally, let’s rotate colors a bit. We want to respect nesting logic, so function declarations should be brighter (yellow) than variable declarations (blue).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi70.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Compare with what we started:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/syntax-highlighting/dyi00.png&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;In my opinion, we got a much more workable color theme: it’s easier on the eyes and helps you find stuff faster.&lt;/p&gt;
&lt;h1 id=&quot;shameless-plug-time&quot;&gt;Shameless plug time&lt;/h1&gt;
&lt;p&gt;I’ve been applying these principles for &lt;a href=&quot;https://github.com/tonsky/vscode-theme-alabaster/commit/5c840f5fb57e5cd0dce93ac8c450495bdb0a2658&quot;&gt;about 8 years now&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I call this theme Alabaster and I’ve built it a couple of times for the editors I used:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tonsky/vscode-theme-alabaster&quot;&gt;VS Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tonsky/intellij-alabaster&quot;&gt;JetBrains IDEs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tonsky/sublime-scheme-alabaster&quot;&gt;Sublime Text&lt;/a&gt; (&lt;a href=&quot;https://github.com/tonsky/clojure-sublimed/?tab=readme-ov-file#color-scheme&quot;&gt;twice&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s also been ported to many other editors and terminals; the most complete list is &lt;a href=&quot;https://github.com/tonsky/sublime-scheme-alabaster?tab=readme-ov-file#variations-1&quot;&gt;probably here&lt;/a&gt;. If your editor is not on the list, try searching for it by name—it might be built-in already! I always wondered where these color themes come from, and now I became an author of one (and I still don’t know).&lt;/p&gt;
&lt;p&gt;Feel free to use Alabaster as is or build your own theme using the principles outlined in the article—either is fine by me.&lt;/p&gt;
&lt;p&gt;As for the principles themselves, they worked out fantastically for me. I’ve never wanted to go back, and just one look at any “traditional” color theme gives me a scare now.&lt;/p&gt;
&lt;p&gt;I &lt;em&gt;suspect&lt;/em&gt; that the only reason we don’t see more restrained color themes is that people never really thought about it. Well, this is your wake-up call. I hope this will inspire people to use color more deliberately and to change the default way we build and use color themes.&lt;/p&gt;</content>
		<author>
			<name>Nikita Prokopov</name>
			<email>niki@tonsky.me</email>
			<uri>https://tonsky.me/</uri>
		</author>
		<source>
			<title type="html">tonsky.me</title>
			<subtitle type="html">Nikita Prokopov’s blog</subtitle>
			<link rel="self" href="https://tonsky.me/atom.xml"/>
			<id>https://tonsky.me/</id>
		</source>
	</entry>

	<entry>
		<title type="html" xml:lang="en-US">Statistics made simple</title>
		<link href="https://tonsky.me/blog/clj-simple-stats/"/>
		<id>https://tonsky.me/blog/clj-simple-stats/</id>
		<updated>2026-06-28T21:53:35+00:00</updated>
		<content type="html" xml:lang="en-US">&lt;p&gt;I have a weird relationship with statistics: on one hand, I try not to look at it too often. Maybe once or twice a year. It’s because analytics is not actionable: what difference does it make if a thousand people saw my article or ten thousand?&lt;/p&gt;
&lt;p&gt;I mean, sure, you might try to guess people’s tastes and only write about what’s popular, but that will destroy your soul pretty quickly.&lt;/p&gt;
&lt;p&gt;On the other hand, I feel nervous when something is not accounted for, recorded, or saved for future reference. I might not need it now, but what if ten years later I change my mind?&lt;/p&gt;
&lt;p&gt;Seeing your readers also helps to know you are not writing into the void. So I really don’t need much, something very basic: the number of readers per day/per article, maybe, would be enough.&lt;/p&gt;
&lt;p&gt;Final piece of the puzzle: I self-host my web projects, and I use an old-fashioned web server instead of delegating that task to Nginx.&lt;/p&gt;
&lt;p&gt;Static sites are popular and for a good reason: they are fast, lightweight, and fulfil their function. I, on the other hand, might have an unfinished gestalt or two: I want to feel the full power of the computer when serving my web pages, to be able to do fun stuff that is beyond static pages. I need that freedom that comes with a full programming language at your disposal. I want to program my own web server (in Clojure, sorry everybody else).&lt;/p&gt;
&lt;h1 id=&quot;existing-options&quot;&gt;Existing options&lt;/h1&gt;
&lt;p&gt;All this led me on a quest for a statistics solution that would uniquely fit my needs. Google Analytics was out: bloated, not privacy-friendly, terrible UX, Google is evil, etc.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/clj-simple-stats/ga@2x.webp&quot; /&gt;What is going on?&lt;/figure&gt;
&lt;p&gt;Some other JS solution might’ve been possible, but still questionable: SaaS? Paid? Will they be around in 10 years? Self-host? Are their cookies GDPR-compliant? How to count RSS feeds?&lt;/p&gt;
&lt;p&gt;Nginx has access logs, so I tried server-side statistics that feed off those (namely, Goatcounter). Easy to set up, but then I needed to create domains for them, manage accounts, monitor the process, and it wasn’t even performant enough on my server/request volume!&lt;/p&gt;
&lt;h1 id=&quot;my-solution&quot;&gt;My solution&lt;/h1&gt;
&lt;p&gt;So I ended up building my own. You are welcome to join, if your constraints are similar to mine. This is how it looks:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/clj-simple-stats/screenshot@2x.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;It’s pretty basic, but does a few things that were important to me.&lt;/p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;Extremely easy to set up. And I mean it as a feature.&lt;/p&gt;
&lt;p&gt;Just add our middleware to your Ring stack and get everything automatically: collecting and reporting.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(def app
  (-&amp;gt; routes
    ...
    (ring.middleware.params/wrap-params)
    (ring.middleware.cookies/wrap-cookies)
    ...
    (clj-simple-stats.core/wrap-stats))) ;; &amp;lt;-- just add this&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s zero setup in the best sense: nothing to configure, nothing to monitor, minimal dependency. It starts to work immediately and doesn’t ask anything from you, ever.&lt;/p&gt;
&lt;p&gt;See, you already have your web server, why not reuse all the setup you did for it anyway?&lt;/p&gt;
&lt;h2 id=&quot;request-types&quot;&gt;Request types&lt;/h2&gt;
&lt;p&gt;We distinguish between request types. In my case, I am only interested in live people, so I count them separately from RSS feed requests, favicon requests, redirects, wrong URLs, and bots. Bots are particularly active these days. Gotta get that AI training data from somewhere.&lt;/p&gt;
&lt;p&gt;RSS feeds are live people in a sense, so extra work was done to count them properly. Same reader requesting &lt;code&gt;feed.xml&lt;/code&gt; 100 times in a day will only count as one request.&lt;/p&gt;
&lt;p&gt;Hosted RSS readers often report user count in User-Agent, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Feedly/1.0 (+http://www.feedly.com/fetcher.html; 457 subscribers; like FeedFetcher-Google)

Mozilla/5.0 (compatible; BazQux/2.4; +https://bazqux.com/fetcher; 6 subscribers)

Feedbin feed-id:1373711 - 142 subscribers&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My personal respect and thank you to everybody on this list. I see you.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/clj-simple-stats/readers@2x.webp&quot; /&gt;&lt;/figure&gt;
&lt;h2 id=&quot;graphs&quot;&gt;Graphs&lt;/h2&gt;
&lt;p&gt;Visualization is important, and so is choosing the correct graph type. This is wrong:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/clj-simple-stats/goat@2x.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Continuous line suggests interpolation. It reads like between 1 visit at 5am and 11 visits at 6am there were points with 2, 3, 5, 9 visits in between. Maybe 5.5 visits even! That is not the case.&lt;/p&gt;
&lt;p&gt;This is how a semantically correct version of that graph should look:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://tonsky.me/blog/clj-simple-stats/graph@2x.webp&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Some attention was also paid to having reasonable labels on axes. You won’t see something like 117, 234, 10875. We always choose round numbers appropriate to the scale: 100, 200, 500, 1K etc.&lt;/p&gt;
&lt;p&gt;Goes without saying that all graphs have the same vertical scale and syncrhonized horizontal scroll.&lt;/p&gt;
&lt;h2 id=&quot;insights&quot;&gt;Insights&lt;/h2&gt;
&lt;p&gt;We don’t offer much (as I don’t need much), but you can narrow reports down by page, query, referrer, user agent, and any date slice.&lt;/p&gt;
&lt;h2 id=&quot;not-implemented-yet&quot;&gt;Not implemented (yet)&lt;/h2&gt;
&lt;p&gt;It would be nice to have some insights into “What was this spike caused by?”&lt;/p&gt;
&lt;p&gt;Some basic breakdown by country would be nice. I do have IP addresses (for what they are worth), but I need a way to package GeoIP into some reasonable size (under 1 Mb, preferably; some loss of resolution is okay).&lt;/p&gt;
&lt;p&gt;Finally, one thing I am really interested in is “Who wrote about me?” I do have referrers, only question is how to separate signal from noise.&lt;/p&gt;
&lt;p&gt;Performance. DuckDB is a sport: it compresses data and runs column queries, so storing extra columns per row doesn’t affect query performance. Still, each dashboard hit is a query across the entire database, which at this moment (~3 years of data) sits around 600 MiB. I definitely need to look into building some pre-calculated aggregates.&lt;/p&gt;
&lt;p&gt;One day.&lt;/p&gt;
&lt;h2 id=&quot;how-to-get&quot;&gt;How to get&lt;/h2&gt;
&lt;p&gt;Head to &lt;a href=&quot;https://github.com/tonsky/clj-simple-stats&quot;&gt;github.com/tonsky/clj-simple-stats&lt;/a&gt; and follow the instructions:&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://github.com/tonsky/clj-simple-stats&quot;&gt;&lt;img src=&quot;https://tonsky.me/blog/clj-simple-stats/banner@2x.webp&quot; /&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;Let me know what you think! Is it usable to you? What could be improved?&lt;/p&gt;</content>
		<author>
			<name>Nikita Prokopov</name>
			<email>niki@tonsky.me</email>
			<uri>https://tonsky.me/</uri>
		</author>
		<source>
			<title type="html">tonsky.me</title>
			<subtitle type="html">Nikita Prokopov’s blog</subtitle>
			<link rel="self" href="https://tonsky.me/atom.xml"/>
			<id>https://tonsky.me/</id>
		</source>
	</entry>

	<entry>
		<title type="html" xml:lang="en-us">Clojure on Fennel part four: Parsing (again)</title>
		<link href="https://andreyor.st/posts/2026-06-29-clojure-on-fennel-part-four-parsing-again/"/>
		<id>https://andreyor.st/posts/2026-06-29-clojure-on-fennel-part-four-parsing-again/</id>
		<updated>2026-06-28T21:29:00+00:00</updated>
		<content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://andreyor.st/posts/2026-04-07-clojure-on-fennel-part-one-persistent-data-structures/&quot;&gt;part one: persistent data structures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://andreyor.st/posts/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/&quot;&gt;part two: immutable.fnl optimizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://andreyor.st/posts/2026-04-27-clojure-on-fennel-part-three-parsing/&quot;&gt;part three: parsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;part four: parsing (again)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Other than that, the parsing is complete, and we can look at the compiler part of the ClojureFnl project.
But that’s gonna be in the next post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sike!&lt;/p&gt;
&lt;p&gt;Wasn’t my intention to fake out like this again, but while working on the compiler, I had an important realization that led me to redesign the parser completely.
And it’s a bit of a shame, because I already had a decent part of the compiler working, being able to run the REPL and do various cool things with it.
But, better sooner than later, I guess.&lt;/p&gt;
&lt;p&gt;I had a good amount of the post about the compiler already written too, but now I’ll have to discard all of that.
So instead, I decided to talk about the new parser by explaining why the old one failed me.
And to do so, I’ll give you a brief look on how the compiler currently works with parsed code.&lt;/p&gt;
&lt;h2 id=&quot;old-parser-and-compiler&quot;&gt;Old parser and compiler&lt;/h2&gt;
&lt;p&gt;So, in the previous post, we’ve reached a point where the parser can read code into a tagged tree.
I spent a fair amount of time discussing how I wanted this specifically, and now it feels like I’m backpedalling, but tagged tree is not the way forward.
Here’s the idea.&lt;/p&gt;
&lt;p&gt;Currently, we’re reading this expression &lt;code&gt;(+ 1 2)&lt;/code&gt; into:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;+&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:number&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;1&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:number&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;2&quot;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The compiler then walks this tree, taking the first value of each node to decide what to do.
First, it sees &lt;code&gt;:code&lt;/code&gt; - that’s just an entry point, containing multiple top-level expressions.
Next, it sees &lt;code&gt;:list&lt;/code&gt; and dispatches to a function dedicated to compiling lists.&lt;/p&gt;
&lt;p&gt;This function checks the first element of the list, sees that it is a &lt;code&gt;:symbol&lt;/code&gt;, and checks whether this symbol is somehow special.
While &lt;code&gt;+&lt;/code&gt; doesn’t look that special, it is to the compiler, because Clojure’s &lt;code&gt;+&lt;/code&gt; and Fennel’s &lt;code&gt;+&lt;/code&gt; are not the same thing.
In Clojure, &lt;code&gt;+&lt;/code&gt; is a function, in Fennel, it is a special.&lt;/p&gt;
&lt;p&gt;So the compiler replaces &lt;code&gt;+&lt;/code&gt; with &lt;code&gt;clojure_core_ns.add&lt;/code&gt;, and then proceeds over the rest of the forms in the list, recursively calling itself over each.
In the end, we get this, written into a string builder:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure_core_ns.add&lt;/span&gt; 1 2)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a somewhat simplified explanation, because I’m omitting scope resolution, symbol shadowing, and other such things that the compiler currently tracks.
For actual specials, like Clojure’s &lt;code&gt;let*&lt;/code&gt;, the story is a bit different too.
The compiler has a dedicated &lt;code&gt;compile-special&lt;/code&gt; function for all Clojure specials provided by &lt;code&gt;cljlib&lt;/code&gt; as Fennel macros.
And that’s where things started to go haywire.&lt;/p&gt;
&lt;p&gt;Look at this code:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;font-weight: bold;&quot;&gt;def &lt;/span&gt;^&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:private&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;foo&lt;/span&gt; 42)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It’s read like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;def&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:private&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;foo&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:number&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;42&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here’s a problem - the parser reads metadata &lt;code&gt;^:private&lt;/code&gt;, and metadata in Clojure is attached to symbols, so the grammar I use reads the next value after the metadata and wraps it into a single node:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:private&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;foo&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The compiler, while compiling this list, sees &lt;code&gt;def&lt;/code&gt; as the first symbol and enters &lt;code&gt;compile-special.def&lt;/code&gt;.
But &lt;code&gt;def&lt;/code&gt; expects a symbol to bind the value to, while here we have a different node type, called &lt;code&gt;:metadata&lt;/code&gt;.
So my compiler had to account for all cases where metadata can appear - and it wasn’t easy to do.&lt;/p&gt;
&lt;p&gt;I tried to side-step this by always expecting metadata, because it can appear almost anywhere, and discarding it, because Fennel’s concept of metadata is a bit different.
In which I mostly succeeded.
But this wasn’t the only problematic thing to support.&lt;/p&gt;
&lt;p&gt;Here’s another example, now from &lt;a href=&quot;https://github.com/borkdude/edamame/blob/cd392b2246acced63099ae605f7580942a9cd462/src/edamame/impl/parser.cljc#L92&quot; target=&quot;_blank&quot;&gt;Edamame&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;defn- &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;read-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;Read in a single logical token from the reader&quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  ^&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;String&lt;/span&gt; [#&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;?&lt;/span&gt;(&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:clj&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;rdr&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:cljs&lt;/span&gt; ^&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;not-native&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;rdr&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:cljr&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;rdr&lt;/span&gt;) &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;_kind&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;initch&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  (&lt;span style=&quot;font-weight: bold;&quot;&gt;loop &lt;/span&gt;[&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;sb&lt;/span&gt; #&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;?&lt;/span&gt;(&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:clj&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;StringBuilder.&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;               &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:cljs&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;StringBuffer.&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;               &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:cljr&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;StringBuilder.&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;ch&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;initch&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    (&lt;span style=&quot;font-weight: bold;&quot;&gt;if &lt;/span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;or &lt;/span&gt;(&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;whitespace?&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;ch&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;            (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;macro-terminating?&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;ch&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;            (&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;nil? &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;ch&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      (&lt;span style=&quot;font-weight: bold;&quot;&gt;do &lt;/span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;when &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;ch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;            (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;r/unread&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;rdr&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;ch&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;          (&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;str &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;sb&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;recur&lt;/span&gt; #&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;?&lt;/span&gt;(&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:clj&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;.append&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;sb&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;ch&lt;/span&gt;) &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:cljs&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;.append&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;sb&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;ch&lt;/span&gt;) &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:cljr&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;.Append&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;sb&lt;/span&gt; (&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;str &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;ch&lt;/span&gt;))) (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;r/read-char&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;rdr&lt;/span&gt;)))))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It reads into this tagged tree:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;defn-&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;read-token&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n  &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:string&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\&quot;Read in a single logical token from the reader\&quot;&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n  &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;String&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:conditional&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:clj&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;rdr&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:cljs&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;not-native&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;rdr&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:cljr&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;rdr&quot;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;_kind&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;initch&quot;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n  &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;loop&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;sb&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:conditional&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:clj&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;StringBuilder.&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n               &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:cljs&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;StringBuffer.&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n               &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:cljr&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;StringBuilder.&quot;&lt;/span&gt;]]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n         &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;ch&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;initch&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n    &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;if&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;or&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;whitespace?&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;ch&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n            &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;macro-terminating?&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;ch&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n            &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;nil?&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;ch&quot;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n      &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;do&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;when&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;ch&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n            &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;r/unread&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;rdr&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;ch&quot;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n          &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;str&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;sb&quot;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;\n      &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;recur&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:conditional&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:clj&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;.append&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;sb&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;ch&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:cljs&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;.append&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;sb&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;ch&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:cljr&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;      [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;.Append&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;sb&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;str&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;ch&quot;&lt;/span&gt;]]]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;    [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;r/read-char&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;rdr&quot;&lt;/span&gt;]]]]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yes, it’s abysmal, but bear with me.
I had to work with this, after all, thinking that this is a blessing.&lt;/p&gt;
&lt;p&gt;The compiler sees &lt;code&gt;defn-&lt;/code&gt; and enters the &lt;code&gt;compile-special.defn-&lt;/code&gt; function.
Defn expects a function name, then a vector for its arguments.
Here, instead of the vector, we have &lt;code&gt;metadata&lt;/code&gt; node again.
This is fine, since I just mentioned above that I managed to sidestep this problem.&lt;/p&gt;
&lt;p&gt;Since this is an arglist, we need to compile it in a special way, adding its symbols to the function scope, etc.
However, instead of arguments, we see the &lt;code&gt;conditional&lt;/code&gt; node:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:conditional&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:clj&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;rdr&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:cljs&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;not-native&quot;&lt;/span&gt;]] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;rdr&quot;&lt;/span&gt;]] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;:cljr&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;rdr&quot;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;_kind&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;initch&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I didn’t think it was allowed in Clojure to use conditional reading inside forms like this.
But apparently it’s OK, and my compiler failed to deal with it.&lt;/p&gt;
&lt;p&gt;So now, any node can not only be a &lt;code&gt;metadata&lt;/code&gt; node, but also a &lt;code&gt;conditional&lt;/code&gt; node.
This complicates things, but at this point I’m still thinking that I can persevere.&lt;/p&gt;
&lt;p&gt;So I did.
I added support for almost all specials provided by &lt;code&gt;cljlib&lt;/code&gt;.
The main thing that was left to do were macros.
And then it hit me:&lt;/p&gt;
&lt;p&gt;I can’t do macros like that!&lt;/p&gt;
&lt;p&gt;Why?
Why, of course, because macros don’t emit tagged trees that my compiler understands.
They emit code!&lt;/p&gt;
&lt;p&gt;Here’s a simple macro:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;defmacro &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;unless&lt;/span&gt; [&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;test &lt;/span&gt;&amp;amp; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;body&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  `(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;when &lt;/span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;not &lt;/span&gt;~&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;test&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     ~@&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;body&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can parse it:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;defmacro&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;unless&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;test&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;&amp;amp;&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;body&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:backtick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;when&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;not&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:unquote&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;test&quot;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;   [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:unquote-splicing&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;body&quot;&lt;/span&gt;]]]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can probably even compile it to something that we could then call during the compiler step.
However, what will &lt;code&gt;(unless true (println 42))&lt;/code&gt; thing return?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;(when (not true) (println 42))&lt;/code&gt;, of course.
It’s a Lisp macro, what else did you expect?&lt;/p&gt;
&lt;p&gt;But what does the compiler expect?&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;when&quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;not&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;true&quot;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:list&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:symbol&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;println&quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:whitespace&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot; &quot;&lt;/span&gt;] [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:number&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;42&quot;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oh, it wants this.&lt;/p&gt;
&lt;p&gt;I have to convert macro’s output into a string, parse it, and feed it into the compiler if I want macros to work at all.
And that’s BAD.
If only I realized this sooner!&lt;/p&gt;
&lt;p&gt;This was the final nail in the coffin of the tagged tree approach for my project.
I knew I had to rewrite the parser to emit actual data structures that I’ll be able to emit from macros as well, and compile them.&lt;/p&gt;
&lt;h2 id=&quot;new-reader&quot;&gt;New &lt;del&gt;parser&lt;/del&gt; reader&lt;/h2&gt;
&lt;p&gt;So I decided that I need a proper lisp reader that will produce data structures:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; Welcome to Fennel Proto REPL 0.6.4-dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; Fennel version: 1.7.0-dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; Lua version: PUC Lua 5.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; Work directory: ~/Projects/fennel/ClojureFnl/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;local &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;reader&lt;/span&gt; (&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;require &lt;/span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:impl.reader&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;reader.read-string&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;(def ^:private foo 42)&quot;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;foo&lt;/span&gt; 42)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;local &lt;/span&gt;{&lt;span style=&quot;font-weight: bold;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;meta&lt;/span&gt; &lt;span style=&quot;font-weight: bold;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;second&lt;/span&gt;} (&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;require &lt;/span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:clojure.core&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;meta&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;second&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;reader.read-string&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;(def ^:private foo 42)&quot;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;{&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:private&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;true&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As can be seen, the new &lt;code&gt;reader&lt;/code&gt; module provides the &lt;code&gt;read-string&lt;/code&gt; function that produces data structures.
Notably, there are no longer any metadata nodes - metadata is assigned to the symbol &lt;code&gt;foo&lt;/code&gt; in this case.&lt;/p&gt;
&lt;p&gt;Same goes for the reader conditionals:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;reader.read-string&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;[0 #?(:clj 1 :cljfnl 2) #?@(:clj [2 3] :cljfnl [3 4]) 5]&quot;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[0 2 3 4 5]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These are now correctly spliced at read time.&lt;/p&gt;
&lt;p&gt;This also solved macro problems for the most part:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;reader.read-string&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;`(+ ~x ~@y ~z)&quot;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/seq&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/concat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/list&lt;/span&gt; (&lt;span style=&quot;font-weight: bold;&quot;&gt;quote &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/+&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/list&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/list&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;z&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And it matches what Clojure itself does:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;Clojure&lt;/span&gt; 1.12&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;user=&amp;gt;&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;read-string&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;`(+ ~x ~@y ~z)&quot;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/seq&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/concat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/list&lt;/span&gt; (&lt;span style=&quot;font-weight: bold;&quot;&gt;quote &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/+&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/list&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;clojure.core/list&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;z&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Due to this change, the compiler doesn’t have to know about syntax quote (&lt;code&gt;`&lt;/code&gt;) at all, and thus macros will correctly return these kinds of lists.&lt;/p&gt;
&lt;p&gt;So, now we can read Clojure code into data structures.
Previously, the parser returned &lt;code&gt;[:code A B ...]&lt;/code&gt; kind of result, where &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, and the rest are all top-level forms.
New reader also does this, but returns them as a list:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;reader.read-string-all&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;1 \&quot;str\&quot; :keyword ::namespaced #:map{:key :val}&quot;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(1 &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;str&quot;&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:user/namespaced&lt;/span&gt; {&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:map/key&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:val&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, there’s a problem.&lt;/p&gt;
&lt;p&gt;Consider the &lt;code&gt;::namespaced&lt;/code&gt; keyword.
Both my reader and Clojure read it as &lt;code&gt;:user/namespaced&lt;/code&gt;, but this is because the default namespace in the REPL is &lt;code&gt;user&lt;/code&gt;.
For my parser, it’s mostly an arbitrary choice because it doesn’t know anything about runtime.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;Clojure&lt;/span&gt; 1.12&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;user=&amp;gt;&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;read-string&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;::x&quot;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:user/x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;user=&amp;gt;&lt;/span&gt; (&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;ns &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;foo&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;foo=&amp;gt;&lt;/span&gt; (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;read-string&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;::x&quot;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:foo/x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And when compiling a file, the file might have an &lt;code&gt;ns&lt;/code&gt; declaration, or even multiple of them:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;ns &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;foo&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;println &lt;/span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;::x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;ns &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;bar&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;println &lt;/span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;::x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we were to run this code, we would see &lt;code&gt;:foo/x&lt;/code&gt; then &lt;code&gt;:bar/x&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My parser currently reads all input, meaning it transforms all top-level forms from text to data.
However, this also means that it doesn’t understand anything about namespaces.&lt;/p&gt;
&lt;p&gt;Previously, this was fine because the compiler handled this - the tagged tree didn’t resolve anything.
Currently, however, we need to construct namespaced keys, and we need to know the namespace.
But it’s not possible unless we parse the input expression by expression, instead of all at once.&lt;/p&gt;
&lt;p&gt;Hence, I had to update the grammar so it could parse expression by expression.
This way, I could read source code form by form, and compile each form separately.
And if the compiler encounters an &lt;code&gt;ns&lt;/code&gt; declaration, it could update some state, so the reader would know how to read namespaced keywords, and other things that may include namespaces.&lt;/p&gt;
&lt;p&gt;But we still need to pass this state to the reader.
And my reader is based on a PEG grammar.&lt;/p&gt;
&lt;p&gt;Luckily for me, the &lt;code&gt;lpeg&lt;/code&gt; library I use has a &lt;code&gt;Carg&lt;/code&gt; function that can pass additional arguments into the PEG parser.
This way I can write my transformation function &lt;code&gt;T&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-fennel&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;fn &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;T&lt;/span&gt; [&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;tag&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;patt&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;  (&lt;span style=&quot;font-weight: bold;&quot;&gt;/ &lt;/span&gt;(&lt;span style=&quot;font-weight: bold;&quot;&gt;* &lt;/span&gt;(&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;Ct&lt;/span&gt; (&lt;span style=&quot;font-weight: bold;&quot;&gt;* &lt;/span&gt;(&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;Cc&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;tag&lt;/span&gt;) &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;patt&lt;/span&gt;)) (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;Carg&lt;/span&gt; 1))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;     (&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;fn &lt;/span&gt;[&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;node&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;state&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;       (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;case&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;node&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; ----8&amp;lt;----&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:macro-keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;bo&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;data&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;read-macro-keyword&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;data&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;bo&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:conditional&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;data&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;read-conditional&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;data&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:conditional-splicing&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;data&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         (&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;conditional-splicing&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;data&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; ----8&amp;lt;----&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;         &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;_&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;_&lt;/span&gt;))))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This state can be passed anew after reading each expression, or mutated in place - either way, the reader now knows how to access state.
The reader itself is still stateless.&lt;/p&gt;
&lt;p&gt;The reader now supports all Clojure syntax and produces data structures:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-clojure&quot;&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; Clojure                                          ;; Fennel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;1                                                   &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;0.5                                                 &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; 0.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;1&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;e10&lt;/span&gt;                                                &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; 10000000000.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;1&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;/2&lt;/span&gt;                                                 &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; 1/2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;1&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;N&lt;/span&gt;                                                  &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; 1N&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;1.33333333333333333333333333333333&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;M&lt;/span&gt;                 &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; 1.33333333333333333333333333333333M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;0&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;xFF&lt;/span&gt;                                                &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; 255&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;017                                                 &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; 15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;16&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;rFF&lt;/span&gt;                                               &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; 255&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;\c&lt;/span&gt;                                                  &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; &quot;c&quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;string&quot;&lt;/span&gt;                                            &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; &quot;string&quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;\u&lt;/span&gt;0041                                              &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; &quot;A&quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;\o&lt;/span&gt;101                                               &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; &quot;A&quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:keyword&lt;/span&gt;                                            &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; :keyword&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:namespaced/keyword&lt;/span&gt;                                 &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; :namespaced/keyword&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;::auto-keyword&lt;/span&gt;                                      &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; :user/auto-keyword&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;symbol &lt;/span&gt;                                             &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; symbol&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;namespaced/symbol&lt;/span&gt;                                   &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; namespaced/symbol&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;#&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;'var&lt;/span&gt;                                               &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; (var var)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;'quoted-symbol&lt;/span&gt;                                      &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; (quote quoted-symbol)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;(&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;list&lt;/span&gt;)                                              &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; (list)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;vector&lt;/span&gt;]                                            &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; [vector]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;{&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;hash&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;map&lt;/span&gt;}                                          &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; {hash map}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;#&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:namespaced&lt;/span&gt;{&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:map&lt;/span&gt; 42}                               &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; {:namespaced/map 42}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;#&lt;span&gt;::&lt;/span&gt;{&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:auto&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:namespaced&lt;/span&gt;}                              &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; {:user/auto :namespaced}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;#{&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;hash&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;set&lt;/span&gt;}                                         &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; #{set hash}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;#&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;regexp&quot;&lt;/span&gt;                                           &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; &quot;regexp&quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;@&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;dereferencing&lt;/span&gt;                                      &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; (clojure.core/deref dereferencing)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;#(&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;+ &lt;/span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;%1&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;%2&lt;/span&gt;)                                          &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; (fn* [p__0__ p__1__] (+ p__0__ p__1__))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;^&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:meta&lt;/span&gt; &lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;data&lt;/span&gt;                                         &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;#&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;_discard&lt;/span&gt;                                           &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;[#&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;?&lt;/span&gt;(&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:cljfnl&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;reader&quot;&lt;/span&gt;) #&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;?&lt;/span&gt;@(&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:cljfnl&lt;/span&gt; [&lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;:conditionals&lt;/span&gt;])] &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; [&quot;reader&quot; :conditionals]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;nil&lt;/span&gt;                                                 &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;true&lt;/span&gt;                                                &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;false&lt;/span&gt;                                               &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;##&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;NaN&lt;/span&gt;                                               &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; .nan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;##&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;Inf&lt;/span&gt;                                               &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; .inf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;#&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;inst&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;2022&quot;&lt;/span&gt;                                        &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; #inst &quot;2022-01-01T00:00:00.000-00:00&quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display: flex;&quot;&gt;&lt;span&gt;#&lt;span style=&quot;color: #666; font-weight: bold; font-style: italic;&quot;&gt;uuid&lt;/span&gt; &lt;span style=&quot;color: #666; font-style: italic;&quot;&gt;&quot;c6e8050a-789b-4305-b518-8f0f7c31da7a&quot;&lt;/span&gt;        &lt;span style=&quot;color: #888; font-style: italic;&quot;&gt;;; #uuid &quot;c6e8050a-789b-4305-b518-8f0f7c31da7a&quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note about data structures: even though we’re working in Fennel, the produced maps, vectors and lists are custom data structures that are implemented in the &lt;code&gt;cljlib&lt;/code&gt; library.
These are implementations of persistent data structures I’ve talked about in &lt;a href=&quot;https://andreyor.st/posts/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/&quot;&gt;part 2&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;further-work&quot;&gt;Further work&lt;/h2&gt;
&lt;p&gt;With that, I can work on the compiler, now for real.
It’s a shame that I basically have to do all the work on the compiler again from scratch, because the underlying data has changed so much, but this simplifies a lot of things, so I’m OK with that.&lt;/p&gt;
&lt;p&gt;One thing that the reader still doesn’t support yet is read-time evaluation with &lt;code&gt;#=expr&lt;/code&gt;.
This requires a working compiler, and I’ll have to first implement it, then integrate it back into the reader.&lt;/p&gt;
&lt;p&gt;Another thing I was thinking about was to drop the LPEG parser altogether.
Maybe, when I have the compiler working and have support for all Clojure runtime semantics in place, I’ll make a fork of the Edamame parser and replace the current reader with it, adding support for ClojureFnl via reader conditionals.
This will remove a C library dependency, which is a good thing for distribution.
I still have another C library for arbitrary-precision numbers, but it can be worked around.&lt;/p&gt;
&lt;p&gt;But, lesson learned - when implementing a lisp, even if it is just a transpiler, don’t try to cut corners, and make a proper reader.&lt;/p&gt;
&lt;p&gt;Next post, &lt;strong&gt;for sure&lt;/strong&gt;, will be about the compiler!&lt;/p&gt;
&lt;div class=&quot;comment-link&quot;&gt; &lt;a href=&quot;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Clojure on Fennel part four: Parsing (again)&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</content>
		<author>
			<name>Andrey Listopadov</name>
			<uri>https://andreyor.st/tags/clojure/</uri>
		</author>
		<source>
			<title type="html">Andrey Listopadov</title>
			<subtitle type="html">Posts tagged 'clojure' by Andrey Listopadov</subtitle>
			<link rel="self" href="https://andreyor.st/tags/clojure/feed.xml"/>
			<id>https://andreyor.st/tags/clojure/</id>
			<rights type="html">Andrey Listopadov 2020-2026 - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</rights>
		</source>
	</entry>

</feed>
