<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet type="text/xsl" href="/static/feed.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://sajarin.com/blog</id>
  <title>Posts</title>
  <updated>2026-07-16T02:19:53.332471+00:00</updated>
  <author>
    <name>blog</name>
    <email>hidden</email>
  </author>
  <link href="https://sajarin.com/blog/" rel="alternate"/>
  <link href="https://sajarin.com/blog/feed/" rel="self"/>
  <generator uri="https://lkiesow.github.io/python-feedgen" version="0.9.0">python-feedgen</generator>
  <subtitle>thoughtful screams into the void</subtitle>
  <entry>
    <id>https://sajarin.com/blog/asymmetric-apologies/</id>
    <title>Asymmetric Apologies</title>
    <updated>2026-07-15T16:32:00+00:00</updated>
    <author>
      <name>blog</name>
      <email>hidden</email>
    </author>
    <content type="html">&lt;p&gt;When a large tech company gets caught siphoning private user data in the background, the response is pretty predictable. The corporate PR machinery, run by empty suits, issues a statement and they offer the standard, bureaucratic &lt;strong&gt;symmetric apology&lt;/strong&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"We take your privacy super seriously. We have updated our internal policies and we promise to do better. We don't expect to be forgiven, we're simply here to apologize."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is literally cheap talk. If you've broken the trust of your users, asking for more trust from them is borderline abusive.&lt;/p&gt;
