<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss"
        xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
        xmlns:media="http://search.yahoo.com/mrss/"><channel>
<title>Entropic Thoughts</title>
<atom:link href="https://entropicthoughts.com/feed.xml" rel="self" type="application/rss+xml" />
<link>https://entropicthoughts.com/</link>
<description><![CDATA[]]></description>
<language>en</language>
<pubDate>Tue, 10 Nov 2026 11:17:08 +0100</pubDate>
<lastBuildDate>Tue, 10 Nov 2026 11:17:08 +0100</lastBuildDate>
<generator>Emacs 31.1 Org-mode 10.0-pre</generator>
<webMaster>a@xkqr.org (kqr)</webMaster>
<image>
<url>https://entropicthoughts.com/image/favicon/favicon-48x48.png</url>
<title>Entropic Thoughts</title>
<link>https://entropicthoughts.com/</link>
</image>

<item>
<title>Claude Code token cost explorer</title>
<link>https://entropicthoughts.com/claude-code-token-cost-explorer</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/claude-code-token-cost-explorer</guid>
<pubDate>Tue, 10 Nov 2026 00:00:00 +0100</pubDate>
<category><![CDATA[ai]]></category>
<description><![CDATA[<p>
I am experimenting with involving subagents more in my LLM coding agent
workflow. I discovered that subagents make it more difficult to diagnose token
usage in Claude Code,
because the regular <code>/context</code> command only breaks down context usage for the
main conversation, not for the subagents.
</p>



<p>
I tried some existing tools like <code>ccusage</code> and <code>claude-agent-ledger</code>, but they
weren&rsquo;t able to give me the sort of breakdown into specifics I needed to
optimise my usage. Thus, I shamelessly had Claude Code whip up a tool that gives
me a better view of its token usage. This is vibecoded in the true sense of the
word – I have barely looked at the code – but I&rsquo;m okay with that becuase it is
not critical infrastructure and it does what I need it to.
</p>



<p>
If you want to know how to get more out of your Claude Code sessions without
paying more for it, here&rsquo;s how you can find out.
</p>



<p>
First, open the context cost explorer in your browser. Everything happens
locally on your machine, so none of your transcript data leaves your computer.
If you&rsquo;re the paranoid kind, you can verify by opening the network tab in your
browser and reading in a non-sensitive session and seeing that nothing funny
happens. Then follow the steps below, or use your own
analytical ability.
</p>


<p><a href="https://entropicthoughts.com/claude-code-token-cost-explorer">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Runs scored in World Baseball Classic</title>
<link>https://entropicthoughts.com/sums-of-overdispersed-counts</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/sums-of-overdispersed-counts</guid>
<pubDate>Tue, 03 Nov 2026 00:00:00 +0100</pubDate>
<category><![CDATA[statistics]]></category>
<category><![CDATA[forecasting]]></category>
<description><![CDATA[<p>
In the Spring 2026 edition of the Metaculus Cup one question concerned the
number of runs in the knockout stage of the World Baseball Classic (WBC)
in 2026.
</p>



<p>
The community predicted a mean of just under 70 runs, with a spread of 60–80. My
prediction was 60 runs, with a spread of 42–80. The actual number ended up being
59 runs – nearly exactly what I predicted. Let&rsquo;s see how that was done.
</p>



<p>
First, there&rsquo;s a sloppy way of making a quick guess. It&rsquo;s not very interesting,
but it&rsquo;s a good baseline. Once we have that, we&rsquo;ll dig into modeling counts on a
more granular level, and that&rsquo;s when it gets interesting. Those techniques can
be used for all sorts of counts, not just sports prediction!
</p>



<p>
I don&rsquo;t know anything about baseball. I don&rsquo;t even know what a run is, so I had
my research assistant count how many runs were in a few of the previous WBCs.
</p>


<p><a href="https://entropicthoughts.com/sums-of-overdispersed-counts">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Extremely brief history of PDP computers</title>
<link>https://entropicthoughts.com/history-of-pdp-computers</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/history-of-pdp-computers</guid>
<pubDate>Thu, 29 Oct 2026 00:00:00 +0100</pubDate>
<category><![CDATA[product_development]]></category>
<category><![CDATA[organisations]]></category>
<category><![CDATA[unix]]></category>
<category><![CDATA[lisp]]></category>
<description><![CDATA[<p>
I don&rsquo;t know why I started to put this together. These machines fascinate me,
because so much of early computing/hacker type culture happened on them. I
started writing a table, but realised a timeline would be more intuitive. In
this graph, the price is for a minimal configuration and adjusted for inflation.
The time is the memory cycle time, a decent proxy for speed.
</p>




<div id="orgff05818" class="figure">
<p>
</p>
</div>



<p>
Some notes:
</p>



<ul class="org-ul">
<li>The PDP-1 was created mostly as an experiment. At the time, industrial
control systems used either humans or complicated contraptions of logic boards
to make decisions. Some engineers at DEC wanted to see if there was a
customer base willing to use general-purpose computers instead.</li>

<li>The PDP-4 was a variant of PDP-1 that was a little slower and much
cheaper. It was not compatible with the PDP-1. The PDP-4 was upgraded with
new chip technology and became the PDP-7, which was then optimised further
for cost and speed and became the PDP-9.</li>

<li>The PDP-5 was created when they realised the PDP-1 and PDP-4 machines
were overkill for industrial control systems. By being smaller, it could be
much cheaper, at the cost of compatibility with PDP-1 and PDP-4. It was
still intended for roughly the same market. The chip upgrade turned it into
the PDP-8.</li>

<li>The PDP-6 was a completely different beast, aimed at a different market. It
supported having tonnes of directly-addressable memory, as well as memory
protection, so it could be used as a time-sharing system. The amount of memory
made it run Lisp well. With the chip upgrade, it became the PDP-10.</li>

<li>The PDP-11 was a new architecture again, intended to subsume all the
previous models, to avoid running three incompatible lines in parallel. There
were many variants of the PDP-11, with various performance characteristics
and corresponding price points. However, even its most powerful configuration
wasn&rsquo;t powerful enough to attract many PDP-10 users.</li>

<li>The VAX came much later, but it&rsquo;s included in the table anyway because it is
what eventually subsumed the PDP-10. The VAX was somewhat compatible with
the PDP-11, but they served different markets.</li>
</ul>


<p><a href="https://entropicthoughts.com/history-of-pdp-computers">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>MTL-style circuit simulator</title>
<link>https://entropicthoughts.com/mtl-style-circuit-simulator</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/mtl-style-circuit-simulator</guid>
<pubDate>Tue, 27 Oct 2026 00:00:00 +0100</pubDate>
<category><![CDATA[haskell]]></category>
<category><![CDATA[programming]]></category>
<description><![CDATA[<p>
After the first attempt at making a very side-effectful circuit simulator, and
then an ambitious attempt at making it pure, I was reminded of a middle ground,
using a pattern common in production Haskell code. It is called <i>MTL-style
effects</i>, and it is a specific type of <i>final tagless encoding</i>.
</p>



<p>
The idea is that we first find a set of operations that our circuit simulator
needs to support. Drawing this boundary well requires some experience and taste.
Then we encode that limited set of operations into a type class. Here&rsquo;s one way
to do it.
</p>



<label class="input">In[1]:</label><div class="org-src-container">
<pre class="src src-haskell"><code>class CircuitOperations m where
 make_wire :: m Wire
 get_signal :: Wire -&gt; m Signal
 set_signal :: Wire -&gt; Signal -&gt; m ()
 add_action :: Wire -&gt; m () -&gt; m ()
 after_delay :: Int -&gt; m () -&gt; m ()
 get_time :: m Int
 probe_output :: String -&gt; m ()
 propagate :: m ()
</code></pre>
</div>



<p>
Having read the previous article, these operations should sound familiar. I&rsquo;m
sure it&rsquo;s possible to create a better set of operations, but this will suffice
to illustrate the point.
</p>


<p><a href="https://entropicthoughts.com/mtl-style-circuit-simulator">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Script dependencies with Nix</title>
<link>https://entropicthoughts.com/python-dependencies-with-nix</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/python-dependencies-with-nix</guid>
<pubDate>Thu, 22 Oct 2026 00:00:00 +0200</pubDate>
<category><![CDATA[programming]]></category>
<category><![CDATA[python]]></category>
<category><![CDATA[nix]]></category>
<description><![CDATA[<p>
In the theme of NixOS enabling fearlessness, here&rsquo;s a way I use Nix on <i>other</i>
Linux distributions to sleep better at night: managing dependencies of simple
scripts. 
</p>



<p>
This way of using Nix is for everyone, not just those willing to try NixOS.
We&rsquo;ll look at it first for a Python script, and then see how it can be adapted
to any other language.
</p>



<p>
I wanted to create a script that would read the verbatim content of my articles
out loud to me with a synthesised voice. One of the more convenient ways to do this is with Python, where
I&rsquo;ve heard good things about Trafilatura for extracting the contents out of a
web page, filtering on types of content, etc.
</p>



<p>
But Trafilatura is a third-party Python dependency. Those suck because they
break your scripts when you so much as glance sideways at <code>pip</code>. The code also
ended up depending on <code>aplay</code>, which is a system dependency, making it two
different sources of future breakage. A couple of years ago, I would probably
have judged that to be too much maintenance, so I wouldn&rsquo;t have created the
script. Fortunately, now I know about Nix.
</p>


<p><a href="https://entropicthoughts.com/python-dependencies-with-nix">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>NixOS is making me less afraid</title>
<link>https://entropicthoughts.com/nixos-is-making-me-less-afraid</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/nixos-is-making-me-less-afraid</guid>
<pubDate>Tue, 20 Oct 2026 00:00:00 +0200</pubDate>
<category><![CDATA[nixos]]></category>
<category><![CDATA[sysadmin]]></category>
<description><![CDATA[<p>
Something funny happened a little while ago: I read about FreshRSS, and it
sounded nice, so I <i>installed it on a server and started using it</i>. That would
not have happened a year ago, but then again, a year ago I didn&rsquo;t have any NixOS
servers.
</p>



<p>
For the past few years, my RSS feeds have lived locally on my phone only.
There was a good idea behind that, but it never quite worked in practice. For one,
many things are easier to read on a larger screen. But it was also brittle to
trust a phone app to manage the read/unread status of thousands of articles
across a hundred feeds. What I really wanted was a server-side feed aggregator. This is software
that runs on a server, manages RSS feeds, and then I can read the RSS feeds
either through a web interface, or through client applications. I&rsquo;ve wanted that
for a while, but I&rsquo;ve never taken the step, because it would be yet another side
project to maintain the deployment of.
</p>



<p>
But now, with some NixOS experience, adding a working FreshRSS configuration
(including backups) took 9 lines of Nix code. That installation has
just hands-off worked for the past few months. If or when that server breaks, I
can spin up a new one running the exact same configuration, probably in
something like 15 minutes. When we can do something like that, there is
literally no reason to not just try the new software.
</p>



<p>
Contrast this against how FreshRSS is installed outside of NixOS. Then we&rsquo;d
</p>


<p><a href="https://entropicthoughts.com/nixos-is-making-me-less-afraid">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>The geometric model of maintenance</title>
<link>https://entropicthoughts.com/geometric-model-of-maintenance</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/geometric-model-of-maintenance</guid>
<pubDate>Tue, 13 Oct 2026 00:00:00 +0200</pubDate>
<category><![CDATA[statistics]]></category>
<category><![CDATA[programming]]></category>
<description><![CDATA[<p>
James Shore posits that each person-month spent writing code results in 10 days
of maintenance in the first year, then 5 days every year after that. Naïvely
interpreted, this is a <i>threshold</i>-based model for induced maintenance, and its
interesting because it&rsquo;s so simple.
</p>



<p>
If it could be shown to be true, that&rsquo;d be useful. The argument Shore is
making about the effects of robot-generated code does not depend on the specific
numbers, so Shore&rsquo;s numbers come from a hypothetical wisdom-of-the-crowds
aggregation rather than concrete data. But no hypotheticals are necessary here.
We have a thing called git.
</p>



<p>
Let&rsquo;s say we have a way of labeling each commit as either &ldquo;new development&rdquo; or
&ldquo;maintenance&rdquo;. Then we can count how many commits of each type there are in a
month. Possibly better, we can count how much
complexity has been handled in a month.
</p>



<p>
The following plot shows the cumulative effort that has gone into the
development of tmux since its beginning to the day I start writing this article.
</p>


<p><a href="https://entropicthoughts.com/geometric-model-of-maintenance">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Swemak: Colemak for Swedish</title>
<link>https://entropicthoughts.com/swemak</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/swemak</guid>
<pubDate>Thu, 08 Oct 2026 00:00:00 +0200</pubDate>
<category><![CDATA[life]]></category>
<description><![CDATA[<p>
I have written before about my Swedish Colemak hack, but I never explained what
makes it so good. This is a better image what it looks like:
</p>




<div id="org0fd3c2b" class="figure">
<p>
</p>
</div>



<p>
If you&rsquo;re struggling to find the difference against normal Colemak, look at the
cluster of five keys next to the return key.
</p>



<p>
This layout makes room for the three Swedish letters <code>Å</code>, <code>Ä</code>, and <code>Ö</code>, by only
changing five keys in total, and they have changed very thoughtfully:
</p>


<p><a href="https://entropicthoughts.com/swemak">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Predicting Peace</title>
<link>https://entropicthoughts.com/forecasting-peace</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/forecasting-peace</guid>
<pubDate>Tue, 06 Oct 2026 00:00:00 +0200</pubDate>
<category><![CDATA[forecasting]]></category>
<category><![CDATA[statistics]]></category>
<description><![CDATA[<p>
I have long struggled with predicting when peace will occur. Actually, I&rsquo;m
struggling with predicting anything relating to war, but let&rsquo;s focus on when peace arrives for now. There are two such
questions in the current ACX 2026 competition:
</p>



<ul class="org-ul">
<li>Will there be a cease-fire in Ukraine in 2026?</li>
<li>Will there be a cease-fire in Sudan in 2026?</li>
</ul>



<p>
When I entered my forecast for these questions, I did it based on Laplace&rsquo;s rule
of succession, i.e. as a function of their duration only. This is sort of
justified if war durations are heavy-tailed, which would mean that the longer
they go on for, the less likely it is that peace comes soon. But I don&rsquo;t
know if that is true.
</p>



<p>
Either way, this first, roughest forecast is
</p>


<p><a href="https://entropicthoughts.com/forecasting-peace">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Donating to open source</title>
<link>https://entropicthoughts.com/open-source-donation</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/open-source-donation</guid>
<pubDate>Tue, 29 Sep 2026 00:00:00 +0200</pubDate>
<category><![CDATA[life]]></category>
<description><![CDATA[<p>
I have decided to start donating a small amount of my fun money to open source
projects. After some research, the initial set of recipients are
</p>



<ul class="org-ul">
<li>Magit</li>
<li>Perl 5 Core Maintenance Fund</li>
<li>XMonad</li>
</ul>



<p>
These are important to me, they need more money, and I believe they can use
donations well. If you disagree, I&rsquo;d genuinely like to hear what mistakes I&rsquo;ve
made in the analysis below. I hope it shows that this is important to me.
</p>



<p>
(Which is also why it is so heart-warming when people reach out to tell me this
article prompted them to donate some of their own money. Thank you for being so
kind.)
</p>


<p><a href="https://entropicthoughts.com/open-source-donation">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Shrinking a Fedora virtual disk</title>
<link>https://entropicthoughts.com/shrinking-fedora-virtual-disk</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/shrinking-fedora-virtual-disk</guid>
<pubDate>Thu, 24 Sep 2026 00:00:00 +0200</pubDate>
<category><![CDATA[sysadmin]]></category>
<description><![CDATA[<p>
I have a weird setup for my laptop. In summary, I have been irrationally scared
of running Fedora on bare, dual-GPU metal, so I run it in a virtual machine
with Hyper-V on Windows as a hypervisor. The idea was to let Windows manage
GPUs and power management stuff like hibernation, and then use Fedora as my main
UI to the computer.
</p>



<p>
This has worked surprisingly well, but that&rsquo;s only because my expectations were
low to begin with. The VM can barely access the hardware of the underlying
machine: it can&rsquo;t use the GPU, it can&rsquo;t use the front-facing camera,
Linux starts thrashing weirdly when the VM runs out of memory, among other quirks I&rsquo;ve learned to work around. Thus, I&rsquo;ve wanted
to re-install on bare metal for a while, but! problem!
</p>



<p>
Come on a journey with me. It&rsquo;s got a hero with hubris, mistakes, tension, and a
cliffhanger.
</p>



<p>
During a transition period, I want to have two copies of the Fedora
installation, and I couldn&rsquo;t figure out how to fit it in:
</p>


<p><a href="https://entropicthoughts.com/shrinking-fedora-virtual-disk">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Cheaper LLM labelling</title>
<link>https://entropicthoughts.com/cheaper-llm-labeling</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/cheaper-llm-labeling</guid>
<pubDate>Tue, 22 Sep 2026 00:00:00 +0200</pubDate>
<category><![CDATA[statistics]]></category>
<category><![CDATA[ai]]></category>
<category><![CDATA[perl]]></category>
<category><![CDATA[programming]]></category>
<description><![CDATA[<p>
I have a small project where I needed to label commits as either &ldquo;maintenance&rdquo;
or &ldquo;new development&rdquo;. The obvious way to do it is with a cheap but relatively
capable LLM, like GPT 5.6 Luna. I tested it on a small set of commits and
manually verified its labelling, and it emitted the same label as I would have
for the entire test set. That was good enough for me to roll out on a wider scale.
</p>



<p>
If we have Simon Willison&rsquo;s <code>llm</code> CLI tool installed (and you should – it&rsquo;s
great!), we can call it in a pipe from Perl, and read its response. My script
had a loop that retried the request a few times, but without that bookkeeping,
the code for this is simple enough.
</p>



<label class="input">In[1]:</label><div class="org-src-container">
<pre class="src src-perl"><code>sub classify {
    my ($msg) = @_;
    my $pid = open2(
        my $output,
        my $prompt,
        'llm -m openrouter/openai/gpt-5.6-luna'
    );
    print $prompt prompt_template($msg);
    close $prompt;

    # Slurp the entire response and chomp off the
    # trailing newline.
    chomp(my $result = do { local $/; &lt;$output&gt; });
    waitpid($pid, 0);

    return $result;
}
</code></pre>
</div>



<p>
The drawback of this approach is that I wanted to label some 21,000 commits, and
this shells out to the LLM for every single commit. Since the prompt is
designed to contain mainly input tokens and not many output tokens (see Appendix
C for details), these calls are cheap in money, but the latency is no fun, at
around 1.5 seconds per call.
</p>


<p><a href="https://entropicthoughts.com/cheaper-llm-labeling">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Tag index for Org mode blog</title>
<link>https://entropicthoughts.com/tag-index-for-org-mode-blog</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/tag-index-for-org-mode-blog</guid>
<pubDate>Tue, 15 Sep 2026 00:00:00 +0200</pubDate>
<category><![CDATA[meta]]></category>
<category><![CDATA[programming]]></category>
<description><![CDATA[<p>
Since people keep asking how this blog is made, and I don&rsquo;t want to share the
awful, terrible code that it is taped together with, I&rsquo;ve decided to start
explaining parts of it piecewise. Generally, any time something breaks and I
have to fix it, I write down what I did and what it connects to.
</p>



<p>
The most recent issue was the stack limit being blown by a helper function
involved in generating the tag index. I had written it to be explicitly
recursive, which worked fine with a small-ish number of published articles, but
not anymore. The tag index creation follows a similar pattern to the RSS feed
generation detailed in the previous article.
</p>



<label class="input">In[1]:</label><div class="org-src-container">
<pre class="src src-elisp"><code>(defun tw-tag-index (project)
  "Generate a tag index page for PROJECT."
  (let* ((filename (concat (project-dir "src" "org") "tags.org"))
         ;; Get all publically listed files in this project.
         (files (seq-filter (lambda (entry) (plist-get entry :indexed))
                            (tw-get-all-files project)))
         ;; Convert to sorted alist of tag×article pairs.
         (all-tags (sort (tw-get-all-file-tags files)
                         (lambda (a b) (string&lt; (car a) (car b))))))
    (with-temp-file filename
      (insert (concat
               "#+TITLE: Tags\n"
               ;; Don't show author and publish date.
               "#+AUTHOR: \n"
               ;; Don't list in public indices.
               "#+FILETAGS: :page:\n\n"
               ;; Include table of contents.
               "#+TOC: headlines:1\n\n"
               ;; Then produce headings for each tag.
               (org-element-interpret-data
                   (tw-article-entry-headlines all-tags)))))
    ;; Return the filename of the created file. I don't
    ;; remember why. Maybe it's not necessary.
    filename))
</code></pre>
</div>



<p>
First we filter out the articles that should be indexed from a list of all files
in the project. We pass this through the <code>tw-get-all-file-tags</code> function, which
produces an <i>association list</i>, which is like a dictionary in Emacs
Lisp. Each entry in the association list is a tag paired with a list of articles
that have that tag. That list is sorted alphabetically on the tag name, and then
it gets converted into an Org document that can be exported.
</p>


<p><a href="https://entropicthoughts.com/tag-index-for-org-mode-blog">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Better AI code comment detector</title>
<link>https://entropicthoughts.com/better-ai-comment-classifier</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/better-ai-comment-classifier</guid>
<pubDate>Wed, 09 Sep 2026 00:00:00 +0200</pubDate>
<category><![CDATA[ai]]></category>
<category><![CDATA[statistics]]></category>
<category><![CDATA[programming]]></category>
<description><![CDATA[<p>
When I trained the previous AI comment classifier, I used partially personal
private data to do it, and built it on a somewhat shaky foundation, so I
couldn&rsquo;t share the code or data. I rebuilt it on public data and a better
foundation!
</p>



<p>
First off, you might want to try it out. Nothing you paste into that web page
leaves your browser, so you can safely try it with whatever you like. I have
invited some testers to try out an earlier version of it, and they had mainly
positive feedback to give.
</p>



<p>
We won&rsquo;t break down robot-isms the way we broke down Claude-isms in the previous
article, because in the UI of the new classifier you can just click any part
of the text being classified to see which features activate on that portion of
the text, and how they contribute to the overall judgement. Here&rsquo;s an example of
the expanded feature activation view.
</p>




<div id="orgc7fff93" class="figure">
<p>
</p>
</div>


<p><a href="https://entropicthoughts.com/better-ai-comment-classifier">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Claude comment detection</title>
<link>https://entropicthoughts.com/ai-comment-classifier</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/ai-comment-classifier</guid>
<pubDate>Tue, 01 Sep 2026 00:00:00 +0200</pubDate>
<category><![CDATA[programming]]></category>
<category><![CDATA[ai]]></category>
<category><![CDATA[statistics]]></category>
<description><![CDATA[<p>
Here&rsquo;s a comment I read in the code I was working on.
</p>



<blockquote>
<p>
Walk the entire collection to locate flagged clusters. Skip it altogether when
the cached, dirty-tracked flag count says there are none (the usual case):
nothing needs marking, so the walk is pure waste. When the count is stale (e.g.
right after an edit) or non-zero, we fall through to the real lookup, so stale
marks never show. Same eventually-consistent signal the &ldquo;Flags&rdquo; badge relies on.
</p>
</blockquote>



<p>
One thing in this comment caught my mind: the small parenthetical that says &ldquo;the
usual case&rdquo;. If that were true, it would be important information! However, this
comment is generated by the LLM model Claude, which has <i>no</i> sense of what
counts as the usual case in this domain. But by generating that comment, it
tricks every future reader (both human and robot) into believing in a property
of the system that does not exist.
</p>



<p>
Since I knew, in this case, that the comment was generated by a robot and that
the property it contained was hallucinated, I could fix the comment. But that&rsquo;s
not always so easy. Here&rsquo;s another comment.
</p>


<p><a href="https://entropicthoughts.com/ai-comment-classifier">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>A curmudgeon tries a language server</title>
<link>https://entropicthoughts.com/curmudgeon-tries-language-server</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/curmudgeon-tries-language-server</guid>
<pubDate>Tue, 25 Aug 2026 00:00:00 +0200</pubDate>
<category><![CDATA[haskell]]></category>
<category><![CDATA[programming]]></category>
<description><![CDATA[<p>
I write code roughly the same way I did ten years ago: I make edits in my text
editor, I switch over to a terminal window and I run a command to compile and
execute the code. I look at the result, then switch back to my editor. If I am
unsure of what happens, I either add tracer prints to the code and restart the
program, or I restart the program in a debugger. If I hit an exception that
crashes my program, I fix the problem and then restart it.
</p>



<p>
I&rsquo;m envious of what Lisp programmers do. Lisp development typically happens by
typing code directly into the REPL to add, remove, and replace parts of the
live, running system.
</p>



<ul class="org-ul">
<li>A Lisp programmer does not need to &ldquo;switch over&rdquo; to something else because
they are already inside their program process.</li>

<li>They never &ldquo;compile and execute&rdquo; the code because the code is already running
and they edit it by hot-swapping code.</li>

<li>They never restart in a debugger because they are already inside their program
process and can inspect anything they want.</li>

<li>They don&rsquo;t have to restart the program on exceptions, because the condition
system allows resuming the crashing code <i>from anywhere in the stack</i> after
the system has been patched with a fix.</li>
</ul>



<p>
A consequence of this way of working is that early in a Lisp project, there may
not even be any <i>source code</i> to speak of. Instead, the evolving definition of
the system exists only in the <i>memory image of the running process</i> and nowhere
else. This is so unlike how we normally do development that people have
expressed trouble understanding what that even means. A comparison that might
help is to think of how people treat relational databases in the beginning of a
project. Often, people don&rsquo;t start out with a version-controlled schema
definition; rather, the evolving schema exists only in the running database.
Sure, at some point when the project matures, the database schema is dumped out
to a file, put under version control, and from that point forward, schema
changes are made through explicit migrations. Similarly, a Common Lisp project
that matures will be dumped from memory image to source code eventually, and
then that source code will go under version control, with patches applied more
carefully to the live system.
</p>


<p><a href="https://entropicthoughts.com/curmudgeon-tries-language-server">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Snakes and Ladders</title>
<link>https://entropicthoughts.com/snakes-and-ladders</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/snakes-and-ladders</guid>
<pubDate>Tue, 18 Aug 2026 00:00:00 +0200</pubDate>
<category><![CDATA[games]]></category>
<category><![CDATA[life]]></category>
<description><![CDATA[<p>
It is common for promotional pamphlets aimed at
children to contain a themed variant of <i>snakes and ladders</i>. This is the &ldquo;game&rdquo;
where the players toss a die, move as many places as the die says, and then some
spots on the board have an event that send the player forward, or back, or give
them an extra toss or whatever.
</p>



<p>
The theme is typically related to whatever the promoter wants to promote, but
styled for children. I get the appeal – for children. They can probably imagine
they&rsquo;re really travelling along the path! In the variant pictured below, there&rsquo;s
forest and water and tunnels and dogs and everything!
</p>




<div id="org12d74e9" class="figure">
<p>
</p>
</div>



<p>
But as a game, it <i>sucks</i>. For everyone. My children hate losing, and these
games have <i>n</i>−1 losers for every <i>n</i> players. There are also literally zero
decisions for the players to make, so I cannot make it through even a few turns
without zoning out.
</p>


<p><a href="https://entropicthoughts.com/snakes-and-ladders">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>A noob learns FFT</title>
<link>https://entropicthoughts.com/fft</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/fft</guid>
<pubDate>Tue, 11 Aug 2026 00:00:00 +0200</pubDate>
<category><![CDATA[maths]]></category>
<description><![CDATA[<p>
I have never done any time–frequency transformations. I understand the general
idea, but I have never worked with the details. Today I had a reason to use it,
but that means learning the basics first.
</p>



<p>
The following is three seconds of a single 5 Hz sine wave, recorded at a sample
rate of 100 Hz.
</p>




<div id="org4e8c16c" class="figure">
<p>
</p>
</div>


<p><a href="https://entropicthoughts.com/fft">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Purely functional digital circuit simulator (SICP 3.3)</title>
<link>https://entropicthoughts.com/sicp-3-3-pure-digital-circuit-simulator</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/sicp-3-3-pure-digital-circuit-simulator</guid>
<pubDate>Wed, 05 Aug 2026 00:00:00 +0200</pubDate>
<category><![CDATA[programming]]></category>
<description><![CDATA[<p>
I have a copy of SICP, or as it is also known, <i>The Wizard
Book</i>. This book is widely praised, but I can&rsquo;t take the
time to work my way through all of it. Instead, I&rsquo;m going to occasionally jump
into the parts of it that look interesting.
</p>



<p>
Since last week, are in the process of simulating a digital circuit. The reason
this is interesting is the solution in SICP uses hidden mutable state and
message-passing to make the code object-oriented. It even uses a mutable global
variable for scheduling! We managed to replicate all of that in Haskell, but now
we want to refactor the solution to be easier to work with.
</p>



<p>
If we are going to manage the simulation in a pure functional manner, we still
have to contend with the fact that during simulation, wires are objects with a
fixed identity. A wire does not become a different wire just because its signal
changes – the same wire is still hooked up to the same gates. Wires need to
maintain their identity somehow.
</p>



<p>
In addition to the <code>Signal</code> type from before, we&rsquo;ll define a <code>Wire</code> to be a
string. This is its identity. We&rsquo;ll also make a convenience function for
creating a wire whose name is based on another wire name. This will be useful
for creating unique wire names when we build circuits.
</p>


<p><a href="https://entropicthoughts.com/sicp-3-3-pure-digital-circuit-simulator">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Digital circuit simulator in Haskell (SICP 3.3)</title>
<link>https://entropicthoughts.com/sicp-3-3-digital-circuit-simulator-in-haskell</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/sicp-3-3-digital-circuit-simulator-in-haskell</guid>
<pubDate>Mon, 27 Jul 2026 00:00:00 +0200</pubDate>
<category><![CDATA[programming]]></category>
<description><![CDATA[<p>
I have a copy of SICP, or as it is also known, <i>The Wizard
Book</i>. This book is widely praised, but I can&rsquo;t take the
time to work my way through all of it. Instead, I&rsquo;m going to occasionally jump
into the parts of it that look interesting.
</p>



<p>
In the previous two installations of this series, we looked at ways of
implementing generic functions. In the first one, by tagging values and
dispatching operations on tags. In the second case, by filling a mutable table
of operation–tag pairs. We saw how these are roughly equivalent to the existing
Haskell features of sum types and type classes.
</p>



<p>
Now we&rsquo;ll simulate a digital circuit. The reason this is interesting is that the
solution in SICP uses hidden mutable state and message-passing to make the
code object-oriented. It even uses a mutable global variable for scheduling! I
wasn&rsquo;t sure if it would be possible to replicate that in Haskell, but as we&rsquo;ll
see, it can get incredibly close.
</p>



<p>
There are many ways to implement mutable state in Haskell, but to begin with,
we&rsquo;ll use the way that&rsquo;s most similar to the SICP solution: <code>IORef</code>s. An
<code>IORef</code> is a mutable variable, like in any other programming language.
</p>


<p><a href="https://entropicthoughts.com/sicp-3-3-digital-circuit-simulator-in-haskell">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Kuiper Q-Q plot: are these the same?</title>
<link>https://entropicthoughts.com/kuiper-q-q-plot</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/kuiper-q-q-plot</guid>
<pubDate>Tue, 21 Jul 2026 00:00:00 +0200</pubDate>
<category><![CDATA[statistics]]></category>
<description><![CDATA[<p>
When we tried to get an intuition for the differences in distribution functions,
we learned that children subjected to a treatment had greater variation in their
score than the reference group.
</p>




<div id="org181a0dd" class="figure">
<p>
</p>
</div>



<p>
However, we couldn&rsquo;t tell if this difference was large enough to be a useful
guide for making decisions. It could just be an artifact of the small sample
size.
</p>


<p><a href="https://entropicthoughts.com/kuiper-q-q-plot">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Intuition for distribution differences</title>
<link>https://entropicthoughts.com/distribution-differences</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/distribution-differences</guid>
<pubDate>Tue, 14 Jul 2026 00:00:00 +0200</pubDate>
<category><![CDATA[statistics]]></category>
<description><![CDATA[<p>
Imagine every person in a country is assigned some sort of score, and a higher
score is better. I can&rsquo;t share the specifics around the score I&rsquo;m investigating
right now, but on the population level it is collected
by a government agency. Here&rsquo;s the distribution of these scores for the
population in my country.
</p>




<div id="org717591a" class="figure">
<p>
</p>
</div>



<p>
Since the curve does not start at the origin but a few percent up on the y axis,
we can conclude that a few percent of the population have a score of zero. That
doesn&rsquo;t mean they are bad or miserable people – there may be factors that make
zero the best score for them – but on average, a score of zero is worse than a
higher score.
</p>


<p><a href="https://entropicthoughts.com/distribution-differences">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>A full body MRI earns you a year of smoking</title>
<link>https://entropicthoughts.com/full-body-mri-earns-you-a-base-jump</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/full-body-mri-earns-you-a-base-jump</guid>
<pubDate>Tue, 07 Jul 2026 00:00:00 +0200</pubDate>
<category><![CDATA[statistics]]></category>
<description><![CDATA[<p>
Alternative titles:
</p>



<ul class="org-ul">
<li>… earns you a high-risk pregnancy</li>
<li>… earns you an ascent of Matterhorn</li>
<li>… earns you 10,000 km on a motorcycle</li>
<li>… earns you two BASE jumps</li>
<li>… earns you a day on the frontline in Ukraine</li>
</ul>



<p>
These are all about equivalent to the risk of one year of smoking.
</p>




<p>
I&rsquo;m skeptical of submitting asymptomatic people to medical tests. Almost any
time I look into the evidentiary power of medical tests, I&rsquo;m struck by how much
work is performed by the base rate. The tests only work because we perform them
on people who appear sick, i.e. have a higher probability of actually being sick
in the first place. If we would perform them on seemingly-healthy people we
would get nonsensical results.
</p>


<p><a href="https://entropicthoughts.com/full-body-mri-earns-you-a-base-jump">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Data-directed programming in Haskell (SICP 2.4.3)</title>
<link>https://entropicthoughts.com/sicp-2-4-data-directed-programming-in-haskell</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/sicp-2-4-data-directed-programming-in-haskell</guid>
<pubDate>Tue, 30 Jun 2026 00:00:00 +0200</pubDate>
<category><![CDATA[haskell]]></category>
<description><![CDATA[<p>
I have a copy of SICP, or as it is also known, <i>The Wizard
Book</i>. This book is widely praised, but I can&rsquo;t take the
time to work my way through all of it. Instead, I&rsquo;m going to occasionally jump
into the parts of it that look interesting. Last week, we looked at tagged data
in Haskell. The authors of SICP weren&rsquo;t convinced that&rsquo;s the best approach, so
they move on to data-directed programming. We&rsquo;ll do the same.
</p>




<p>
Complex numbers can be stored in their rectangular form, with a real
and an imaginary part. They can also be stored in polar form, where there&rsquo;s a
magnitude and an angle. Whichever way a complex number is stored, we would like
to be able to query it for all of these four quantities:
</p>



<ul class="org-ul">
<li>The real coordinate in the rectangular form of the complex number.</li>
<li>The imaginary coordinate in the rectangular form.</li>
<li>The magnitude in the polar form.</li>
<li>The angle in the polar form.</li>
</ul>



<p>
Last time we implemented these on top of a <i>tagged</i> value. To return one of the
four coordinates above, a function would introspect the tag to figure out how to
satisfy the caller for the representation involved. This works, but any time we
want to add a new representation, we have to change all four existing
operations.
</p>


<p><a href="https://entropicthoughts.com/sicp-2-4-data-directed-programming-in-haskell">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
<item>
<title>Tagged data in Haskell (SICP 2.4.2)</title>
<link>https://entropicthoughts.com/sicp-2-4-tagged-data-in-haskell</link>
<author>a@xkqr.org (kqr)</author>
<guid isPermaLink="false">https://entropicthoughts.com/sicp-2-4-tagged-data-in-haskell</guid>
<pubDate>Tue, 23 Jun 2026 00:00:00 +0200</pubDate>
<category><![CDATA[haskell]]></category>
<description><![CDATA[<p>
I have a copy of SICP, or as it is also known, <i>The Wizard
Book</i>. This book is widely praised, but I can&rsquo;t take the
time to work my way through all of it. However, sometimes I jump into parts of
it that look interesting. Today, we&rsquo;ll see how to support multiple
representations of data through tagging.
</p>



<p>
This article is written in Haskell throughout, but at the start it will look a
lot like the Lisp code in SICP. I have intentionally tried to recreate the
SICP solution as closely as possible, including dynamic typing and all. See
the appendix if you&rsquo;re curious how it works.
</p>



<p>
Complex numbers can be stored in their rectangular form, where there&rsquo;s a real
and an imaginary part. They can also be stored in polar form, where there&rsquo;s a
magnitude and an angle. The authors ask us to imagine that two people have been
working on a library for mathematics, but ended up choosing different ways to
store complex numbers. How can they write their code so that they don&rsquo;t have to
agree on one way to store the data?
</p>



<p>
Abelson and Sussman propose a <i>tagged</i> representation, where the data of the
complex numbers are paired with a tag, which indicates to the implementation
what representation is being used. They suggest the following functions to
attach a tag, as well as inspect tagged data.
</p>


<p><a href="https://entropicthoughts.com/sicp-2-4-tagged-data-in-haskell">(Continue reading the full article on the web.)</a></p>
]]></description>
</item>
</channel>
</rss>