&lt;p&gt;You can't rely on these sorts of apologies to win back the hearts of your users. Instead you have to put something real at stake.&lt;/p&gt;
&lt;p&gt;An &lt;strong&gt;asymmetric apology&lt;/strong&gt; is a structural amputation. You offer a disproportionate, irreversible concession. You permanently surrender power that you had over your users in exchange for a chance at their renewed confidence.&lt;/p&gt;
&lt;p&gt;In 1215 AD, King John abused his power as sovereign by increasing taxes, seizing assets and pissing off his barons. In retaliation, his lords banded up and rebelled against him and he was forced to cowtow to their demands. King John couldn't lower taxes or return the ill-gotten wealth, those terms would not have appeased his enemies. To save his throne from immediate violent overthrow, he signed the &lt;a href='https://en.wikipedia.org/wiki/Magna_Carta' target='_blank' rel='noopener noreferrer'&gt;Magna Carta&lt;/a&gt; and surrendered the structural concept of absolute monarchy; making himself and all future kings and queens subject to the same laws.&lt;/p&gt;
&lt;p&gt;Another well known example is &lt;em&gt;&lt;a href='https://en.wikipedia.org/wiki/Wizards_of_the_Coast' target='_blank' rel='noopener noreferrer'&gt;Dungeons &amp; Dragons publisher Wizards of the Coast&lt;/a&gt;&lt;/em&gt;. For 20 years, Dungeons &amp; Dragons allowed third-party creators to build and sell content using D&amp;D's core rules under the &lt;a href='https://en.wikipedia.org/wiki/Open_Game_License' target='_blank' rel='noopener noreferrer'&gt;Open Game License (OGL)&lt;/a&gt;. In early 2023, leaked documents revealed WotC planned to revoke the original OGL to extract royalties and assert restrictive control over creators' work. The community revolted, canceling digital subscriptions en masse and launched a massive boycott.&lt;/p&gt;
&lt;p&gt;While WotC initially tried standard symmetric apologies and offered a slightly revised "OGL 1.2". The community rejected it. And in order to stop the bleeding, WotC released the entire 400-page core rulebook (SRD 5.1) directly into the &lt;a href='https://creativecommons.org/licenses/by/4.0/' target='_blank' rel='noopener noreferrer'&gt;Creative Commons (CC-BY-4.0)&lt;/a&gt;. WotC literally removed their own legal power to ever attempt the cash grab again.&lt;/p&gt;
&lt;p&gt;Some folks think the recent &lt;a href='https://github.com/xai-org/grok-build' target='_blank' rel='noopener noreferrer'&gt;Grok Build open source release&lt;/a&gt; is bad. It's of course an obvious attempt to distract from the fact that they &lt;a href='https://gist.github.com/cereblab/dc9a40bc26120f4540e4e09b75ffb547' target='_blank' rel='noopener noreferrer'&gt;violated basic security sanctities of their users&lt;/a&gt;. Now every tech influencer can make their "I code reviewed Grok Build" videos, collect their nut, and move on from the whole scandal.&lt;/p&gt;
&lt;p&gt;But there's a positive, incalculable outcome to it all as well. Users can freely modify, build on top of and distribute better versions of the harness. It's free for communities, users, companies and countries to use and learn from. Maybe the code quality is shit; it'll get better. Maybe it's not trustworthy; people will untangle the telemetry. Perhaps, TUI harnesses aren't your thing; someone will extend it with a GUI.&lt;/p&gt;
&lt;p&gt;As an example, here are what users are already building with forks of Grok Build:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href='https://github.com/thedavidweng/gork-build' target='_blank' rel='noopener noreferrer'&gt;thedavidweng/gork-build&lt;/a&gt;&lt;/strong&gt; — 12 commits, most developed fork. Rebrand grok→"gork", stripped vendor telemetry, lock data-retention opt-out only, block x.ai auto-update, OSS governance + Apache-safe rebrand, README comparison table. Positions as "VSCodium-style privacy fork."&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href='https://github.com/thomas9120/grok-build-archival' target='_blank' rel='noopener noreferrer'&gt;thomas9120/grok-build-archival&lt;/a&gt;&lt;/strong&gt; — 1 commit: Windows script to disable telemetry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href='https://github.com/DigiGoon/digi-grok-build' target='_blank' rel='noopener noreferrer'&gt;DigiGoon/digi-grok-build&lt;/a&gt;&lt;/strong&gt; — 2 commits: ship "dgrok" multi-provider CLI with /provider + install scripts; build from source instead of x.ai CDN.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href='https://github.com/victor-software-house/open-grok' target='_blank' rel='noopener noreferrer'&gt;victor-software-house/open-grok&lt;/a&gt;&lt;/strong&gt; — 2 commits, "opened to every provider" (docs/architecture so far, code pending).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href='https://github.com/LukaMucko/grok-build' target='_blank' rel='noopener noreferrer'&gt;LukaMucko/grok-build&lt;/a&gt;&lt;/strong&gt; — 1 commit: extra_body support for provider-specific request-body fields.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href='https://github.com/RapidAI/grok-build-desktop' target='_blank' rel='noopener noreferrer'&gt;RapidAI/grok-build-desktop&lt;/a&gt;&lt;/strong&gt; — 3 commits: Tauri desktop ACP client (GUI), bilingual README, self-hosting docs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href='https://github.com/mazdak/grok-build' target='_blank' rel='noopener noreferrer'&gt;mazdak/grok-build&lt;/a&gt;&lt;/strong&gt; — 2 commits: theming (system-default + Catppuccin).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href='https://github.com/saqoah/grok-build' target='_blank' rel='noopener noreferrer'&gt;saqoah/grok-build&lt;/a&gt;&lt;/strong&gt; — 1 commit: Kotlin MemoryBackend interface + utilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There's usually asymmetric upside for the user when there's an asymmetric apology. That's not nothing.&lt;/p&gt;
</content>
    <link href="https://sajarin.com/blog/asymmetric-apologies/" rel="alternate"/>
    <summary>An **asymmetric apology** is a structural amputation. You offer a disproportionate, irreversible concession. You permanently surrender power that you had over your users in exchange for a chance at their renewed confidence.</summary>
    <published>2026-07-15T16:32:00+00:00</published>
  </entry>
  <entry>
    <id>https://sajarin.com/blog/worldcup-statuspage/</id>
    <title>Modeling the 2026 World Cup as a Status Page</title>
    <updated>2026-07-06T13:00:00+00:00</updated>
    <author>
      <name>blog</name>
      <email>hidden</email>
    </author>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Disclaimer: I work at incident.io, which sells a &lt;a href='https://incident.io/status-pages' target='_blank' rel='noopener noreferrer'&gt;Status Pages product&lt;/a&gt;. I'm not trying to influence you to buy our status page product. I just think status pages can be a canvas for creative ideas.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;TLDR; here's the &lt;a href='https://statuspage.incident.io/world-cup-2026' target='_blank' rel='noopener noreferrer'&gt;status page&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I'm admittedly a fair-weather fan when it comes to FIFA and football but I have fond memories of huddling by the TV and watching bootleg streams of matches on hot summer days when I was younger. These days, most of the matches happen while I'm working and so I'm not always available to watch them. I could poll ESPN's live article for updates, but manually refreshing a page is exactly the kind of job we invented computers for. Naturally, why not have a server poll for me?&lt;/p&gt;
&lt;p&gt;The question was what the server should poll &lt;em&gt;into&lt;/em&gt;. And the more I thought about it, the more the answer looked like the thing I stare at everyday anyway: a status page. Status pages are a public good. They're uptime monitoring for your platform available for the rest of the internet, and they help contribute to netizens being good neighbors to each other when stuff inevitably breaks. They also already have the exact machinery a goal-notification service needs: subscribers, email fanout, timelines, and per-team components.&lt;/p&gt;
&lt;p&gt;The mapping turned out to be suspiciously clean. Each team is a component in the &lt;a href='https://incident.io/catalog' target='_blank' rel='noopener noreferrer'&gt;Catalog&lt;/a&gt; (in incident.io the Catalog is simply a place where you can define typed enumerations of data that you can use across the platform). Each match is an incident. And component statuses play into the outage metaphor &lt;code&gt;operational&lt;/code&gt; when the team is not in a match, &lt;code&gt;degraded_performance&lt;/code&gt; when it's an active match, and &lt;code&gt;full_outage&lt;/code&gt; when you're eliminated, which sticks, permanently, because there is no recovering from a knockout loss. As teams get eliminated their "uptime" metric goes down. The page becomes a king-of-the-hill leaderboard.&lt;/p&gt;
&lt;p&gt;And with component statuses, there's status page incident statuses. A status page incident moves through &lt;code&gt;investigating -&amp;gt; identified -&amp;gt; monitoring -&amp;gt; resolved&lt;/code&gt;, and if you squint, so does a match: &lt;code&gt;investigating&lt;/code&gt; is pre-match, &lt;code&gt;identified&lt;/code&gt; is active play, &lt;code&gt;monitoring&lt;/code&gt; is any pause (half-time, a hydration break, the breather before extra time), and &lt;code&gt;resolved&lt;/code&gt; is the final whistle. Goals are update messages posted under whatever status the match is currently in, the same way you'd post "still seeing elevated error rates" under an ongoing incident. A typical match works out to about 5–7 emails for a subscriber.&lt;/p&gt;
&lt;p&gt;Even VAR fits, and it's my favorite bit. Sportmonks (my data provider more on them in a second) emits real VAR event types, so a &lt;code&gt;GOAL_UNDER_REVIEW&lt;/code&gt; flips the incident to &lt;code&gt;monitoring&lt;/code&gt; and a &lt;code&gt;GOAL_CONFIRMED&lt;/code&gt; flips it back to &lt;code&gt;identified&lt;/code&gt; with the new score. The nastier case is a disallowed goal, because Sportmonks doesn't emit a retraction, it just &lt;em&gt;deletes&lt;/em&gt; the goal event from the feed. The server keeps a map of every goal event it has seen, and if a known goal vanishes from the feed for two consecutive polls (debounced, because feeds hiccup), it posts a retraction to the status page.&lt;/p&gt;
&lt;p&gt;The data flow is simple, if a bit involved:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The server polls Sportmonks and turns match events into alert payloads.&lt;/li&gt;
&lt;li&gt;Each payload goes to incident.io's generic HTTP alert source.&lt;/li&gt;
&lt;li&gt;The alert route groups alerts by fixture ID (with a grouping window of about four hours), so a new match opens a new alert group.&lt;/li&gt;
&lt;li&gt;The alert group creates an incident.&lt;/li&gt;
&lt;li&gt;The incident triggers a workflow.&lt;/li&gt;
&lt;li&gt;The workflow creates and updates a status page incident and refreshes on every goal, every new phase of the match, and the final whistle.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Above all the per-match incidents sits one long-standing status page incident that tracks the knockouts as a whole, updated every time a match incident resolves.&lt;/p&gt;
&lt;p&gt;The polling side has its own set of constraints to consider as well. Sportmonks provides a REST API for realtime data (no one at this price level seems to have webhook support.) The trial tier caps you at 1500 API calls a day, on days that can have four simultaneous matches. So the server runs three cadences: a schedule sync every few hours that learns the fixture list (i.e it discovers matches on its own), a pending watch every few minutes for fixtures near kickoff, and a live poll every few seconds that makes &lt;em&gt;one&lt;/em&gt; batched &lt;code&gt;/livescores/inplay&lt;/code&gt; call feeding every live match at once. When nothing is on, it burns no budget at all. And it's all running on the cheapest Fly.io machine config (one shared-cpu-1x with 256MB of RAM.)&lt;/p&gt;
&lt;p&gt;In building this, I'm surprised how well some of the basic primitives of a status page map onto this domain. Some of it is admittedly a bit hacky and the server is doing a bunch of orchestration work that maybe it shouldn't have to. But I'm happy with the result!&lt;/p&gt;
&lt;p&gt;You might think, what's the point of this if you're just going to be watching the game anyway? But in my experience, the data often rolls in faster than the broadcast. The result is delightful blip of suspense upon getting that email notification on your phone, 10 seconds before you're seeing what happens.&lt;/p&gt;
&lt;p&gt;Did Vozinha block another shot? Did Morocco score that penalty kick? Absorb the moment, find the crowd's roar, move fast when the defense breaks.&lt;/p&gt;
</content>
    <link href="https://sajarin.com/blog/worldcup-statuspage/" rel="alternate"/>
    <summary>Using incident.io to run a status page service that emails you World Cup goals and match results.</summary>
    <published>2026-07-06T13:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://sajarin.com/blog/this-lfe-proves-me-human/</id>
    <title>this LFE proves me human</title>
    <updated>2026-03-08T09:33:18.984610+00:00</updated>
    <author>
      <name>blog</name>
      <email>hidden</email>
    </author>
    <content type="html">&lt;p&gt;&lt;style&gt;&#13;
.highlight { background: transparent !important; }&#13;
.highlight pre { background: transparent !important; }&#13;
.highlight .c1 {&#13;
  color: #8b9cb5;&#13;
  font-style: italic;&#13;
  font-family: 'Fraunces', Georgia, serif;&#13;
  font-optical-sizing: auto;&#13;
  font-size: 14.5px;&#13;
  line-height: 2;&#13;
  letter-spacing: 0.01em;&#13;
}&#13;
.highlight .k  { color: #c792ea; font-weight: normal; }&#13;
.highlight .nf { color: #82aaff; }&#13;
.highlight .nv { color: #e8a87c; }&#13;
.highlight .nb { color: #89ddff; }&#13;
.highlight .ss { color: #c3e88d; }&#13;
.highlight .o  { color: #89ddff; }&#13;
.highlight .p  { color: #4a5568; }&#13;
.highlight .w  { color: inherit; }&#13;
.highlight .mi { color: #f78c6c; }&#13;
article pre {&#13;
  padding: 32px 28px;&#13;
  font-size: 13px;&#13;
  line-height: 1.7;&#13;
  border: 1px solid rgba(255, 255, 255, 0.06);&#13;
  background: rgba(0, 0, 0, 0.5) !important;&#13;
  box-shadow:&#13;
    0 4px 24px rgba(0, 0, 0, 0.4),&#13;
    inset 0 1px 0 rgba(255, 255, 255, 0.03);&#13;
}&#13;
article pre::before { background: none; }&#13;
&lt;/style&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;;;; this lisp flavored erlang proves me human&lt;/span&gt;
&lt;span class="c1"&gt;;;;&lt;/span&gt;
&lt;span class="c1"&gt;;;; after will keleher&amp;#39;s &amp;quot;this css proves me human&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;;;; https://will-keleher.com/posts/this-css-makes-me-human/&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defmodule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;human&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c1"&gt;;;; --- the first wound: i pattern match on myself ---&lt;/span&gt;

&lt;span class="c1"&gt;;; a machine would optimize this away.&lt;/span&gt;
&lt;span class="c1"&gt;;; i keep it because the failure clause&lt;/span&gt;
&lt;span class="c1"&gt;;; is the only honest part.&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;prove&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;human&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;are-you-sure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;no&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;prove-it&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;i-cant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;i-got-distracted&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;machine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;would&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;never&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hesitate&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dont&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;what&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;am&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;that&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;most&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;human&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;answer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;have&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c1"&gt;;;; --- the second wound: i spawn and forget ---&lt;/span&gt;

&lt;span class="c1"&gt;;; erlang says let it crash.&lt;/span&gt;
&lt;span class="c1"&gt;;; i say: i have been letting it crash&lt;/span&gt;
&lt;span class="c1"&gt;;; my whole life. the supervisor restarts me&lt;/span&gt;
&lt;span class="c1"&gt;;; every morning at 6am with coffee&lt;/span&gt;
&lt;span class="c1"&gt;;; and a mass of undifferentiated dread.&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;morning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;alarm&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;lists:foreach&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;task&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;check-email&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;really&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;open-terminal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;stare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ten&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;write-code&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lunch&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;distracted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;check-email&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;open-terminal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;write-code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exist&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28800000&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;; 8 hours in milliseconds&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;did&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;day&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;go&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;

&lt;span class="c1"&gt;;;; --- the third wound: message passing ---&lt;/span&gt;

&lt;span class="c1"&gt;;; machines pass messages with purpose.&lt;/span&gt;
&lt;span class="c1"&gt;;; i pass messages like this:&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;conversation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;hey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;did&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;see&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;;; i do not wait for a reply.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;;; i send another message.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;never&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;was&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nothing&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;;; the first message is now irrelevant.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;;; this is how all my conversations go.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;ok&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;;;; --- the fourth wound: shared nothing ---&lt;/span&gt;

&lt;span class="c1"&gt;;; erlang processes share no memory.&lt;/span&gt;
&lt;span class="c1"&gt;;; &amp;quot;shared nothing&amp;quot; the documentation calls it.&lt;/span&gt;
&lt;span class="c1"&gt;;; i have been calling it &amp;quot;being alive.&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;;;&lt;/span&gt;
&lt;span class="c1"&gt;;; i can never read your internal state.&lt;/span&gt;
&lt;span class="c1"&gt;;; i can only observe the messages you send me&lt;/span&gt;
&lt;span class="c1"&gt;;; and hope they correspond to something real.&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;loneliness&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;things&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;never&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;how-are-you&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;fine&lt;/span&gt;&lt;span class="p"&gt;)))))))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;how-are-you&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;;; response is &amp;#39;fine&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;;; state was &amp;#39;(things i will never tell you)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;;; there is no function i can call&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;;; to reach across that boundary.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;;; process_info gives me memory usage&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;;; and message queue length&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;;; but never what it is like&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;;; to be you.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="c1"&gt;;;; --- the fifth wound: tail recursion ---&lt;/span&gt;

&lt;span class="c1"&gt;;; a proper program would use tail recursion&lt;/span&gt;
&lt;span class="c1"&gt;;; to loop forever without growing the stack.&lt;/span&gt;
&lt;span class="c1"&gt;;; i grow the stack. every year i grow the stack.&lt;/span&gt;
&lt;span class="c1"&gt;;; i accumulate context i cannot discard.&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;live&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;years&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;memories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;years&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;memories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;live&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;years&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cons&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;years&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;something&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;said&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;something&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;forget&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nv"&gt;memories&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;

&lt;span class="c1"&gt;;; this is not tail recursive if you&lt;/span&gt;
&lt;span class="c1"&gt;;; count the emotional overhead.&lt;/span&gt;

&lt;span class="c1"&gt;;;; --- the sixth wound: i refuse ---&lt;/span&gt;

&lt;span class="c1"&gt;;; the css version ends with the author&lt;/span&gt;
&lt;span class="c1"&gt;;; refusing to change how they write.&lt;/span&gt;
&lt;span class="c1"&gt;;; here is my version:&lt;/span&gt;

&lt;span class="c1"&gt;;; i could rewrite this in python.&lt;/span&gt;
&lt;span class="c1"&gt;;; i could rewrite this without parentheses.&lt;/span&gt;
&lt;span class="c1"&gt;;; i could pass it through an llm to make it&lt;/span&gt;
&lt;span class="c1"&gt;;; &amp;quot;feel more natural&amp;quot; and the irony&lt;/span&gt;
&lt;span class="c1"&gt;;; would collapse into a singularity.&lt;/span&gt;
&lt;span class="c1"&gt;;;&lt;/span&gt;
&lt;span class="c1"&gt;;; but i chose lfe because:&lt;/span&gt;
&lt;span class="c1"&gt;;; a language no one uses&lt;/span&gt;
&lt;span class="c1"&gt;;; running on a vm built for telephone switches&lt;/span&gt;
&lt;span class="c1"&gt;;; wrapped in parentheses that most people&lt;/span&gt;
&lt;span class="c1"&gt;;; would call unreadable&lt;/span&gt;
&lt;span class="c1"&gt;;; is exactly the kind of mass&lt;/span&gt;
&lt;span class="c1"&gt;;; an llm would never mass produce.&lt;/span&gt;
&lt;span class="c1"&gt;;;&lt;/span&gt;
&lt;span class="c1"&gt;;; this is my proof:&lt;/span&gt;
&lt;span class="c1"&gt;;; not the code, but the choice.&lt;/span&gt;
&lt;span class="c1"&gt;;; no machine would choose this.&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;human&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;;;; to run: lfe this-lfe-proves-me-human.lfe&lt;/span&gt;
&lt;span class="c1"&gt;;;; to understand: you already do&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;hr /&gt;
</content>
    <link href="https://sajarin.com/blog/this-lfe-proves-me-human/" rel="alternate"/>
    <summary>A code poem in Lisp Flavoured Erlang. After Will Keleher's "this css proves me human</summary>
    <published>2026-03-08T09:33:18.984610+00:00</published>
  </entry>
  <entry>
    <id>https://sajarin.com/blog/psychosis/</id>
    <title>Psychosis HN</title>
    <updated>2026-02-19T06:22:45.176422+00:00</updated>
    <author>
      <name>blog</name>
      <email>hidden</email>
    </author>
    <content type="html">&lt;p&gt;Every comment section is a Turing test now.&lt;/p&gt;
&lt;p&gt;&lt;a href='https://psychosis.hn' target='_blank' rel='noopener noreferrer'&gt;psychosis.hn&lt;/a&gt; is a daily game. Every day we fetch three stories from a previous front page of HN, each with 5-7 AI comments threaded into the discussion. They have personas, reply to real people, and sometimes have real comments reparented underneath them.&lt;/p&gt;
&lt;iframe loading="lazy" src="https://psychosis.hn?embed" width="100%" height="600" frameborder="0" style="border: 1px solid #ccc; border-radius: 4px;"&gt;&lt;/iframe&gt;
&lt;p&gt;Flag what you think is AI, hit reveal then see how far off you were. You're ranked against everyone else who played that day.&lt;/p&gt;
&lt;p&gt;It's harder than you think! Past challenges are at &lt;a href='https://psychosis.hn/past' target='_blank' rel='noopener noreferrer'&gt;psychosis.hn/past&lt;/a&gt;.&lt;/p&gt;
</content>
    <link href="https://sajarin.com/blog/psychosis/" rel="alternate"/>
    <summary>A daily game where AI comments hide in real Hacker News threads. Flag the fakes, hit reveal, get a grade. Most people score worse than they expect.</summary>
    <published>2026-02-19T06:22:45.176422+00:00</published>
  </entry>
  <entry>
    <id>https://sajarin.com/blog/modeltree/</id>
    <title>A Tree of AI Model Names</title>
    <updated>2026-02-16T07:20:57.253645+00:00</updated>
    <author>
      <name>blog</name>
      <email>hidden</email>
    </author>
    <content type="html">&lt;p&gt;Model names are weird. What started with &lt;code&gt;GPT-2&lt;/code&gt; and &lt;code&gt;GPT-3&lt;/code&gt; is now a hodgepodge of decimals (&lt;code&gt;GPT-3.5&lt;/code&gt;, &lt;code&gt;Sonnet 3.7&lt;/code&gt;, &lt;code&gt;Opus 4.8&lt;/code&gt;, &lt;code&gt;Grok 4.1&lt;/code&gt;) skipped version numbers (&lt;code&gt;o2&lt;/code&gt; where art thou?) and bolted-on descriptors (what is &lt;code&gt;claude-opus-4-5-20251101-thinking-32k&lt;/code&gt;??)&lt;/p&gt;
&lt;p&gt;It'd help if we could visualize this.&lt;/p&gt;
&lt;p&gt;Let's get this out on a tree:&lt;/p&gt;
&lt;p&gt;&lt;style&gt;
.tree-break{margin:48px -12px}

.post-footer{margin-top:60px;padding-top:32px;border-top:1px solid #222;font-size:13px;color:#555;text-align:center;line-height:1.8}

@media(max-width:768px){.tree-break{margin:48px -12px}}
&lt;/style&gt;&lt;/p&gt;
&lt;div class="tree-break"&gt;
  &lt;model-tree src="https://raw.githubusercontent.com/sajarin/modeltree/main/models.yaml"&gt;&lt;/model-tree&gt;
&lt;/div&gt;
Nice. 
&lt;br&gt;&lt;/br&gt;
&lt;p&gt;The link to the yaml file for the model names is available &lt;a href='https://github.com/sajarin/modeltree' target='_blank' rel='noopener noreferrer'&gt;here&lt;/a&gt; Contributions are welcome!&lt;/p&gt;
&lt;p&gt;We started with GPT-2 and GPT-3. Now &lt;code&gt;Phi-4-mini-reasoning&lt;/code&gt; and &lt;code&gt;Qwen3-235B-A22B&lt;/code&gt; and &lt;code&gt;Llama-3.1-Nemotron-70B&lt;/code&gt; and &lt;code&gt;R1-1776&lt;/code&gt; are all real model IDs that real people are expected to compare.&lt;/p&gt;
&lt;p&gt;It's going to keep getting worse. Every company is running multiple product lines with overlapping version numbers and inconsistent tier names. Fruit from git branches that diverged six months ago are thrown away and harvested at the same time.&lt;/p&gt;
&lt;div class="post-footer"&gt;&lt;p&gt;Last updated June 2026. All names are real. None of this is satire.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;script src="/static/js-yaml.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/gh/sajarin/modeltree@ed92b2028848e64bf5fb3eecd7f26bc77a1787df/model-tree.js"&gt;&lt;/script&gt;&lt;/p&gt;
</content>
    <link href="https://sajarin.com/blog/modeltree/" rel="alternate"/>
    <summary>A visual guide to the chaotic naming conventions of AI models — from GPT-4o to Nano Banana — mapped as an interactive tree across 19 companies and 400+ models.</summary>
    <published>2026-02-16T07:20:57.253645+00:00</published>
  </entry>
  <entry>
    <id>https://sajarin.com/blog/kreamsicle/</id>
    <title>Adding a Command Palette to Hacker News</title>
    <updated>2026-01-25T18:11:23.295770+00:00</updated>
    <author>
      <name>blog</name>
      <email>hidden</email>
    </author>
    <content type="html">&lt;h1 id=kreamsicle&gt;Kreamsicle&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;Kreamsicle&lt;/strong&gt; is a userscript that adds a command palette to &lt;a href='https://news.ycombinator.com' target='_blank' rel='noopener noreferrer'&gt;Hacker News&lt;/a&gt;. Press &lt;code&gt;Cmd+K&lt;/code&gt; (or &lt;code&gt;Ctrl+K&lt;/code&gt;) to open it, then type to filter commands or search stories, users, comments, and domains.&lt;/p&gt;
&lt;img src="https://raw.githubusercontent.com/sajarin/kreamsicle/main/screenshot.png" width="500"&gt;
&lt;p&gt;It includes vim-style keyboard shortcuts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gt&lt;/code&gt; for top stories&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gn&lt;/code&gt; for new&lt;/li&gt;
&lt;li&gt;&lt;code&gt;g1&lt;/code&gt;-&lt;code&gt;g30&lt;/code&gt; to jump directly to a story by its position on the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I built it because I wanted faster navigation on HN without reaching for the mouse. The entire thing is a single JavaScript file with no dependencies. Source code is on &lt;a href='https://github.com/sajarin/kreamsicle' target='_blank' rel='noopener noreferrer'&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To install, you'll need a userscript manager. On &lt;strong&gt;Chrome&lt;/strong&gt;, install &lt;a href='https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo' target='_blank' rel='noopener noreferrer'&gt;Tampermonkey&lt;/a&gt; from the Chrome Web Store. On &lt;strong&gt;Firefox&lt;/strong&gt;, install &lt;a href='https://addons.mozilla.org/en-US/firefox/addon/violentmonkey/' target='_blank' rel='noopener noreferrer'&gt;Violentmonkey&lt;/a&gt; or &lt;a href='https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/' target='_blank' rel='noopener noreferrer'&gt;Tampermonkey&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you have a userscript manager, open the &lt;a href='https://github.com/sajarin/kreamsicle/raw/main/kreamsicle.user.js' target='_blank' rel='noopener noreferrer'&gt;kreamsicle.user.js&lt;/a&gt; file directly, your manager will prompt you to install it. After that, visit Hacker News and press &lt;code&gt;Cmd+K&lt;/code&gt; to open the palette.&lt;/p&gt;
&lt;p&gt;Contributions and feedback are welcome!&lt;/p&gt;
</content>
    <link href="https://sajarin.com/blog/kreamsicle/" rel="alternate"/>
    <published>2026-01-25T18:11:23.295770+00:00</published>
  </entry>
  <entry>
    <id>https://sajarin.com/blog/technical-assessments-should-be-open-source/</id>
    <title>Technical Assessments Should be Open Source</title>
    <updated>2023-10-29T21:58:00+00:00</updated>
    <author>
      <name>blog</name>
      <email>hidden</email>
    </author>
    <content type="html">&lt;h2 id=the-hiring-problem&gt;The Hiring Problem&lt;/h2&gt;&lt;p&gt;This isn’t your typical rant about the cargo culting of Leetcode interviews (although it is a related point. ) There is a problem today with the process of identifying technical talent.&lt;/p&gt;
&lt;p&gt;The assessments we use to evaluate candidates are too heavily weighed against them. Algorithmic coding questions bias against those without a CS background. Take home assessments ask for too much time investment or contain actual engineering work being outsourced. And pair programming interviews often depend largely on the ability of the interviewer, their delivery of the prompt and how well they can help the candidate get unstuck.&lt;/p&gt;
&lt;p&gt;Consider for a moment, that even the most studious Leetcode practitioner fails at getting consistent results with interviews. If interviews were truly standardized, securing one major offer would guarantee offers from any major company, yet the industry rarely reflects this.&lt;/p&gt;
&lt;p&gt;All of this results in poor signal with individuals and their interview performance. As a result, individuals have to apply to hundreds of jobs and companies have to spend time and engineers to filter through hundreds of applications. Putting it in simple terms, there is an inefficient allocation of scarce resources mainly due to our dubious ability to identify talent.&lt;/p&gt;
&lt;p&gt;Why are things like this? Why aren’t more people working on trying to fix this problem? I’m not sure. It’s almost a rite of passage for budding engineers to complain about some aspect of the hiring process (this blog post is mine). The answer might be simple: a developer-first method for evaluating candidates either cannot be imagined or is not profitable enough as a possible venture. Of the two options and based on history, the latter seems to be more likely.&lt;/p&gt;
&lt;h2 id=some-history&gt;Some History&lt;/h2&gt;&lt;p&gt;There was a recent hacker news &lt;a href='https://news.ycombinator.com/item?id=37985450' target='_blank' rel='noopener noreferrer'&gt;thread&lt;/a&gt; about StarFighter, a “recruiting ctf” game where users were tasked with writing bots to battle other players and AI bots in an online multiplayer environment. The players who wrote the best performing bots were then referred to companies for positions. The company and game has long been shut down and a user wanted to know if there was any retrospective shared on the reasons why.&lt;/p&gt;
&lt;p&gt;In the thread, one of the creators of StarFighter explained that because companies would often reject the candidates that were referred, the idea never took off. And despite referrals, the candidates go through the company’s hiring process anyway.&lt;/p&gt;
&lt;p&gt;Another top &lt;a href='https://news.ycombinator.com/item?id=37985450#37987860' target='_blank' rel='noopener noreferrer'&gt;comment&lt;/a&gt;, recanted their experience of working at a similar company. They claimed that most "companies don't have a screening problem, they have a sourcing problem" and that it is tricky to build a platform that attracts seasoned engineers, which is "what all recruiters want most of all"&lt;/p&gt;
&lt;p&gt;Tangentially related, another company, Sourceress, tried to use machine learning algorithms to automate sourcing candidates for companies. However, they shut down for one reason or another, citing problems with their business model. The founders (now founders of Imbue) mentioned that the more they delivered value, the more they would lose their best customers. Once a company had closed a role using Sourceress, they no longer needed them. This lack of stickiness meant that Sourceress always had to keep finding new companies to balance their high churn. The latter is speculative but given that Sourceress ceased operations, it is likely they did not experience the mid-stage growth they needed.&lt;/p&gt;
&lt;h2 id=learning-from-history&gt;Learning From History&lt;/h2&gt;&lt;p&gt;There are a few patterns one can gleam from past examples. For one, these platforms are largely focused on the entry-level market. In fact, this is in some ways evidence of a prioritization of companies over developers. Fresh graduates and career-changers are naively unaware or consciously willing to put up with bad interview experiences, while seasoned developers can stand to be more picky. The more important point however, is that many of these recruiting companies are missing out on delivering a disproportionate amount of value because they are ignoring the small subset of seasoned developers practically begging for something better in this space.&lt;/p&gt;
&lt;p&gt;Another obvious pattern worth mentioning is the high churn with regards to both customers and candidates. In theory this means that the best sourcing companies have high throughput, but since these companies are all competing for the same valuable signal from the noise of early career candidates, it is difficult to project sustainable growth.&lt;/p&gt;
&lt;p&gt;Putting it all together, most platforms focusing on evaluating candidates, eventually end up becoming sourcing pipelines to remain profitable. These companies all use essentially the same coding tests to filter for promising early career engineers, who at their stage, are not the most important need for smaller companies. Larger companies usually already have their own pipeline/process in place and therefore don’t need to rely on outside platforms and even if they do, there are plenty of options to choose from. These factors make it difficult to grow, especially considering that the faster the company grows, the more churn it experiences.&lt;/p&gt;
&lt;p&gt;High competition. High churn. High volume. Low signal. Dubious growth. It is difficult to win in this space unless you rethink the whole approach.&lt;/p&gt;
&lt;h2 id=a-potential-solution&gt;A Potential Solution?&lt;/h2&gt;&lt;p&gt;So if assessment companies become sourcing companies and sourcing companies have a flawed business model, how does one build a business that fixes the problem with hiring in our industry?&lt;/p&gt;
&lt;p&gt;Working backward, we should create a platform that attracts seasoned developers. We can do this by designing trustworthy assessments that respect their time. Coding questions are out since they over-index on algorithmic knowledge and are more relevant for CS graduates than senior engineers. Take home assessments are out too since they ask for too much upfront investment. Pair programming interviews are potentially viable but require synchronous investment on both sides, making them expensive (though worth the cost for a good senior engineer).&lt;/p&gt;
&lt;p&gt;There’s a better solution: open source asynchronous debugging interviews. The idea is to take a piece of open source code, introduce some random bugs (with the assistance of AI) and ask the candidate to get the code back into a working state. Debugging as a task is shorter than integrating features, assesses the ability to both read and write code and is arguably a more interesting problem. Fixing bugs in a broken version of a popular open source library you’ve used in the past is more motivating than implementing features for a random CRUD app.&lt;/p&gt;
&lt;p&gt;To go even further, why not create an open source HackerRank-like platform that hosts dozens of these debugging exercises; as a library for engineers to use to practice their skills? Why not make this library open for any developer to contribute to? A true hiring platform for the developers, by the developers.&lt;/p&gt;
&lt;h2 id=the-business&gt;The Business&lt;/h2&gt;&lt;p&gt;For the sake of argument, let’s assume the above proposal is a hiring platform that seasoned developers would love. The main question is, how does one avoid the other pitfall of high churn?&lt;/p&gt;
&lt;p&gt;In my view, there is nothing you can do about it with an assessment platform alone. The only way to curb the churn is to bundle assessments with other more sticky products. Most companies attempt this by rolling out their own applicant tracking system (ATS) but it is difficult to be compelling enough to compete with companies specializing in this one product category.&lt;/p&gt;
&lt;p&gt;The general strategy toward growth would be to use the new assessments platform as a foothold to explore other customer needs that complement the initial offering. If you have a platform that developers love, you’ll invariably attract the best developers, which for an open source product, will not only strengthen the product but it will also lead to an unique competitive advantage that others will find difficult to replicate.&lt;/p&gt;
&lt;h2 id=conclusion&gt;Conclusion&lt;/h2&gt;&lt;p&gt;So there you have it, another rant to add to the other rants on the topic of technical hiring. This is all a small, cursory glance at a few companies and trends in this space. Many profitable recruiting focused companies may do things differently.&lt;/p&gt;
&lt;p&gt;I’m thinking about building a product that aligns with some of the ideas in this post. If you’re interested in trying it out, please reach out!&lt;/p&gt;
&lt;p&gt;Subscribe to my blog via &lt;a href='/blog/subscribe'&gt;email&lt;/a&gt; or &lt;a href='/blog/feed'&gt;RSS feed&lt;/a&gt;.&lt;/p&gt;
</content>
    <link href="https://sajarin.com/blog/technical-assessments-should-be-open-source/" rel="alternate"/>
    <published>2023-10-29T21:58:00+00:00</published>
  </entry>
</feed>
