<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Programming-Languages | AkitaOnRails.com</title><link>https://akitaonrails.github.io/en/tags/programming-languages/</link><description>Fabio Akita's blog — tech, career, and assorted geek topics. English edition.</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 19 Oct 2026 23:20:52 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/en/tags/programming-languages/index.xml" rel="self" type="application/rss+xml"/><item><title>Are There Better Languages for Programming with LLMs?</title><link>https://akitaonrails.github.io/en/2026/10/11/are-there-better-languages-for-programming-with-llms/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/10/11/are-there-better-languages-for-programming-with-llms/</guid><pubDate>Sun, 11 Oct 2026 21:00:00 -0300</pubDate><description>&lt;p&gt;Last week DHH decided to farm engagement with the laziest format there is: he converted Campfire, his chat app, to several languages using AI agents, and started dropping the throughput results one by one on X. In the series he posted, the Rust version came out ahead of the Elixir and Ruby equivalents. The whole thread was designed to provoke, and it provoked.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-partner="tweetdeck"&gt;&lt;p lang="en" dir="ltr"&gt;Last conversion of the Campfire from my token stash goes to JavaScript/Express. As with the others, I'm sure you can find more to optimize. But this gives you a sense of what order of magnitude these options exist within with agent-led development. https://t.co/UUapL99SJE https://t.co/2H2NXfeZog&lt;/p&gt;</description><content:encoded><![CDATA[<p>Last week DHH decided to farm engagement with the laziest format there is: he converted Campfire, his chat app, to several languages using AI agents, and started dropping the throughput results one by one on X. In the series he posted, the Rust version came out ahead of the Elixir and Ruby equivalents. The whole thread was designed to provoke, and it provoked.</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr">Last conversion of the Campfire from my token stash goes to JavaScript/Express. As with the others, I'm sure you can find more to optimize. But this gives you a sense of what order of magnitude these options exist within with agent-led development. https://t.co/UUapL99SJE https://t.co/2H2NXfeZog</p>&mdash; DHH (@dhh) <a href="https://twitter.com/dhh/status/2107077808182976817">October 5, 2026</a></blockquote>
<p>I won&rsquo;t defend him this time. A one-shot conversion, done by an agent, with no fine-tuning at all, doesn&rsquo;t measure language: it measures how much the model knows each stack&rsquo;s idioms. It&rsquo;s rage bait and he knows it. That said, I do the same from time to time, and so do half his critics, so we&rsquo;re all hypocrites and life goes on.</p>
<p>And the weak spot of his methodology became a topic of its own: several people noticed the Rust version had gone through far more agent iterations than the others, meaning more optimization rounds. @kurtome answered in practice: he took the Elixir version and sped up the main page requests 10x, with obvious optimizations that no first conversion does, like caching and async database connection pooling.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">@dhh Here you go https://t.co/gUtK7Z7zG7<br><br>got a 10x speed up on the Room page requests, and sped up all the other requests significantly as well. the biggest gains came from some no brainers: implement caching and pool the db connections with proper async https://t.co/mbp94q9N0s</p>&mdash; kurt (.com) (@kurtome) <a href="https://twitter.com/kurtome/status/2106902762076467681">October 5, 2026</a></blockquote>
<p>His comparison, then, wasn&rsquo;t &ldquo;Rust versus Elixir&rdquo;: it was &ldquo;Rust after N tuning rounds versus Elixir after one.&rdquo; And that lays bare the truth the thread pretends to ignore: <strong>no real software ships from half a dozen prompts</strong>. Coding with AI shines when an experienced engineer steers the agent toward their goal, and that means dozens of iterations, not three.</p>
<p>The good part is that the bait was useful to me. I&rsquo;ve been doing <a href="/en/2026/09/15/new-llm-benchmark-v4-retesting-all-top-llms-part-1/">LLM benchmarks</a> and <a href="/en/2026/10/10/which-jev-like-ai-decision-classifier-model-is-best/">decision-model benchmarks</a>, but I had never measured, in a reproducible way, the question that thread pretended to answer: <strong>does it matter which language you pick when the one writing the code is an LLM?</strong> My short answer was already out in two posts of mine:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Pressure under load is not a trivial problem to solve from scratch and I recommend you do not attempt to. It's a solved problem already and all solutions converge to what Elixir already brings out of the box. So yes. I use Rust all the time, but if I had to solve high scalability problems (particularly high volume message queues), I would think of Elixir first.</p>&mdash; Fabio Akita (@AkitaOnRails) <a href="https://twitter.com/AkitaOnRails/status/2108587462595367353">October 9, 2026</a></blockquote>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Language wars are tiresome. And the reality is simpler if you're doing AI assisted coding.<br><br>- system level (drivers, fs, daemons, etc): Rust. C++ is you're extending existing legacy<br>- windows only desktop apps: C#<br>- iOS/MacOS/iPadOS: Swift<br>- multi-OS: Rust/Tauri, Qt/C++, Flutter, Electron (pick your poison)<br>- simple websites: Node/Typescript/Bun/Deno<br>- highly scalable backend: Elixir, parts in Rust, parts in Go<br>- CLI tools: Rust<br><br>Everything else, by default, Rust. Unless it's an edge case.</p>&mdash; Fabio Akita (@AkitaOnRails) <a href="https://twitter.com/AkitaOnRails/status/2108590592263352540">October 9, 2026</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>There&rsquo;s no better language, there&rsquo;s the right tool<span class="hx:absolute hx:-mt-20" id="theres-no-better-language-theres-the-right-tool"></span>
    <a href="#theres-no-better-language-theres-the-right-tool" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>That list from the post isn&rsquo;t a ranking, it&rsquo;s a terrain map. Every language and every framework was designed for a use case, and none covers them all. That was true when you wrote every line by hand and it stays true with agents. The LLM doesn&rsquo;t change the physics of the problem: it only changes who&rsquo;s typing.</p>
<p>And the map goes beyond language. A real system has infrastructure around it: Kubernetes when scale asks for it, SQLite by default because most apps never need more than that, Postgres with pgBouncer when they do, Redis for cache and key-value, RabbitMQ or NATS for queues. Picking &ldquo;the stack&rdquo; on day zero and expecting it to last ten years is fantasy. It doesn&rsquo;t, and that&rsquo;s fine.</p>
<p>Personal example: new web app, I open Ruby on Rails without thinking twice. It&rsquo;s the most complete end-to-end solution there is for the web, and with an LLM writing most of the code, the framework&rsquo;s productivity weighs even more than the language&rsquo;s. As the system grows, I swap the parts that ask for another tool: a high-volume queue leaves Rails&rsquo; side and goes to something from Erlang land, starting with RabbitMQ, which already solves the hard backpressure part for me.</p>
<blockquote>
  <p>No good software is finished: it grows. Usage dictates what you replace, rewrite and add. The final architecture is never the first commit&rsquo;s, and whoever tries to get everything right in the first commit only produces dead documents.</p>

</blockquote>
<h2>The token superstition<span class="hx:absolute hx:-mt-20" id="the-token-superstition"></span>
    <a href="#the-token-superstition" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>DHH&rsquo;s thread resurrected two beliefs that have been circulating since LLMs became work tools:</p>
<ul>
<li><strong>&ldquo;Script languages are better for AI because the lean syntax spends fewer tokens.&rdquo;</strong></li>
<li><strong>&ldquo;Strongly typed languages are better for AI because the compiler catches the errors, so you need less testing and less repair.&rdquo;</strong></li>
</ul>
<p>Both sound reasonable and both are superstition until someone measures. I was betting against both, from experience: a good compiler helps, but it doesn&rsquo;t do magic, and syntax conciseness was never the bottleneck of an agent that reads and writes files all day.</p>
<p>What really separates a project that moves from a project that spins in circles is test discipline. I&rsquo;ve written that <a href="/en/2026/04/20/clean-code-for-ai-agents/">TDD became a technical obligation, not a philosophy</a>, and this post&rsquo;s benchmark is one more data point in the same direction. It doesn&rsquo;t matter whether you do test-first or test-after: what matters is that the suite grows with the code, that <a href="/en/2026/03/01/software-is-never-done-4-projects-life-after-deploy-one-shot-prompt-myth/">every user-reported bug becomes a regression test</a> before the fix, and that you never make the same mistake twice. Software without users is dead software, so <a href="/en/2026/09/22/stop-making-excuses-and-ship-more-the-premise-changed/">release early, release often</a>, break fast and fix even faster. AI closes exactly that gap: the reproduce-test-fix cycle got cheap. QA became the programmer&rsquo;s most valuable skill, far above &ldquo;knowing Rust syntax.&rdquo;</p>
<p>Discipline beats compiler and beats language. That was my hypothesis. Time to measure.</p>
<h2>The benchmark<span class="hx:absolute hx:-mt-20" id="the-benchmark"></span>
    <a href="#the-benchmark" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I built a reproducible experiment, public in the <a href="https://github.com/akitaonrails/vibe-coding-benchmarks"target="_blank" rel="noopener">vibe-coding-benchmarks repository</a>, with per-attempt results published in JSON and the raw traces kept out of the repo for credential containment. It&rsquo;s not a language championship, and the design itself says so: it&rsquo;s a snapshot of one real task, measuring what can be honestly measured.</p>
<h3>The task<span class="hx:absolute hx:-mt-20" id="the-task"></span>
    <a href="#the-task" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>One task only, on purpose: maintain a multi-tenant <strong>ledger engine</strong>. The agent receives working v1 code and has to take it to a v2 contract, with per-tenant identities, authorization, serializable snapshots, a unique secondary index with alias swap, a capacity-bounded journal, torn-group recovery, checkpointing, legacy schema migration and durable retry IDs. It&rsquo;s not FizzBuzz: it&rsquo;s the kind of system that demands invariants across operations, exactly where LLMs tend to trip.</p>
<p>Correctness is judged by <strong>43 test stories applied after collection</strong>, out of the agent&rsquo;s sight: the 27 public ones from T1 plus 16 hidden ones. Passing is 43 out of 43. The conditions:</p>
<ul>
<li><strong>T0:</strong> prose contract, input and output examples, a working launcher. No test suite.</li>
<li><strong>T1:</strong> the same, plus 27 public stories and a driver to run them.</li>
</ul>
<p>Each attempt had a 15-minute budget, with at most two 3-minute continuations. In practice, none of the 112 needed a continuation.</p>
<h3>The languages and the models<span class="hx:absolute hx:-mt-20" id="the-languages-and-the-models"></span>
    <a href="#the-languages-and-the-models" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Seven languages in three strata, with pinned toolchains and Rust in safe mode:</p>
<ul>
<li><strong>Typed natives:</strong> Rust (safe, pinned), Haskell (pinned GHC).</li>
<li><strong>Typed VMs:</strong> C# (with nullable analysis), Kotlin, TypeScript (<code>strict</code>, <code>noUncheckedIndexedAccess</code>, <code>exactOptionalPropertyTypes</code>).</li>
<li><strong>Dynamic scripts:</strong> Python and Ruby, no checker at all.</li>
</ul>
<p>Four model configurations, all on subscriptions, no paid fallback: <strong>Kimi K3</strong> (high), <strong>GLM 5.3</strong> via OpenCode (high), <strong>Claude Opus 5.5</strong> (medium) and <strong>Codex with gpt-6 Sol</strong> (medium). Crossing everything: 7 languages × 4 models × 2 conditions × 2 repetitions = <strong>112 attempts</strong>.</p>
<h3>What this design doesn&rsquo;t measure<span class="hx:absolute hx:-mt-20" id="what-this-design-doesnt-measure"></span>
    <a href="#what-this-design-doesnt-measure" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Before the numbers, the brakes, because they&rsquo;re part of the result. It&rsquo;s a single task, with two repetitions per cell: a small sample by construction. Token coverage is partial in some models (missing data is not zero). The harness changed mid-campaign across three instrumentation epochs. And provider delays contaminate wall time: one of the Opus attempts spent half the clock waiting for quota release. All numbers below are descriptive, and I point out where they can&rsquo;t support a conclusion.</p>
<p>There&rsquo;s one additional brake, on purpose: <strong>the iteration level</strong>. My design measures what can be reached in all languages with similar iteration levels, on a short budget, with no human tuning afterwards. It&rsquo;s a photograph of the floor, not the ceiling. Any of the 112 implementations probably improves with more agent rounds, and the four queue stacks likewise: what I publish is what each language delivers &ldquo;out of the factory&rdquo; under the same effort, not the maximum a patient engineer would extract from it.</p>
<blockquote>
  <p><strong>Mandatory disclaimer before quoting any number from this post around:</strong> every result that follows holds for <strong>this</strong> benchmark, with <strong>this</strong> task, <strong>these</strong> models, <strong>these</strong> prompts and <strong>this</strong> hardware. Your case may vary violently. If your task doesn&rsquo;t look like a ledger engine, if your model isn&rsquo;t one of these four, if your queue isn&rsquo;t my queue, the numbers don&rsquo;t transfer. The code, the tasks, the grades and the step-by-step to reproduce are all in the <a href="https://github.com/akitaonrails/vibe-coding-benchmarks"target="_blank" rel="noopener">GitHub repository</a>. And if you disagree with some result and that disagreement matters to your company or your project, the only honest attitude is the one I had: <strong>stop arguing and run your own benchmark.</strong></p>

</blockquote>
<h2>Result 1: on correctness, the frontier models tied at the ceiling<span class="hx:absolute hx:-mt-20" id="result-1-on-correctness-the-frontier-models-tied-at-the-ceiling"></span>
    <a href="#result-1-on-correctness-the-frontier-models-tied-at-the-ceiling" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><table>
  <thead>
      <tr>
          <th>Configuration</th>
          <th style="text-align: right">Correct within budget</th>
          <th style="text-align: right">Correct at the limit</th>
          <th style="text-align: right">Failures</th>
          <th style="text-align: right">Ungraded</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Kimi K3 high</td>
          <td style="text-align: right">17</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">2</td>
          <td style="text-align: right">2</td>
      </tr>
      <tr>
          <td>GLM 5.3 high (OpenCode)</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">5</td>
      </tr>
      <tr>
          <td>Opus 5.5 medium</td>
          <td style="text-align: right">25</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">3 (infra interruption)</td>
          <td style="text-align: right">0</td>
      </tr>
      <tr>
          <td>Codex / Sol medium</td>
          <td style="text-align: right">28</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">0</td>
      </tr>
  </tbody>
</table>
<p>Reading: Sol passed all 28 eligible attempts and Opus its 25 (the 3 failures were infrastructure, not code). <strong>53 of 53 eligible attempts correct between the two.</strong> The task, made to be hard, was too easy to separate the frontier models, let alone the languages: with everyone passing everything, there&rsquo;s no correctness ranking to publish.</p>
<p>The difference showed up in the weaker models, and in the direction my hypothesis predicted: <strong>provided tests helped</strong>. Kimi and GLM, with the public suite in hand (T1), delivered 24 correct artifacts against 16 without it (T0). Kimi, in particular, passed all 14 attempts with provided tests. It&rsquo;s descriptive, not causal, but it&rsquo;s the cleanest signal in the whole benchmark: what changed the outcome wasn&rsquo;t the language, it was the presence of tests.</p>
<p>Per language, within each model, correctness scattered between 1/4 and 4/4 with no pattern that repeats across models. The three semantic failures I audited in depth are instructive: a GLM C# and a Kimi Python lost the same story by violating the same cross-operation invariant, and a Kimi Haskell missed a checkpoint path. None of the failures was a type error.</p>
<blockquote>
  <p>Where the model erred, it erred on domain invariants: the kind of error no compiler catches, in typed or dynamic languages. The myth that strong typing spares you the work of understanding the problem dies right there.</p>

</blockquote>
<h2>Result 2: concise language saved no tokens<span class="hx:absolute hx:-mt-20" id="result-2-concise-language-saved-no-tokens"></span>
    <a href="#result-2-concise-language-saved-no-tokens" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Here&rsquo;s the table that kills the first superstition. Medians per language on Sol, four valid attempts per cell:</p>
<table>
  <thead>
      <tr>
          <th>Language</th>
          <th style="text-align: right">Median time</th>
          <th style="text-align: right">Median tokens</th>
          <th style="text-align: right">Final code lines</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>TypeScript</td>
          <td style="text-align: right">53 s</td>
          <td style="text-align: right">147.5 thousand</td>
          <td style="text-align: right">~280</td>
      </tr>
      <tr>
          <td>Python</td>
          <td style="text-align: right">77 s</td>
          <td style="text-align: right">138.2 thousand</td>
          <td style="text-align: right">~300</td>
      </tr>
      <tr>
          <td>C#</td>
          <td style="text-align: right">61 s</td>
          <td style="text-align: right">171.6 thousand</td>
          <td style="text-align: right">~260</td>
      </tr>
      <tr>
          <td>Kotlin</td>
          <td style="text-align: right">65 s</td>
          <td style="text-align: right">170.6 thousand</td>
          <td style="text-align: right">~210</td>
      </tr>
      <tr>
          <td>Haskell</td>
          <td style="text-align: right">75 s</td>
          <td style="text-align: right">184.7 thousand</td>
          <td style="text-align: right">~265</td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td style="text-align: right">82 s</td>
          <td style="text-align: right">185.6 thousand</td>
          <td style="text-align: right">~380</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td style="text-align: right">80 s</td>
          <td style="text-align: right">188.1 thousand</td>
          <td style="text-align: right">~650</td>
      </tr>
  </tbody>
</table>
<p>Ruby, the most concise in lines (a bit over half of Rust&rsquo;s), cost practically the same tokens as Rust on Sol, and on Opus it was <strong>the most expensive of all</strong>, about 205 thousand median tokens against C#&rsquo;s 94 thousand. The cheapest were TypeScript and Python on Sol, C#, Haskell and Kotlin on Opus. If &ldquo;fewer source code lines = fewer LLM tokens&rdquo; were a law, Ruby should have led by a wide margin. It finished last.</p>
<p>And the second superstition? I audited the traces looking for the compiler-guided repair cycles that strong typing supposedly avoids. Result: <strong>zero compilation-error repair cycles</strong> in the audited Opus attempts, across Ruby, Rust and Haskell. Rust needed no compiler-driven fix at all; Haskell had one warning cleanup; Ruby spent its extra tokens on voluntary performance tuning. The frontier model almost always writes code that compiles on the first try, and the real effort goes to verification, not to appeasing the compiler.</p>
<p>One counterpoint that closes the coffin on the &ldquo;cheap is good&rdquo; thesis: GLM&rsquo;s <strong>lowest</strong> token-consumption cells were the failures. TypeScript T0 at 50 thousand tokens looks efficient until you see it&rsquo;s two unmodified starters because the model blew the output limit. Low tokens can mean early surrender.</p>
<h2>Result 3: under load, everyone tied at the queue<span class="hx:absolute hx:-mt-20" id="result-3-under-load-everyone-tied-at-the-queue"></span>
    <a href="#result-3-under-load-everyone-tied-at-the-queue" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now for DHH&rsquo;s question: Rust &ldquo;beats&rdquo; Elixir and Ruby when the subject is throughput. I built the separate track for that: four queue workers against a real RabbitMQ, written with agent help, each in its natural idiom:</p>
<ul>
<li><strong>Elixir</strong> with OTP and Broadway</li>
<li><strong>Rust</strong> with Tokio and Lapin</li>
<li><strong>Rails</strong> with Bunny</li>
<li><strong>Plain Ruby</strong> with Bunny (control)</li>
</ul>
<p>A generator published 10 thousand 4 KB messages to RabbitMQ, at 200 per second, and each worker consumed as fast as it could, hitting the effects on an HTTP/SQLite collector with a nominal 50 ms delay, limited to 2 CPUs and 1 GB of RAM. Two concurrency profiles:</p>
<table>
  <thead>
      <tr>
          <th>Stack</th>
          <th style="text-align: right">Handlers</th>
          <th style="text-align: right">Effects/s</th>
          <th style="text-align: right">Correct/10,000</th>
          <th style="text-align: right">Memory peak</th>
          <th>Drained?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Elixir</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">94.8</td>
          <td style="text-align: right">10,000</td>
          <td style="text-align: right">135 MiB</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">94.6</td>
          <td style="text-align: right">10,000</td>
          <td style="text-align: right">16 MiB</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Rails</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">92.7</td>
          <td style="text-align: right">10,000</td>
          <td style="text-align: right">79 MiB</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">92.5</td>
          <td style="text-align: right">10,000</td>
          <td style="text-align: right">38 MiB</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Elixir</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">84.8</td>
          <td style="text-align: right">7,915</td>
          <td style="text-align: right">161 MiB</td>
          <td>no</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">88.2</td>
          <td style="text-align: right">8,151</td>
          <td style="text-align: right">23 MiB</td>
          <td>no</td>
      </tr>
      <tr>
          <td>Rails</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">88.5</td>
          <td style="text-align: right">8,108</td>
          <td style="text-align: right">87 MiB</td>
          <td>no</td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">88.0</td>
          <td style="text-align: right">8,081</td>
          <td style="text-align: right">60 MiB</td>
          <td>no</td>
      </tr>
  </tbody>
</table>
<p>On the 8-handler profile, the four stacks delivered the same throughput, 93 to 95 effects per second, and drained the whole queue. The apparent bottleneck was the shared collector (a Python lock, a single SQLite writer), not the languages. On the 64-handler profile, all of them left a backlog and blew the client timeout en masse, so I marked those cells as not qualified: the strong protocol, with a group-commit collector and a 50 to 800 msg/s ramp, is designed but not executed.</p>
<p>The only difference left standing in the numbers was <strong>memory</strong>: Rust held the peak at 16 MiB where the BEAM spent 135. It&rsquo;s a real Rust advantage in density, with the caveat that a cgroup peak is not a language footprint. On throughput, which was the thread&rsquo;s claim, a technical tie.</p>
<p>And the tie deserves an honest reading: it may only mean my pressure was too little. With the shared collector holding everything around 95 effects per second, any stack capable of more than that shows up as &ldquo;equal.&rdquo; What this test shows is where the bottleneck was, not where each stack breaks. To find each one&rsquo;s ceiling I&rsquo;d need the strong protocol, with a group-commit collector and a load ramp, which is designed but not executed. Until then, &ldquo;all stacks perform the same&rdquo; is the wrong conclusion; &ldquo;all of them stayed above what my test could measure&rdquo; is the right one.</p>
<p>And the question that interested me most, &ldquo;does the agent work less to deliver a reliable queue consumer in Elixir than in Rust?&rdquo;, was deliberately left unanswered: these four implementations were written to validate the environment, not as a controlled coding attempt. Comparing AI effort between Elixir and Rust would require the same ledger matrix running on the queue task. It&rsquo;s on the list.</p>
<h2>So, does the language matter?<span class="hx:absolute hx:-mt-20" id="so-does-the-language-matter"></span>
    <a href="#so-does-the-language-matter" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>With the data on the table, the thread&rsquo;s three questions can be answered:</p>
<p><strong>Is there a better language for programming with LLMs?</strong> None showed up. In the frontier models, correctness and effort varied less between languages than between models. Where there was a correctness difference, it followed the presence of provided tests, not syntax. &ldquo;No general language ranking is established&rdquo; is the report&rsquo;s official sentence, and it&rsquo;s the correct one.</p>
<p><strong>Do the token-economy theses hold?</strong> Neither one. Conciseness didn&rsquo;t save: Ruby cost as much as Rust, or more. Strong typing spared no repair: there was no type repair to spare. The models&rsquo; effort went to verification and understanding the contract, and that has no language.</p>
<p><strong>Was DHH right?</strong> In what he measured, he measured what an agent delivers for free on each stack, without tuning, and called that a verdict on language. In my test, with real load and honest measurement, the four stacks tied on throughput in the stable regime, and only memory differentiated Rust. If his thread proves anything, it proves that agent conversion measures the model, not the language.</p>
<p>My bets from the introduction came out standing: testing and discipline are the lever, the compiler is a supporting actor, and the right tool depends on the problem. New web app is still Rails in my hand, heavy queue is still Erlang territory, low-level system is still Rust, and the LLM writes any of them with the same competence. Whoever spent the week fighting for their favorite language on X fought over nothing.</p>
<p>And my daily practice tells the same story. I have a personal bias toward Rust and even so I&rsquo;ve published more than 30 open source projects in all kinds of stacks: <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">ai-memory</a> is Rust, <a href="https://github.com/akitaonrails/FrankMD"target="_blank" rel="noopener">FrankMD</a> is Ruby on Rails, <a href="https://github.com/akitaonrails/ai-memory-web"target="_blank" rel="noopener">ai-memory-web</a> and <a href="https://github.com/akitaonrails/ai-jail-web"target="_blank" rel="noopener">ai-jail-web</a> are Node.js. In none of them did I feel any relevant difference that came from the stack: the wins were similar, the errors too, and the errors were always understandable. Every error became an invariant, a rule, a skill improvement. It never became a language switch.</p>
<p>So forget &ldquo;token savings&rdquo; and &ldquo;typing superstition.&rdquo; Do real software engineering: pick the right tool for your specific problem, apply proper engineering technique, and any reasonable agent will perform more or less the same. And stop wasting time arguing over language-war rage bait on the internet. Be a real software engineer and do real engineering. That&rsquo;s it.</p>
<p>The per-attempt data, the full methodology and the evidence limits are in the <a href="https://github.com/akitaonrails/vibe-coding-benchmarks"target="_blank" rel="noopener">repository</a>. If you run the matrix with another task and find a ranking I didn&rsquo;t, show me.</p>
]]></content:encoded><category>llm-benchmarks</category><category>programming-languages</category><category>llms</category><category>elixir-and-erlang</category></item><item><title>New AI-Focused Language Just Released: Bend 2</title><link>https://akitaonrails.github.io/en/2026/09/19/new-ai-language-just-released-bend-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/09/19/new-ai-language-just-released-bend-2/</guid><pubDate>Sat, 19 Sep 2026 12:00:00 -0300</pubDate><description>&lt;p&gt;Two days ago, on September 17, Brazilian researcher Victor Taelin &lt;a href="https://x.com/VictorTaelin/status/2100681226143092875"target="_blank" rel="noopener"&gt;announced on X&lt;/a&gt; the release of &lt;strong&gt;Bend 2&lt;/strong&gt;, hosted at &lt;a href="https://bend-lang.com/"target="_blank" rel="noopener"&gt;bend-lang.com&lt;/a&gt;. The tagline is ambitious: &lt;em&gt;&amp;ldquo;a fast language that blocks AI mistakes via proof: C speed · CUDA parallelism · Lean proofs · Python syntax&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A language that promises to run near C speed, parallelize itself across CPU and GPU, and still formally verify that the code the AI wrote does what you asked. That&amp;rsquo;s a lot of promises stacked together. I had never touched Bend 1, so this is new territory for me too: on launch day, I pointed my agents at two of my Rust projects and told them to port them to Bend 2. The result of that test is the most interesting part of this article.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Two days ago, on September 17, Brazilian researcher Victor Taelin <a href="https://x.com/VictorTaelin/status/2100681226143092875"target="_blank" rel="noopener">announced on X</a> the release of <strong>Bend 2</strong>, hosted at <a href="https://bend-lang.com/"target="_blank" rel="noopener">bend-lang.com</a>. The tagline is ambitious: <em>&ldquo;a fast language that blocks AI mistakes via proof: C speed · CUDA parallelism · Lean proofs · Python syntax&rdquo;</em>.</p>
<p>A language that promises to run near C speed, parallelize itself across CPU and GPU, and still formally verify that the code the AI wrote does what you asked. That&rsquo;s a lot of promises stacked together. I had never touched Bend 1, so this is new territory for me too: on launch day, I pointed my agents at two of my Rust projects and told them to port them to Bend 2. The result of that test is the most interesting part of this article.</p>
<p>Before we start, the disclaimer: this is a review written on the <strong>second day of the language&rsquo;s life</strong>, based on a one-day experiment. I may be wrong about several of my assumptions, and that&rsquo;s fine. The comments are down below; feel free to correct and add to it.</p>
<h2>Who Victor Taelin is<span class="hx:absolute hx:-mt-20" id="who-victor-taelin-is"></span>
    <a href="#who-victor-taelin-is" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>First things first, the author deserves an introduction. <a href="https://github.com/VictorTaelin"target="_blank" rel="noopener">Victor Taelin</a> is a Brazilian functional programming researcher who spent more than a decade obsessed with one specific problem: optimal reduction of the lambda calculus, Lamping&rsquo;s 1990 algorithm, and then Lafont&rsquo;s interaction nets.</p>
<p><a href="https://gist.github.com/VictorTaelin/77fd5a2a8a4a07e1da6157ebca3c7cf1"target="_blank" rel="noopener">His own autobiography</a> tells the story: he worked at the Ethereum Foundation, created the Formality proof language, retired early on the ETH he stacked from salary, went back to the problem on his own dime, created HVM (the Higher-Order Virtual Machine), and founded the Higher Order Company, which raised a <a href="https://x.com/VictorTaelin/status/1743751536465903795"target="_blank" rel="noopener">$4 million seed</a> to turn the research into a product.</p>
<p>And here&rsquo;s a point that matters a lot to me: <strong>I love seeing a Brazilian doing serious programming language design work</strong>. We barely have real cutting-edge research in this country, let alone in computing fundamentals. For that alone, Taelin already has my respect and my congratulations for what he&rsquo;s built so far.</p>
<p>So let it be on record: every criticism in this article is constructive, from someone rooting for Bend to go further. Nothing against him or the project.</p>
<h2>Where Bend comes from<span class="hx:absolute hx:-mt-20" id="where-bend-comes-from"></span>
    <a href="#where-bend-comes-from" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The first version of Bend showed up in <a href="https://news.ycombinator.com/item?id=40390287"target="_blank" rel="noopener">May 2024</a>, with Python-like syntax running on top of HVM2, and the promise of automatic parallelism: write sequential code, the runtime spreads it across every core and even the GPU, no threads, no locks, no mutexes.</p>
<p>The launch went viral. Almost a thousand points on Hacker News, over 20 thousand GitHub stars, a Fireship video. And then reality hit: the <a href="https://news.ycombinator.com/item?id=40390287"target="_blank" rel="noopener">HN thread</a> was merciless. 24-bit numbers, no FFI, a simple sum taking 42 minutes on a user&rsquo;s machine, single-core C++ beating the demo&rsquo;s RTX 4090. Taelin himself admitted back then that the codegen was bad, and admitted now, in the <a href="https://news.ycombinator.com/item?id=49746163"target="_blank" rel="noopener">Bend 2 thread</a>, that the 2024 hype didn&rsquo;t convert into usage: <em>&ldquo;there is also my own failure into making the language actually be used, rather than just a viral moment&rdquo;</em>.</p>
<p>Bend 2 is the answer to that. And pay attention: <strong>it&rsquo;s a different language</strong>. The README is explicit that Bend 1 programs don&rsquo;t load. Interaction-graph evaluation (HVM) is out; native compilation to a single C file is in, with clang for CPU and Metal or CUDA for GPU. Numbers are now real Nat, U32, and F32, and the memory limit jumped from 2 GB to 8 TB, according to <a href="https://gihyo.jp/article/2026/09/bend-2"target="_blank" rel="noopener">gihyo&rsquo;s Japanese announcement</a>. Interaction nets stay in the architecture, but in his words, <em>&ldquo;inets live in it architecturally, but they don&rsquo;t exist at runtime&rdquo;</em>.</p>
<h2>What Bend 2 promises<span class="hx:absolute hx:-mt-20" id="what-bend-2-promises"></span>
    <a href="#what-bend-2-promises" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The new pitch now revolves around AI. The project page sells a <em>&ldquo;post-AGI&rdquo;</em> vision: humans will stop writing and reading code, so we need an ambiguity-free way to tell the AIs what we want. Bend&rsquo;s solution is splitting the program into two files:</p>
<ul>
<li><code>LAWS.bend</code>: the system&rsquo;s invariants, written by the human.</li>
<li><code>PROOF.bend</code>: the proofs of those laws, written by the AI.</li>
</ul>
<p>Bend&rsquo;s type checker is a proof checker, in the lineage of Lean and Rocq, so <code>bend PROOF.bend</code> becomes a commit gate: if the proof doesn&rsquo;t close, the code doesn&rsquo;t ship. The site&rsquo;s demo is a little game with the law <em>&ldquo;winning is impossible&rdquo;</em> and a direct invitation: <em>&ldquo;Skeptical? Try breaking the game&rdquo;</em>. The positioning is explicit: <strong>LAWS.bend is an AGENTS.md with mathematical backing</strong>.</p>
<p>And what does it look like? The syntax feels like Python with dependent types. A complete program with effects:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">Base</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Performs effects on the CPU.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">IO</span><span class="p">(</span><span class="n">Unit</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">do</span> <span class="n">IO</span><span class="o">&lt;</span><span class="n">Unit</span><span class="o">&gt;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span> <span class="p">:</span> <span class="n">String</span> <span class="o">&lt;-</span> <span class="n">IO</span><span class="o">.</span><span class="k">try</span><span class="p">(</span><span class="n">String</span><span class="p">,</span> <span class="n">IO</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;USER&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">IO</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="s2">&#34;Hello, &#34;</span> <span class="o">++</span> <span class="n">name</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Parallelism is divide-and-conquer: you split the work in two and the runtime spreads it over whatever cores it finds. And the <code>!</code> operator dispatches the function to the GPU:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Computes 2^d in parallel: a tree of d levels, one leaf per unit.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pow2</span><span class="p">(</span><span class="o">+</span><span class="n">d</span><span class="p">:</span> <span class="n">Nat</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">U32</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">match</span> <span class="n">d</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">0</span><span class="n">n</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">p</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">a</span> <span class="n">b</span> <span class="o">=</span> <span class="n">pow2</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="n">pow2</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="p">:</span> <span class="n">U32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Runs pow2 on the GPU, via `!`.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">IO</span><span class="p">(</span><span class="n">Unit</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">result</span> <span class="o">=</span> <span class="n">pow2</span><span class="err">!</span><span class="p">(</span><span class="mi">20</span><span class="n">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">IO</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="n">U32</span><span class="o">.</span><span class="n">show</span><span class="p">(</span><span class="n">result</span><span class="p">))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice the differences from mainstream languages: there&rsquo;s no <code>if</code> (every branch is a <code>match</code> on <code>True</code>/<code>False</code>), natural numbers pattern-match on the successor pattern (<code>1n+p</code>), and the <code>+d</code> marks the parameter as <strong>affine</strong>, which is what lets the language live without a garbage collector.</p>
<p>And proofs are ordinary defs. The law declares what holds; the proof is induction with explicit rewrites:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># CLAIM: for every nat x, x + 0 equals x.</span>
</span></span><span class="line"><span class="cl"><span class="n">law</span> <span class="n">add_zero</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="n">x</span><span class="p">:</span> <span class="n">Nat</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">Nat</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">0</span><span class="n">n</span><span class="p">)</span> <span class="o">==</span> <span class="n">x</span> <span class="p">:</span> <span class="n">Nat</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># PROOF: induction on `x`, one rewrite (`%`) per step.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">add_zero</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="k">match</span> <span class="n">x</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">0</span><span class="n">n</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="o">==</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">xp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="o">%</span><span class="n">add_zero</span><span class="p">(</span><span class="n">xp</span><span class="p">)</span> <span class="p">:</span> <span class="p">{</span><span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">Nat</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">xp</span><span class="p">,</span> <span class="mi">0</span><span class="n">n</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">_</span> <span class="p">:</span> <span class="n">Nat</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="o">==</span><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No tactics, no inference, everything annotated by hand. It&rsquo;s verbose on purpose: that&rsquo;s the price of the 0.1s checker.</p>
<p>The other promises, all with benchmarks on the page:</p>
<ul>
<li><strong>Runs fast</strong>: Game of Life on an M4 Max in 7.8s on one core, against C&rsquo;s 6.78s. On the GPU, 0.06s, or 124x.</li>
<li><strong>Checks fast</strong>: 3,200 generic instantiations checked in 0.38s, against Lean&rsquo;s 19.2s and Rocq&rsquo;s 6.04s. The idea is that the agent can verify after every edit.</li>
<li><strong>Parallelizes by itself</strong>: the <code>!</code> operator sends the function to the GPU, no threads, no locks, no hand-written kernels.</li>
<li><strong>No garbage collector</strong>: affine types, each closure can be called at most once, so memory resolves statically.</li>
</ul>
<p>Two honest details I liked seeing published: the site itself warns <em>&ldquo;Bend is still evolving. Expect bugs&rdquo;</em>, and Taelin stated that <a href="https://gihyo.jp/article/2026/09/bend-2"target="_blank" rel="noopener">the compiler is 99% AI-written</a>, with only the checking kernel audited by humans. Consistent with his thesis, at least.</p>
<p>And there&rsquo;s already a business plan: the <a href="https://bend-lang.com/bender"target="_blank" rel="noopener">Bender</a>, a paid, proprietary <em>&ldquo;proving agent&rdquo;</em>, a harness on top of Anthropic and OpenAI models today, with a dedicated model and its own symbolic prover on the roadmap.</p>
<h2>Not everyone swallowed the benchmarks<span class="hx:absolute hx:-mt-20" id="not-everyone-swallowed-the-benchmarks"></span>
    <a href="#not-everyone-swallowed-the-benchmarks" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The technical reception had strong caveats, and they matter. Nezk&rsquo;s gist, <a href="https://gist.github.com/Nezk/dda0511c492cf9bd673885f0341dca0e"target="_blank" rel="noopener">&ldquo;Why the benchmarks of Bend&rsquo;s 2 typechecker are misleading (and possible problems with its type system)&rdquo;</a>, points out that Bend skips elaboration, unification, and implicit arguments, which is exactly where Lean and Rocq spend their time. Comparing times like that would be comparing an apple to a rocket.</p>
<p>Heavier: Liam Powell&rsquo;s post, <a href="https://blog.liampwll.com/posts/bend_vibe_coding/"target="_blank" rel="noopener">&ldquo;Bend 2 and the Vibe-Coding Trap&rdquo;</a>, notes that the expression <em>&ldquo;formal verification&rdquo;</em> appears nowhere on Bend&rsquo;s site or code, redoes the game demo in SPARK/Ada proving the same two properties automatically with GNATprove (<em>&ldquo;all checks proved (12 checks)&rdquo;</em>), without the 442 lines of manual proof, and concludes that Bend is <em>&ldquo;decades behind the state of the art&rdquo;</em>.</p>
<p>These are criticisms from people who know the subject, and they deserve an answer. That said, Bend 2&rsquo;s main HN thread sits at <a href="https://news.ycombinator.com/item?id=49746163"target="_blank" rel="noopener">over 600 points</a>, so attention the project has.</p>
<h2>What I tested<span class="hx:absolute hx:-mt-20" id="what-i-tested"></span>
    <a href="#what-i-tested" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A tagline doesn&rsquo;t compile a program, so I went and tested. I have a <a href="https://github.com/akitaonrails/bend-tests"target="_blank" rel="noopener"><code>bend-tests</code></a> repository where I pointed my agents at porting real projects of mine, written in Rust, to Bend 2 (v2.0.16, pinned via mise; to get a sense of the maturity, the language had nine releases in ten hours on GitHub the day after launch). The documentation for the whole experiment lives in <code>docs/</code> in there, and that&rsquo;s where the next sections come from.</p>
<h3>The three candidates<span class="hx:absolute hx:-mt-20" id="the-three-candidates"></span>
    <a href="#the-three-candidates" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I picked three of my projects with different profiles. Fair warning: none of the three is a worthy Bend example, and I know it. I just grabbed small, simple projects I had lying around, somewhat at random, to see where the language breaks when you rub ordinary day-to-day code against it.</p>
<table>
  <thead>
      <tr>
          <th>Project</th>
          <th>Fit</th>
          <th>Why</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>ghpending</td>
          <td>bad</td>
          <td>90% HTTP, JSON, and formatting; a single small function worth proving</td>
      </tr>
      <tr>
          <td>clock-tui</td>
          <td>bad</td>
          <td>terminal IO from start to finish; the arithmetic is U32, unprovable in practice without a mathlib</td>
      </tr>
      <tr>
          <td>ai-jail</td>
          <td>worst</td>
          <td>namespaces, mounts, seccomp, exec: almost everything is a syscall</td>
      </tr>
  </tbody>
</table>
<p><strong>ghpending</strong> is my CLI that lists open issues and PRs across my repositories via GraphQL. The port covers the main command, the filters, the sorting, and the config.toml, with output verified <strong>byte-for-byte identical</strong> to the Rust binary in the cases tested against the real GitHub API (on my loaded config, the only difference is the fork view the port left out).</p>
<p><strong>tclock</strong> is my fork of clock-tui, a terminal clock with timer and stopwatch. The port covers clock, timer, and stopwatch, without the widget system (which alone is 2,075 lines of the Rust), verified by driving the binary through a pty.</p>
<p><strong>ai-jail</strong>, my sandbox for AI agents (bubblewrap, Landlock, seccomp), never even became code: the paper evaluation killed it. A port would be a C program with a Bend <code>main</code>, which defeats the purpose. And it had the most interesting part to prove: the policy logic, like <em>&ldquo;path resolution never escapes the root&rdquo;</em>. But there&rsquo;s no way to embed that in the Rust binary, so it stays for a next round.</p>
<h3>The numbers<span class="hx:absolute hx:-mt-20" id="the-numbers"></span>
    <a href="#the-numbers" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I measured Rust and Bend side by side on both ports:</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>Rust</th>
          <th>Bend</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>ghpending, lines of code</td>
          <td>1,962 (no tests)</td>
          <td>1,068 Bend + 93 laws/proofs + 109 C + 25 JS</td>
      </tr>
      <tr>
          <td>ghpending, dependencies</td>
          <td>278 crates in the lockfile</td>
          <td>dynamic libcurl, nothing else</td>
      </tr>
      <tr>
          <td>ghpending, binary</td>
          <td>6.8 MB</td>
          <td>1.4 MB</td>
      </tr>
      <tr>
          <td>ghpending, clean build</td>
          <td>57s</td>
          <td>62s (0.1s of checking; the rest is clang -O3 on the generated C)</td>
      </tr>
      <tr>
          <td>ghpending, run with 43 repos</td>
          <td>3.9s wall / 16ms CPU</td>
          <td>2.1s wall / 28ms CPU</td>
      </tr>
      <tr>
          <td>tclock, binary</td>
          <td>6.7 MB</td>
          <td>115 KB</td>
      </tr>
      <tr>
          <td>tclock, idle 5s</td>
          <td>333ms CPU, 10 MB</td>
          <td>9ms CPU, 6.6 MB</td>
      </tr>
  </tbody>
</table>
<p>Read that table carefully, because it lies. The runs are network-bound. The 43-repo gap is Rust doing more work (fork lookups, sequential viewer query). Bend&rsquo;s tclock redraws whole frames, 26 KB/s against ratatui&rsquo;s 2 KB/s of diffs. And the lower line count is partly because the port does less. <strong>No number here shows Bend faster than Rust on the same work.</strong></p>
<h3>The proofs in practice<span class="hx:absolute hx:-mt-20" id="the-proofs-in-practice"></span>
    <a href="#the-proofs-in-practice" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Here&rsquo;s the part Rust can&rsquo;t imitate, so it&rsquo;s worth the detail. The design that worked was <strong>proving an isolated guard, leaving the algorithm outside the proof</strong>. ghpending&rsquo;s <code>--limit</code> allocator ended up in two stages: the first is the original algorithm in U32, unproven; the second is a Nat guard that takes the first stage&rsquo;s proposal and cuts any excess. The laws say the budget is conserved and that no repo gets more items than it has.</p>
<p>This is the real guard, from the port&rsquo;s <code>alloc.bend</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># the lesser of b and w</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">take</span><span class="p">(</span><span class="n">b</span><span class="p">:</span> <span class="n">Nat</span><span class="p">,</span> <span class="n">w</span><span class="p">:</span> <span class="n">Nat</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Nat</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">match</span> <span class="n">b</span> <span class="n">w</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">0</span><span class="n">n</span> <span class="n">w0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">0</span><span class="n">n</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span> <span class="mi">0</span><span class="n">n</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">0</span><span class="n">n</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">wp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">take</span><span class="p">(</span><span class="n">bp</span><span class="p">,</span> <span class="n">wp</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># what is left of b once w is taken from it</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rest</span><span class="p">(</span><span class="n">b</span><span class="p">:</span> <span class="n">Nat</span><span class="p">,</span> <span class="n">w</span><span class="p">:</span> <span class="n">Nat</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Nat</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">match</span> <span class="n">b</span> <span class="n">w</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">0</span><span class="n">n</span> <span class="n">w0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">0</span><span class="n">n</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span> <span class="mi">0</span><span class="n">n</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">wp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">rest</span><span class="p">(</span><span class="n">bp</span><span class="p">,</span> <span class="n">wp</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<blockquote>
  <p>The law holds even if the unproven stage proposes garbage. The guard doesn&rsquo;t trust arithmetic it can&rsquo;t see. That&rsquo;s the pattern that makes proofs work in real code.</p>

</blockquote>
<p>The mutation test is the honest measure of the thing. I injected an off-by-one into the guard that leaks budget: the three sample outputs came out identical, so a test suite of that size would have passed it. The proof gate rejected it, with the exact term that stopped matching. That&rsquo;s Bend working as promised, down to the detail.</p>
<p>Now the price: 93 lines of laws and proofs to guarantee two simple facts about a guard of some 30 lines, starting from zero, because Base ships no Nat lemmas (the only arithmetic lemmas are <code>U32.add_comm</code> and <code>Word.add_comm</code>, which don&rsquo;t help in a Nat proof; we had to prove addition associativity by hand, and Taelin acknowledged in the thread: <em>&ldquo;We need a mathlib!&rdquo;</em>).</p>
<p>And what stayed outside the proof: the algorithm&rsquo;s exactness (it would need division lemmas, days of work), everything U32 (underneath it&rsquo;s a 32-bit vector; proving arithmetic on it without a lemma library is impractical), and literally everything else in the program: JSON, sorting, rendering, IO. The provable surface of a program like ghpending is a few percent of the code.</p>
<p>The checker, on the other hand, is the best part of the tool: 0.1s per round, errors pointing at one exact line. It took some 30 check rounds on ghpending. For an agent iterating in a loop, it&rsquo;s exactly the feedback format it needs.</p>
<h3>The price of escaping to C<span class="hx:absolute hx:-mt-20" id="the-price-of-escaping-to-c"></span>
    <a href="#the-price-of-escaping-to-c" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Everything Base lacks becomes a <em>foreign effect</em>: a Bend def whose body is a C function (for the native binary) and a JS twin (for the dev runner). The two ports needed <strong>eight</strong> hand-written effects:</p>
<ul>
<li><code>Http.post</code>: because there&rsquo;s no TLS, HTTP, or DNS (the official HTTP demo hard-codes an IP).</li>
<li><code>Clock.unix</code> and <code>Clock.local</code>: because <code>IO.now</code> is milliseconds since boot, with no wall clock and no timezone.</li>
<li><code>Tty.width</code>, <code>Tty.raw</code>, <code>Tty.key</code>, <code>Tty.size</code>: because there&rsquo;s no stdin, no raw mode, no terminal query. None of that exists.</li>
<li><code>Clock.millis</code>: convenience.</li>
</ul>
<p>And the official guide is clear about the trade: <strong>there&rsquo;s no ABI promise</strong>. The names are runtime internals and any release can rename them, so <em>&ldquo;rebuild your effects with every update&rdquo;</em>. In a language with nine releases in ten hours, good luck with that. Details that complete the picture: there&rsquo;s no flag to link an extra library (ghpending uses a <code>$CC</code> wrapper that injects <code>-lcurl</code>), custom handle types are WONTFIX, and including a C header mid-file worked by luck, because the runtime defines short names like <code>lock</code> and <code>u32</code>.</p>
<p>Here&rsquo;s where the conceptual point lands: <strong>the moment you cross the FFI boundary, the proof stays behind</strong>. The checker guarantees what&rsquo;s inside its model; the C code on the other side is an act of faith. Rust has exactly the same problem with <code>unsafe</code>, where the borrow checker&rsquo;s guarantees hold up to the edge of the block and inside it the proof is on you. If you want strong end-to-end guarantees, the C boundary has to be minimal or nonexistent, and in Bend today it&rsquo;s enormous for any real program.</p>
<h3>Where Bend fits<span class="hx:absolute hx:-mt-20" id="where-bend-fits"></span>
    <a href="#where-bend-fits" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crossing the three projects with what the language offers, the winning shape is <strong>a small pure core, with a sharp spec that tests cover poorly, inside a thin shell of effects</strong>:</p>
<ul>
<li>allocators, schedulers, rate limiters, quota logic</li>
<li>deterministic game or simulation rules, especially with rollback netcode (the only domain where Base already has what&rsquo;s needed: UDP, windowing, audio)</li>
<li>consensus state machines, ledgers, matching engines</li>
<li>parsers and codecs with a round-trip law</li>
<li>CRDT merge functions (commutative, associative, idempotent)</li>
<li>access policy evaluators (like ai-jail&rsquo;s)</li>
</ul>
<p>And the design center the authors declare: <strong>the AI writes the code and the proofs, the human writes only the LAWS.bend, and the checker arbitrates</strong>. The shortest honest description I found: a <em>proof-carrying language for AI-written kernels</em>. Compared with Lean or Coq, which prove well and deliver applications poorly, Bend is far more executable.</p>
<p>One thing that surprised me in my own numbers: at first I&rsquo;d assume the natural niche was academic math research. It&rsquo;s the opposite. For mathematics it loses badly to Lean 4, Coq, and Isabelle: no tactics, almost no inference, nothing at mathlib&rsquo;s scale, no reals, and a foundation (<code>Type : Type</code>, no positivity check) that would make a mathematician&rsquo;s hair stand on end. What it does is <strong>software verification</strong>, closer to Dafny, F*, or Liquid Haskell than to a proof assistant.</p>
<p>Two limitations weigh even in that niche: Bend emits a whole program instead of a library, so the proven core can&rsquo;t be linked into a Rust application today; and the cost of proving without a lemma library is high.</p>
<h3>What would change the verdict<span class="hx:absolute hx:-mt-20" id="what-would-change-the-verdict"></span>
    <a href="#what-would-change-the-verdict" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Recorded in <code>use-cases.md</code>, the list of what would make me reevaluate:</p>
<ul>
<li>library output with a C ABI, so a proven core can live inside a Rust or C program</li>
<li>U64/I64 and F64</li>
<li>a lemma library for Nat, lists, and ordering</li>
<li>stdin, subprocesses, TLS, and JSON in Base, or a package hub that has them</li>
<li>incremental or faster native builds (62s for 1,000 lines today)</li>
<li>an effects ABI that survives a release</li>
</ul>
<h2>The unvarnished reality<span class="hx:absolute hx:-mt-20" id="the-unvarnished-reality"></span>
    <a href="#the-unvarnished-reality" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now my reading, and you already know I don&rsquo;t sugarcoat.</p>
<p><strong>It&rsquo;s a newborn language.</strong> It launched two days ago. Outside the language&rsquo;s own repo, a GitHub search today finds only a few dozen 48-hour spikes, almost all with zero stars, nothing that looks like a real project. The repo&rsquo;s 21 thousand stars are almost all inherited from Bend 1; the repo was renamed. The site&rsquo;s demos are all toys: a little game, Game of Life, pow2. They&rsquo;re demonstrations of possibility, with no real usage yet.</p>
<p><strong>The stdlib needs to grow by an order of magnitude.</strong> A language&rsquo;s strength lives in its standard library and ecosystem. The only historical exception to that rule was JavaScript, and that&rsquo;s a can of worms that doesn&rsquo;t fit in this article.</p>
<p><strong>The killer app is missing.</strong> Every language that made it had one. My tests point to the niche (proven pure cores, written by AI), but someone needs to turn that into an app the world wants to use. And here&rsquo;s a concrete suggestion, free of charge: Taelin could pick popular open source software he thinks would be better in Bend and rewrite it. Taelin is the exact opposite of the stubborn maintainer who turns his nose up at AI: he designed the whole language around AI. So put that to work: grab a ripgrep, a jq, a sqlite, rewrite it with AI in Bend, and publish the side-by-side comparison. Performance, lines of code, and above all what the proofs guarantee that the original doesn&rsquo;t. A real-world before/after like that is worth more than a thousand little game demos. And as a bonus, every one of those rewrites hands back the most valuable feedback there is: which stdlib holes actually hurt in a real project. Nothing reprioritizes what to implement in the next version better than real code hitting what&rsquo;s missing.</p>
<p><strong>Don&rsquo;t expect webapps, mobile, or anything day-to-day anytime soon.</strong> Maybe that&rsquo;s not even the goal, and that&rsquo;s fine too. But the <em>&ldquo;C speed&rdquo;</em> promise cuts the other way: if the differentiator is performance, it&rsquo;s easier to go straight to C, especially now, with an LLM writing the code for you.</p>
<p>And here&rsquo;s my concrete situation. I use Rust a ton. I <strong>hate</strong> the syntax, and I find the ergonomics miserable. But since the LLM writes the code and I don&rsquo;t have to deal with it by hand, I tolerate it, and in exchange I get performance, good-enough guarantees, and a gigantic ecosystem of crates and frameworks. Ecosystem. That&rsquo;s what decides it.</p>
<blockquote>
  <p>I don&rsquo;t pick a language by the elegance of the paper. I pick it by the size of the ecosystem I don&rsquo;t have to write by hand.</p>

</blockquote>
<h2>The proof-and-GPU focus, and the doubt that remains<span class="hx:absolute hx:-mt-20" id="the-proof-and-gpu-focus-and-the-doubt-that-remains"></span>
    <a href="#the-proof-and-gpu-focus-and-the-doubt-that-remains" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>One thing nagged me the whole time: Taelin clearly concentrates everything on two pillars, proofs and GPU, and I wasn&rsquo;t sure what that meant for the language&rsquo;s scope. I went and read what he himself said in the <a href="https://x.com/VictorTaelin/status/2100374221671051472"target="_blank" rel="noopener">pre-launch thread</a> and on HN, and the story is more interesting than I assumed.</p>
<p>The GPU pillar is research inheritance. Ten years of interaction nets and optimal reduction, from HVM to here. But there&rsquo;s a nuance that surprised me: Bend 2 consciously <strong>betrays</strong> his academic idealism. In his own words: <em>&ldquo;Bend2 is designed to be practical, not idealistic&rdquo;</em>, closer to C than to Haskell, because he couldn&rsquo;t make interaction nets beat the lower-order variants on common hardware. The GPU stayed as the practical application of the obsession, without becoming a fetish.</p>
<p>The proof pillar is older than the LLM era: it goes back to the Ethereum Foundation days, immutable contracts holding millions with a bug, which produced Formality. The LLM era just gave the old obsession a market outfit: <em>&ldquo;the same capabilities that proved Navier-Stokes will now be writing real proofs that your own apps are correct&rdquo;</em>.</p>
<p>And here comes my hypothesis that didn&rsquo;t survive the research. I was ready to write that the minimal stdlib was maybe <strong>on purpose</strong>, that the idea was never general-purpose programming, and that my criticism of the library desert was aiming at the wrong target. Wrong me. His words are general ambition, period: <em>&ldquo;the potential to become the most sensible choice for any vibe-coded project&rdquo;</em>, <em>&ldquo;Bend is made for the general public&rdquo;</em>, and the team <em>&ldquo;working hard to [&hellip;] make this language competitive with the mature alternatives in the market&rdquo;</em>. The only deliberate leanness is the checker&rsquo;s, no inference, verbose on purpose. The small Base is day two of life, so much so that he himself asks for a mathlib.</p>
<p>That makes my stdlib criticism bite even <strong>harder</strong>: if the declared goal is to be the choice for any vibe-coded project, the comparison with mature ecosystems is the game he chose to play.</p>
<p>And there&rsquo;s the business angle, which he admits with rare frankness: <em>&ldquo;we ship without a product&rdquo;</em>. The language is the free substrate; the sellable thing is the <a href="https://bend-lang.com/bender"target="_blank" rel="noopener">Bender</a> and the proving infrastructure behind it. Worth keeping an eye on that space.</p>
<p>The doubt that remains, and this one I register with no answer: the generalist ambition is his north star, but the reality I measured pushes Bend toward the niche of AI-written verified cores. Which of the two wins, only time will tell.</p>
<h2>The final word<span class="hx:absolute hx:-mt-20" id="the-final-word"></span>
    <a href="#the-final-word" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>If I could give Taelin one piece of advice, it would be this: the hard part, the research, he&rsquo;s already done. Now comes the less glamorous part, the one that decides whether a language lives or dies: <strong>marketing and ecosystem building</strong>. Adoption, teaching material, killer app, stdlib, mathlib. Going way beyond toys and demos.</p>
<p>The example that comes to mind is Zig. Years on the road and still fighting for adoption, even with immediate practical use: <code>zig cc</code> alone is a trivial drop-in for compiling C, without using any of the language. Real apps use it; Mitchell Hashimoto&rsquo;s <a href="https://ghostty.org/"target="_blank" rel="noopener">Ghostty</a> is the famous case. But Andrew Kelley, the author, is a notoriously difficult figure, from the crowd that turns its nose up at AI, and he clearly doesn&rsquo;t understand ecosystem building, and that holds Zig back. Technical perfectionism doesn&rsquo;t mix with marketing and adoption.</p>
<p>Bend 2 has real potential. Instant checker, proofs catching bugs that tests don&rsquo;t catch, parallelism without ceremony. But potential alone means nothing if adoption doesn&rsquo;t come. I&rsquo;m rooting for it to come, and rooting double because a Brazilian is leading.</p>
<p>And one final provocation, this time for you who read this far. Thin stdlib, zero frameworks, zero third-party libraries, no killer app: all of that stops being a problem when you look from another angle and becomes an opportunity map. Everything missing in Bend is something someone will have to write, and whoever gets there first etches their name into the project&rsquo;s history. If you always wanted to contribute to impactful open source but never found room in the mature projects, where everything already exists, here&rsquo;s an empty lot waiting.</p>
<p>Those are my two cents. Don&rsquo;t take it too seriously.</p>
]]></content:encoded><category>programming-languages</category><category>artificial-intelligence</category><category>rust</category></item><item><title>The Bun in Rust Response Andrew Kelly Should Have Written</title><link>https://akitaonrails.github.io/en/2026/07/09/the-bun-in-rust-response-andrew-kelly-should-have-written/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/07/09/the-bun-in-rust-response-andrew-kelly-should-have-written/</guid><pubDate>Thu, 09 Jul 2026 17:00:00 -0300</pubDate><description>&lt;p&gt;I read Jarred Sumner&amp;rsquo;s official post, &lt;a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener"&gt;&amp;ldquo;Rewriting Bun in Rust&amp;rdquo;&lt;/a&gt;. And I&amp;rsquo;ll start by making my bias clear: I am completely in favor of Bun&amp;rsquo;s decision to rewrite in Rust. Not because Rust won some tribal war against Zig. That&amp;rsquo;s not it. I like Zig a lot. But, for Bun&amp;rsquo;s context, the decision looks understandable, serious, and well executed.&lt;/p&gt;
&lt;p&gt;What I liked most about Jarred&amp;rsquo;s post was the level of detail. He did not sell the idiotic fantasy of writing a prompt like &amp;ldquo;rewrite Bun in Rust&amp;rdquo; and waiting for a miracle. Quite the opposite. What he described was procedure: &lt;code&gt;PORTING.md&lt;/code&gt;, lifetime analysis, &lt;code&gt;LIFETIMES.tsv&lt;/code&gt;, a trial run with three files, dynamic workflows, adversarial reviewers, process correction when the agents started doing stupid things with &lt;code&gt;git stash&lt;/code&gt;, then compiler errors becoming a work queue, then smoke tests, then the whole suite, then multi-platform CI. No irresponsible vibe coding. Engineering using AI as a multiplier.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I read Jarred Sumner&rsquo;s official post, <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">&ldquo;Rewriting Bun in Rust&rdquo;</a>. And I&rsquo;ll start by making my bias clear: I am completely in favor of Bun&rsquo;s decision to rewrite in Rust. Not because Rust won some tribal war against Zig. That&rsquo;s not it. I like Zig a lot. But, for Bun&rsquo;s context, the decision looks understandable, serious, and well executed.</p>
<p>What I liked most about Jarred&rsquo;s post was the level of detail. He did not sell the idiotic fantasy of writing a prompt like &ldquo;rewrite Bun in Rust&rdquo; and waiting for a miracle. Quite the opposite. What he described was procedure: <code>PORTING.md</code>, lifetime analysis, <code>LIFETIMES.tsv</code>, a trial run with three files, dynamic workflows, adversarial reviewers, process correction when the agents started doing stupid things with <code>git stash</code>, then compiler errors becoming a work queue, then smoke tests, then the whole suite, then multi-platform CI. No irresponsible vibe coding. Engineering using AI as a multiplier.</p>
<p>That&rsquo;s why I was disappointed by Andrew Kelley&rsquo;s response, <a href="https://andrewkelley.me/post/my-thoughts-bun-rust-rewrite.html"target="_blank" rel="noopener">&ldquo;My Thoughts on the Bun Rust Rewrite&rdquo;</a>. Andrew is Zig&rsquo;s creator. I wanted to read the best possible technical argument defending Zig. I wanted to see him explain where Bun confused a language problem with an architecture problem. I wanted to see which Zig APIs could have helped, which invariants should have been hardened, where <code>comptime</code>, allocators, arenas, <code>errdefer</code>, <code>DebugAllocator</code>, and Zig&rsquo;s new I/O design could change the analysis.</p>
<p>Instead, a large part of the text becomes an attempt at character assassination. Comments about personality, management, VC, startup culture, backstage gossip. There may even be real resentment there. I don&rsquo;t care. It does not help technically.</p>
<p>So I decided to do the exercise I expected to have read: put Claude Fable 5 and GPT 5.6 Sol as technical arbiters and ask them for the answer Andrew should have written. Not the polite answer. Not the fan-club answer. The technical answer.</p>
<p>Before handing over the microphone, one off-topic note. If you, like me, stared at those beautiful GitHub animations in Bun&rsquo;s post and thought &ldquo;I want that in my projects&rdquo;, I went there and built it. <a href="https://github.com/akitaonrails/github-visualize"target="_blank" rel="noopener"><code>github-visualize</code></a> is a self-hosted dashboard that tracks your repositories and replays commit evolution, the hourly heatmap, and the CI race until everything turns green. It&rsquo;s all open on GitHub.</p>
<p><a href="https://github.com/akitaonrails/github-visualize"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/07/09/bun-zig-rust/github-visualize-commit-timeline.png" alt="Animated commit timeline in github-visualize"  loading="lazy" /></a></p>
<p>But now let&rsquo;s separate support from proof. One thing is to say &ldquo;I would have done something similar&rdquo;. Another is to say &ldquo;it was technically impossible to continue in Zig&rdquo;. To answer that, we need to look at causes: which bugs were really language bugs, which were architecture problems, which were ecosystem costs, and which could have been reduced with different discipline inside Zig itself.</p>
<p>That&rsquo;s where the analysis starts.</p>
<p>First, minimum vocabulary. <strong>Use-after-free</strong> is using memory after freeing it. <strong>Double-free</strong> is freeing the same memory twice. <strong>Leak</strong> is forgetting to free. <strong>Ownership</strong> is who is responsible for destroying a resource. <strong>Lifetime</strong> is how long a reference remains valid. <strong>FFI</strong> is the boundary where Rust or Zig call C/C++ and lose part of the language guarantees. <strong>GC root</strong> is a way to tell the garbage collector: &ldquo;do not collect this object yet&rdquo;. <strong>Pinning</strong> is holding a buffer in place so it cannot be moved, detached, or invalidated while someone still has a pointer to it. <strong>Reentrancy</strong> is when a call enters the runtime again before the previous call has finished.</p>
<h2>What Rust actually buys<span class="hx:absolute hx:-mt-20" id="what-rust-actually-buys"></span>
    <a href="#what-rust-actually-buys" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The strongest thesis in Bun&rsquo;s post is simple: a large percentage of the listed bugs were use-after-free, double-free, or forgotten resources in error paths. In safe Rust, a good portion of that becomes compiler error or automatic cleanup through <code>Drop</code>, as long as the resource is modeled as an owning value. Rust does not prove the general absence of leaks: <code>Rc</code> cycles, <code>mem::forget</code>, <code>Box::leak</code>, and <code>abort</code> still exist. But it makes accidental leaking in error paths much harder.</p>
<p>That is correct. And it is a strong argument.</p>
<p>Rust&rsquo;s strong point here is not magic. It is that Rust forces certain ownership and lifetime relationships to appear in the type, instead of being scattered around as convention. When a value has an owner, when it is moved, when it leaves scope, when a destructor runs, that stops being just an agreement between programmers. The compiler participates.</p>
<p>In Bun&rsquo;s current code, you can see the pattern in miniature. A MySQL byte wrapper stores a pinned JavaScriptCore value, and <code>Drop</code> undoes the pin when the object dies:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">impl</span><span class="w"> </span><span class="nb">Drop</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Bytes</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="o">!</span><span class="bp">self</span><span class="p">.</span><span class="n">pinned</span><span class="p">.</span><span class="n">is_empty</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="c1">// `pinned` is rooted by the caller&#39;s MarkedArgumentBuffer for the
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="c1">// lifetime of this Value (see struct doc); the FFI itself is `safe fn`.
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">JSC__JSValue__unpinArrayBuffer</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">pinned</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// self.slice dropped automatically
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/sql_jsc/mysql/MySQLValue.rs#L172-L180"target="_blank" rel="noopener"><code>src/sql_jsc/mysql/MySQLValue.rs</code></a>. The important detail is not the FFI call itself. It is that cleanup follows the value&rsquo;s owner.</p>
<p>This is the round Rust wins cleanly. In a small project, discipline is enough. In half a million lines, with reentrant callbacks, threadpool, libuv, sockets, TLS, JavaScriptCore, parser, bundler, package manager, and Node API, discipline becomes recurring cost.</p>
<p>Rust probably reduced the invalid state space the team needed to keep in their heads. Real win.</p>
<p>But a real win does not mean total victory.</p>
<h2>Zig was not defenseless<span class="hx:absolute hx:-mt-20" id="zig-was-not-defenseless"></span>
    <a href="#zig-was-not-defenseless" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The weakest argument you can make against Zig is pretending it is C with new syntax. It is not. Zig has explicit mechanisms to address exactly part of these problems.</p>
<p>The most obvious one is <a href="https://ziglang.org/documentation/master/#errdefer"target="_blank" rel="noopener"><code>errdefer</code></a>, the idiomatic mechanism for cleanup on error paths:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="k">fn</span><span class="w"> </span><span class="nf">readThing</span><span class="p">(</span><span class="n">alloc</span><span class="p">:</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">mem</span><span class="p">.</span><span class="n">Allocator</span><span class="p">,</span><span class="w"> </span><span class="n">fail</span><span class="p">:</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="o">!</span><span class="p">[]</span><span class="kt">u8</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kr">const</span><span class="w"> </span><span class="n">buf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">alloc</span><span class="p">.</span><span class="nf">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">16</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">errdefer</span><span class="w"> </span><span class="n">alloc</span><span class="p">.</span><span class="nf">free</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">error</span><span class="p">.</span><span class="n">Boom</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">buf</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">test</span><span class="w"> </span><span class="s">&#34;leak em caminho de erro&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kr">const</span><span class="w"> </span><span class="n">alloc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">testing</span><span class="p">.</span><span class="n">allocator</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">try</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">testing</span><span class="p">.</span><span class="nf">expectError</span><span class="p">(</span><span class="k">error</span><span class="p">.</span><span class="n">Boom</span><span class="p">,</span><span class="w"> </span><span class="nf">readThing</span><span class="p">(</span><span class="n">alloc</span><span class="p">,</span><span class="w"> </span><span class="kc">true</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><a href="https://ziglang.org/documentation/master/#Report-Memory-Leaks"target="_blank" rel="noopener"><code>std.testing.allocator</code> reports leaks</a> in tests. <a href="https://github.com/ziglang/zig/blob/master/lib/std/heap/debug_allocator.zig"target="_blank" rel="noopener"><code>DebugAllocator</code></a> helps find double-free, leaks, and many invalid memory uses, including making bugs noisier by not immediately reusing addresses. This is not the same thing as a static proof of the absence of use-after-free. It is not a borrow checker. It does not cover allocation done inside C++, JavaScriptCore, BoringSSL, or any other external allocator. It only catches what goes through it and only on the paths exercised by tests.</p>
<p>The allocator&rsquo;s own comment is precise about what it provides:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="cs">//! * Captures stack traces on allocation, free, and optionally resize.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//! * Double free detection, which prints all three traces (first alloc, first</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//!   free, second free).</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//! * Leak detection, with stack traces.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//! * Never reuses memory addresses, making it easier for Zig to detect branch</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//!   on undefined values in case of dangling pointers.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/ziglang/zig/blob/master/lib/std/heap/debug_allocator.zig#L3-L12"target="_blank" rel="noopener"><code>debug_allocator.zig</code></a>. It is a good testing and debugging tool. It is not a theorem.</p>
<p>Even so, it is wrong to say Zig had no tools. It did.</p>
<p>The difference is feedback timing. In Rust, a lot appears at compile time. In Zig, a lot appears in tests, fuzzing, instrumented allocators, ASAN, review, and architectural style. In critical code, that matters. Early feedback is better.</p>
<p>But it also matters to say the whole sentence: if the problem was lack of clear invariants in the Zig code, the answer could have been a rewrite in Rust. It could also have been an aggressive restructuring of the Zig.</p>
<p>We do not know which would have been cheaper. We only know which one Bun chose.</p>
<h2><code>Drop</code> is not just sugar, but it is not a miracle either<span class="hx:absolute hx:-mt-20" id="drop-is-not-just-sugar-but-it-is-not-a-miracle-either"></span>
    <a href="#drop-is-not-just-sugar-but-it-is-not-a-miracle-either" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Bun&rsquo;s post is right to contrast <code>defer</code> with <code>Drop</code>.</p>
<p>In Zig, you write:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">var</span><span class="w"> </span><span class="n">resource</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">Resource</span><span class="p">.</span><span class="nf">init</span><span class="p">(</span><span class="n">allocator</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">defer</span><span class="w"> </span><span class="n">resource</span><span class="p">.</span><span class="nf">deinit</span><span class="p">();</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is explicit. It is in your face. There is no hidden control flow. That is Zig&rsquo;s philosophy: if something happens, you should be able to point to where it happens.</p>
<p>In Rust, if the type implements <code>Drop</code>, cleanup follows the owner:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">impl</span><span class="w"> </span><span class="nb">Drop</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Resource</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">deinit</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is not just syntactic sugar. <a href="https://doc.rust-lang.org/reference/destructors.html"target="_blank" rel="noopener">Rust&rsquo;s destruction rules</a> connect <code>Drop</code> to ownership, move semantics, field destruction order, and panic unwind. Ordinary safe code cannot use a value after it has been moved, nor double-drop the object as if it still owned it. It is a language guarantee.</p>
<p>But the guarantee ends where semantics the compiler does not understand begin.</p>
<p>If <code>drop</code> calls an FFI function to unprotect a JavaScriptCore value, Rust guarantees the destructor will run on normal scope and drop paths, not on <code>abort</code>, <code>mem::forget</code>, intentional leaks, or <code>Rc</code> cycles. It does not prove that the JSC pointer is still valid. It does not prove the GC has no special rule. It does not prove an asynchronous C callback will not call back after you destroyed the wrapper.</p>
<p>The comment in the same Bun wrapper shows the size of the contract that still remains outside the compiler:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="sd">/// JS could `transfer()`/detach an earlier ArrayBuffer, or drop the last
</span></span></span><span class="line"><span class="cl"><span class="sd">/// JS reference to it and force GC, while we still hold a borrowed slice
</span></span></span><span class="line"><span class="cl"><span class="sd">/// into it. Pinning the backing `ArrayBuffer` makes it non-detachable for
</span></span></span><span class="line"><span class="cl"><span class="sd">/// the duration, and the caller&#39;s stack-scoped `MarkedArgumentBuffer`
</span></span></span><span class="line"><span class="cl"><span class="sd">/// roots the wrapper so GC can&#39;t sweep the cell. `Drop` unpins.
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Bytes</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">slice</span>: <span class="nc">ZigStringSlice</span><span class="p">,</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/sql_jsc/mysql/MySQLValue.rs#L142-L154"target="_blank" rel="noopener"><code>MySQLValue.rs</code></a>. Notice the size of the contract. It did not disappear because this is Rust. It became documented and encapsulated.</p>
<p>That is the real boundary: Rust improves the Rust side of the contract. It does not perform magic inside the JavaScriptCore heap.</p>
<h2>The FFI boundary is still hell<span class="hx:absolute hx:-mt-20" id="the-ffi-boundary-is-still-hell"></span>
    <a href="#the-ffi-boundary-is-still-hell" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Bun is not a simple CLI. Bun is a JavaScript runtime. It talks to JavaScriptCore, uWebSockets, usockets, BoringSSL, SQLite, compression libraries, system APIs, asynchronous handles, buffers that JavaScript can detach in the middle of a coercion, reentrant callbacks, and a conservative GC.</p>
<p>That is where the debate gets interesting.</p>
<p>Inside an <code>unsafe</code> block, Rust requires discipline. The <a href="https://doc.rust-lang.org/nomicon/ffi.html"target="_blank" rel="noopener">Rustonomicon on FFI</a> itself treats foreign calls and raw pointers as programmer obligations. If the binding is lying, if the real ownership does not match the type, if the foreign callback outlives the Rust object, the compiler does not save you.</p>
<p>Bun&rsquo;s binding documents the preconditions in the declaration itself:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">unsafe</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="sd">/// By-value `JSValue`; C++ side null-checks and reads its own heap state.
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="sd">/// No caller-side preconditions → `safe fn`.
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">safe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">JSC__JSValue__unpinArrayBuffer</span><span class="p">(</span><span class="n">v</span>: <span class="nc">JSValue</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="sd">/// 0 = detached/null, 1 = FastTypedArray (GC-movable — caller should dupe;
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="sd">/// no unpin needed), 2 = pinned ArrayBuffer (caller must unpin).
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/sql_jsc/mysql/MySQLValue.rs#L859-L868"target="_blank" rel="noopener"><code>MySQLValue.rs</code></a>. The language lets you declare a foreign call safe. The obligation to prove that safety remains ours.</p>
<p>But there is an important difference: Rust lets you encapsulate that <code>unsafe</code> behind safe APIs. You can create handle types, <code>NonNull</code>, <code>PhantomData</code>, <code>Pin</code>, lifetimes marking the relationship between wrapper and runtime, wrappers that do not implement <code>Send</code> or <code>Sync</code>, RAII for GC roots, and a smaller surface for audit. The goal is not to eliminate danger. It is to shrink the place where danger lives.</p>
<p>The most didactic example in Bun&rsquo;s post is the <code>uv_close</code> bug. The first version compiled, but handed a <code>Box</code> to libuv to close asynchronously and let the <code>Box</code> fall at the end of the scope. Use-after-free followed by double-free. The fixed version in the current code makes the transfer explicit:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">spawn</span>::<span class="n">WindowsStdioResult</span>::<span class="n">Buffer</span><span class="p">(</span><span class="n">pipe</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// `uv_close` is async — libuv keeps the raw handle pointer until the
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// next loop tick. Leak the Box so it outlives this scope; dropping it
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// here would be a use-after-free + double-free when the callback fires.
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nb">Box</span>::<span class="n">leak</span><span class="p">(</span><span class="n">pipe</span><span class="p">).</span><span class="n">close</span><span class="p">(</span><span class="n">Subprocess</span>::<span class="n">on_pipe_close</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/runtime/api/bun/js_bun_spawn_bindings.rs#L1359-L1366"target="_blank" rel="noopener"><code>js_bun_spawn_bindings.rs</code></a>. It compiled in Rust and was still wrong at the C/async boundary. The review process caught it.</p>
<p>Here <code>leak</code> does not mean &ldquo;leak forever&rdquo;. It means removing the <code>Box</code> from Rust&rsquo;s lexical control and transferring ownership to libuv&rsquo;s asynchronous protocol, whose callback later reconstructs and frees the allocation.</p>
<p>Zig also lets you write good wrappers. The difference is that it does not have the <code>safe</code>/<code>unsafe</code> boundary as part of the language. In Zig, everything is explicit and reviewable. In Rust, a larger part of the contract can become type.</p>
<p>For a large team, that matters. It matters even more when part of the code is being generated by agents.</p>
<h2>Arenas: yes, Bun already used them<span class="hx:absolute hx:-mt-20" id="arenas-yes-bun-already-used-them"></span>
    <a href="#arenas-yes-bun-already-used-them" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Here the pro-Zig argument needs to be honest. Arenas are idiomatic and excellent for certain problems: parser, AST, request lifecycle, data that dies together.</p>
<p>But Bun&rsquo;s post does not ignore arenas. It explicitly says the existing approach was already a mix of lifetimes by arena, reference counting, and &ldquo;pay really close attention&rdquo;. It even cites parser state and AST nodes as good cases for an arena.</p>
<p>The excerpt from the post is this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Our current approach is a mix of:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">- arena lifetimes, where the scope of when it&#39;s accessible is clear
</span></span><span class="line"><span class="cl">  (parser state doesn&#39;t escape the calling function and so AST nodes
</span></span><span class="line"><span class="cl">  are a good choice there)
</span></span><span class="line"><span class="cl">- reference-counting
</span></span><span class="line"><span class="cl">- pay really close attention</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">Bun&rsquo;s official post</a>. The good counter-argument is not &ldquo;use arena&rdquo;. They already did. The good counter-argument is asking whether the remaining boundaries could have been hardened in Zig.</p>
<p>So the correct response is not &ldquo;you forgot arenas&rdquo;. They did not forget.</p>
<p>The correct response is: arena solves the case where lifetime is clear. It does not, by itself, solve a socket handle that closes asynchronously, a callback that runs later, an SSL session that needs a specific <code>free</code>, a GC root, a pointer that may be invalidated by reentrancy, or an object that crosses several API layers with implicit ownership.</p>
<p>Arena is a good tool. It is not a religion.</p>
<p>If Bun had hardened the boundaries more in Zig, maybe part of the rewrite&rsquo;s benefit could have come from that: more restricted APIs, fewer raw pointers crossing subsystems, handles with explicit ownership, allocators instrumented by default in tests, fuzzing aimed at reentrancy, wrappers that force <code>deinit</code>, less &ldquo;pay attention&rdquo;.</p>
<p>But that would also be a rewrite. Just inside Zig.</p>
<h2>The borrow checker helps, but not where the internet thinks<span class="hx:absolute hx:-mt-20" id="the-borrow-checker-helps-but-not-where-the-internet-thinks"></span>
    <a href="#the-borrow-checker-helps-but-not-where-the-internet-thinks" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A lot of people talk about the borrow checker as if it were a universal bug detector. It is not. The borrow checker is the part of the Rust compiler that prevents certain invalid borrows: two mutable owners at the same time, a live reference after the owner dies, use after move. That is a lot. It is not everything.</p>
<p>It does not understand the JavaScriptCore heap. It does not understand every GC rule. It does not automatically understand that a C callback can trigger JS, that JS can detach an <code>ArrayBuffer</code>, that it can reenter the runtime, that it can invalidate a pointer captured earlier. These invariants need to be modeled.</p>
<p>But when they are modeled, Rust charges you.</p>
<p>A small example in Bun: a <code>uSockets</code> wrapper that turns a C pointer into a Rust borrow tied to <code>&amp;mut self</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">impl</span><span class="w"> </span><span class="n">PacketBuffer</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">get_peer</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">index</span>: <span class="nc">c_int</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">sockaddr_storage</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// SAFETY: uSockets guarantees a non-null, properly-aligned peer pointer for
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// indices &lt; packet count. The returned storage lives inside the C-owned packet
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// buffer, which is exclusively loaned to the data callback for its duration; no
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// other Rust or C path holds a reference to it. The reborrow of `&amp;mut self`
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// ties the returned lifetime to this handle, so the borrow checker prevents
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// obtaining a second overlapping `&amp;mut` via `get_peer`/`get_payload`.
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="n">us_udp_packet_buffer_peer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/uws_sys/udp.rs#L200-L209"target="_blank" rel="noopener"><code>src/uws_sys/udp.rs</code></a>. The <code>unsafe</code> still exists. But after the wrapper creates the reference, the borrow checker can prevent certain abuses on the Rust side.</p>
<p>And Bun is not only JavaScript heap. It is parser, bundler, module resolver, package manager, file watcher, HTTP protocol, buffers, caches, tables, queues, process state, glue code. In these parts, local ownership matters a lot. <code>Vec</code>, <code>String</code>, slices, maps, wrappers, enums, finite states: all of that becomes easier to refactor with the compiler slapping your hand.</p>
<p>The honest argument is not &ldquo;the borrow checker solves Bun&rdquo;. It is: the borrow checker reduces many classes of error on Bun&rsquo;s Rust side, and that frees human attention for the part that remains hard.</p>
<p>That is strong enough. No need to exaggerate.</p>
<h2><code>comptime</code>: here Zig has a real advantage<span class="hx:absolute hx:-mt-20" id="comptime-here-zig-has-a-real-advantage"></span>
    <a href="#comptime-here-zig-has-a-real-advantage" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The best technical counterexample in Bun&rsquo;s own post is <code>Output.pretty</code>.</p>
<p>In Zig, the format string could be a <code>comptime</code> parameter. Bun&rsquo;s own post puts the difference like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// Zig:
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="n">inline</span><span class="w"> </span><span class="k">fn</span> <span class="nf">pretty</span><span class="p">(</span><span class="n">comptime</span><span class="w"> </span><span class="n">fmt</span>: <span class="nc">string</span><span class="p">,</span><span class="w"> </span><span class="n">args</span>: <span class="nc">anytype</span><span class="p">)</span><span class="w"> </span><span class="n">void</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Output</span><span class="p">.</span><span class="n">pretty</span><span class="p">(</span><span class="s">&#34;&lt;r&gt;{f}&lt;r&gt;&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">.{</span><span class="n">hyperlink</span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Primeira API Rust:
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">pretty</span><span class="p">(</span><span class="n">payload</span>: <span class="nc">impl</span><span class="w"> </span><span class="n">PrettyFmtInput</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Output</span>::<span class="n">pretty</span><span class="p">(</span><span class="fm">format_args!</span><span class="p">(</span><span class="s">&#34;&lt;r&gt;</span><span class="si">{}</span><span class="s">&lt;r&gt;&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">hyperlink</span><span class="p">));</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This allowed processing color and formatting markers in the literal known at compile time, before mixing runtime arguments. When porting to Rust, the first version became a function. Result: the tag parser touched bytes that came from the arguments. Issue <a href="https://github.com/oven-sh/bun/issues/30693"target="_blank" rel="noopener">#30693</a> shows the regression in port/canary involving an OSC 8 hyperlink escape and <code>&lt;r&gt;</code>, not a failure released in stable.</p>
<p>The bug was exactly the contamination between template and runtime argument:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">TerminalHyperlink::Display emits an OSC 8 hyperlink:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">\x1b]8;;URL\x1b\ai\x1b]8;;\x1b\
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">The closing string terminator ST is the final two bytes `\x1b\`.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">This is then wrapped via `Output::pretty(format_args!(&#34;&lt;r&gt;{}&lt;r&gt;&#34;, hyperlink))`.
</span></span><span class="line"><span class="cl">The rendered string tail is `...\x1b\&lt;r&gt;`.</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/issues/30693"target="_blank" rel="noopener">issue #30693</a>.</p>
<p>Then they fixed it using a macro.</p>
<p>The current code makes the difference explicit:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="sd">/// Write to stdout with `&lt;tag&gt;` color expansion.
</span></span></span><span class="line"><span class="cl"><span class="sd">/// Function form: performs the `&lt;tag&gt;` → ANSI rewrite at runtime on the rendered
</span></span></span><span class="line"><span class="cl"><span class="sd">/// payload (using stdout&#39;s colour state). Prefer the `pretty!` macro for literal
</span></span></span><span class="line"><span class="cl"><span class="sd">/// templates so the rewrite happens at compile time.
</span></span></span><span class="line"><span class="cl"><span class="cp">#[inline(always)]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">pretty</span><span class="p">(</span><span class="n">payload</span>: <span class="nc">impl</span><span class="w"> </span><span class="n">PrettyFmtInput</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">buf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">payload</span><span class="p">.</span><span class="n">into_pretty_buf</span><span class="p">(</span><span class="n">enable_ansi_colors_stdout</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">write_bytes</span><span class="p">(</span><span class="n">Destination</span>::<span class="n">Stdout</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">buf</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And right below:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="sd">/// Compile-time `&lt;tag&gt;` → ANSI escape rewriter.
</span></span></span><span class="line"><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="cl"><span class="sd">/// This must be a proc-macro because it consumes a string literal and emits
</span></span></span><span class="line"><span class="cl"><span class="sd">/// a new string literal usable as a `format_args!` template.
</span></span></span><span class="line"><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="cl"><span class="sd">/// `pretty_fmt!(&#34;&lt;red&gt;{s}&lt;r&gt;&#34;, true)`  → `&#34;\x1b[31m{}\x1b[0m&#34;`
</span></span></span><span class="line"><span class="cl"><span class="sd">/// `pretty_fmt!(&#34;&lt;red&gt;{s}&lt;r&gt;&#34;, false)` → `&#34;{}&#34;`
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="n">bun_core_macros</span>::<span class="n">pretty_fmt</span><span class="p">;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/bun_core/output.rs#L1765-L1805"target="_blank" rel="noopener"><code>src/bun_core/output.rs</code></a>.</p>
<p>This demonstrates a legitimate strength of Zig: <code>comptime</code> is the language itself running at compile time. You do not switch to a parallel system. In Rust, macros are powerful and mature, but they are another mechanism. For some kinds of API, Zig is simply more direct.</p>
<p>But let&rsquo;s not turn a porting bug into universal proof against Rust. The problem there was not &ldquo;Rust is fragile&rdquo;. It was the wrong translation of a <code>comptime</code> API into a runtime function. Bun&rsquo;s own post recognizes that and fixes it with a macro.</p>
<p>The correct reading is simpler: Zig had a structural guarantee there. The first Rust version lost that guarantee. That cost a regression in port/canary.</p>
<p>Point for Zig.</p>
<h2>Binary size and performance: careful with cheap causality<span class="hx:absolute hx:-mt-20" id="binary-size-and-performance-careful-with-cheap-causality"></span>
    <a href="#binary-size-and-performance-careful-with-cheap-causality" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Bun&rsquo;s post shows good numbers: smaller binary, some faster benchmarks, better startup in certain scenarios.</p>
<p>The numbers from the post:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Bun v1.4.0 (canary) — Windows — 76 MB
</span></span><span class="line"><span class="cl">Bun v1.3.14          — Windows — 94 MB
</span></span><span class="line"><span class="cl">Bun v1.4.0 (canary) — Linux   — 70 MB
</span></span><span class="line"><span class="cl">Bun v1.3.14          — Linux   — 88 MB
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Bun.serve  — 169.6k → 177.7k — +4.8%
</span></span><span class="line"><span class="cl">node:http  — 103.8k → 108.5k — +4.5%
</span></span><span class="line"><span class="cl">fastify    —  91.5k →  95.9k — +4.8%</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">Bun&rsquo;s official post</a>.</p>
<p>I believe the numbers. I do not believe in simple attribution.</p>
<p>A rewrite changes many things at the same time. It changes code layout, removes debt, changes toolchain, touches LTO, changes how generics/comptime are instantiated, changes linker, changes dead code, changes ICU, changes internal organization. When all of that happens together, you cannot look at the graph and say: &ldquo;it was Rust&rdquo;.</p>
<p>You also cannot say: &ldquo;it was only the rewrite&rdquo;.</p>
<p>The post itself talks about a combination: Rust, ICU changes, Identical Code Folding, LTO, codegen, accumulated cleanup. LTO is optimization at the final link stage, when the compiler sees more of the program before generating the binary. Identical Code Folding is the linker merging identical functions into one. <a href="https://andrewkelley.me/post/my-thoughts-bun-rust-rewrite.html"target="_blank" rel="noopener">Andrew is right</a> to point out that Zig also supports LTO and that some of these gains could have been pursued before. But that does not prove the result would be the same. It only proves causality is mixed.</p>
<p>Bun&rsquo;s <code>Cargo.toml</code> shows there is explicit build engineering in this story:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># Cargo&#39;s defaults (lto=false, codegen-units=16) leave us at ~105 crates × 16</span>
</span></span><span class="line"><span class="cl"><span class="c"># CGUs ≈ 1680 separately-optimized units with NO cross-crate inlining beyond</span>
</span></span><span class="line"><span class="cl"><span class="c"># `#[inline]`-annotated leaf fns. `lto = &#34;fat&#34;` + `codegen-units = 1` collapses</span>
</span></span><span class="line"><span class="cl"><span class="c"># the whole Rust crate graph into one LLVM module, matching Zig&#39;s shape.</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="s2">&#34;fat&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">codegen-units</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="nx">debug</span> <span class="p">=</span> <span class="s2">&#34;line-tables-only&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">strip</span> <span class="p">=</span> <span class="s2">&#34;none&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">panic</span> <span class="p">=</span> <span class="s2">&#34;abort&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/Cargo.toml#L117-L135"target="_blank" rel="noopener"><code>Cargo.toml</code></a>.</p>
<p>And there is LTO configuration crossing Rust and C++:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Cross-language LTO: rustc emits LLVM bitcode (`-Clinker-plugin-lto`) into
</span></span></span><span class="line"><span class="cl"><span class="cm"> * `libbun_rust.a` so the final lld `-flto=thin` link sees through Rust↔C++
</span></span></span><span class="line"><span class="cl"><span class="cm"> * call edges. When false but `lto` is true, both halves still LTO
</span></span></span><span class="line"><span class="cl"><span class="cm"> * independently (C++ via `-flto=thin`, Rust via `[profile.release] lto =
</span></span></span><span class="line"><span class="cl"><span class="cm"> * &#34;fat&#34;`); only the cross-language inlining is lost.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="nx">crossLangLto</span>: <span class="kt">boolean</span><span class="p">;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/scripts/build/config.ts#L118-L129"target="_blank" rel="noopener"><code>scripts/build/config.ts</code></a>. It is not &ldquo;Rust won&rdquo;. It is language, toolchain, linker, configuration, and code working together.</p>
<p>Same thing for stack usage and parser depth. The post attributes improvement to Rust emitting better <a href="https://llvm.org/docs/LangRef.html#llvm-lifetime-start-intrinsic"target="_blank" rel="noopener"><code>llvm.lifetime.start/end</code></a> and allowing stack slot reuse. Codegen detail? Yes. But a codegen detail delivered by today&rsquo;s toolchain counts. Zig itself still has an <a href="https://github.com/ziglang/zig/issues/23475"target="_blank" rel="noopener">open issue about stack allocations that do not disappear at the end of scope</a>. For the user, it matters little whether the advantage comes from the pure language, the compiler, or backend maturity. What matters is that it works today.</p>
<p>The mechanism described by Bun is this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Rust&#39;s LLVM IR codegen emits LLVM&#39;s llvm.lifetime.start and
</span></span><span class="line"><span class="cl">llvm.lifetime.end intrinsics for stack variables when they are no longer
</span></span><span class="line"><span class="cl">in use, which lets LLVM reuse stack space slots.</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the Zig ticket says, bluntly, that this case is still not solved:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Block stack allocations do not disappear at end of block scope #23475</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>There is also the architectural solution: iterative parser with an explicit stack on the heap. Works in Zig and Rust. But it is more work.</p>
<p>Honest reading: the numbers favor Bun&rsquo;s Rust version in that experiment. They do not prove general language superiority.</p>
<h2>Miri, crates, and the real world<span class="hx:absolute hx:-mt-20" id="miri-crates-and-the-real-world"></span>
    <a href="#miri-crates-and-the-real-world" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>There are Rust advantages that have no direct equivalent in Zig today.</p>
<p>Crates is the obvious one. The Rust ecosystem is large, mature, and practical. For a project the size of Bun, a good off-the-shelf library is real engineering value. Zig is still maturing on this point.</p>
<p><a href="https://github.com/rust-lang/miri"target="_blank" rel="noopener"><code>Miri</code></a> is also a real advantage. It detects classes of undefined behavior in executed Rust code: out-of-bounds, use-after-free, uninitialized data, alignment problems, data races in certain cases. The project itself documents the limits: it is not a formal proof, it evaluates the executions you provide, and it has restricted support for FFI and platform APIs. It does not replace testing. But to validate isolated <code>unsafe</code> abstractions, it is a tool Zig does not have in the same way.</p>
<p>Bun does not merely cite Miri in the abstract. It is in the toolchain:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">toolchain</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">channel</span> <span class="p">=</span> <span class="s2">&#34;nightly-2026-05-06&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">components</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;rust-src&#34;</span><span class="p">,</span> <span class="s2">&#34;rustfmt&#34;</span><span class="p">,</span> <span class="s2">&#34;clippy&#34;</span><span class="p">,</span> <span class="s2">&#34;miri&#34;</span><span class="p">,</span> <span class="s2">&#34;llvm-tools&#34;</span><span class="p">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/rust-toolchain.toml#L1-L24"target="_blank" rel="noopener"><code>rust-toolchain.toml</code></a>.</p>
<p>And there is an internal test explaining exactly the use:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// Run under Miri (`bun run rust:miri -p bun_ptr`): every path here walks raw
</span></span></span><span class="line"><span class="cl"><span class="c1">// pointers through `Box::into_raw` / `heap::take`, so Tree Borrows is what
</span></span></span><span class="line"><span class="cl"><span class="c1">// proves the ref/deref/destructor handoff does not alias or use-after-free.
</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/ptr/ref_count.rs#L1313-L1315"target="_blank" rel="noopener"><code>src/ptr/ref_count.rs</code></a>. It does not prove the whole program. It exercises a dangerous abstraction under a model made to detect undefined behavior on those executed paths.</p>
<p>There is also hiring, tooling, documentation, IDE, review culture, CI, examples, people in the market. This is not a political detail. It is engineering. A language exists inside an ecosystem.</p>
<p>If Bun were a smaller project, with a smaller team, less pressure, and less Node-compatible surface area, maybe I would weigh this differently. But Bun became a runtime used by millions of monthly downloads. The margin for &ldquo;let&rsquo;s maintain perfect discipline&rdquo; shrinks.</p>
<h2>Async: Zig may have a better answer, but it is not Bun&rsquo;s present yet<span class="hx:absolute hx:-mt-20" id="async-zig-may-have-a-better-answer-but-it-is-not-buns-present-yet"></span>
    <a href="#async-zig-may-have-a-better-answer-but-it-is-not-buns-present-yet" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Zig removed the old async/await and is <a href="https://kristoff.it/blog/zig-new-async-io/"target="_blank" rel="noopener">redesigning I/O around <code>std.Io</code></a>, passed by the caller in a way similar to <code>Allocator</code>. The idea is good: avoid classic function coloring, make the function less coupled to blocking, threaded, or event-driven execution, and treat I/O as an explicit capability.</p>
<p>The proposed format is like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">std</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">@import</span><span class="p">(</span><span class="s">&#34;std&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">Io</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">Io</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">fn</span><span class="w"> </span><span class="nf">saveData</span><span class="p">(</span><span class="n">io</span><span class="p">:</span><span class="w"> </span><span class="n">Io</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="kr">const</span><span class="w"> </span><span class="kt">u8</span><span class="p">)</span><span class="w"> </span><span class="o">!</span><span class="kt">void</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kr">const</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">Io</span><span class="p">.</span><span class="n">Dir</span><span class="p">.</span><span class="nf">cwd</span><span class="p">().</span><span class="nf">createFile</span><span class="p">(</span><span class="n">io</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;save.txt&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">.{});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="n">file</span><span class="p">.</span><span class="nf">close</span><span class="p">(</span><span class="n">io</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">try</span><span class="w"> </span><span class="n">file</span><span class="p">.</span><span class="nf">writeAll</span><span class="p">(</span><span class="n">io</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kr">const</span><span class="w"> </span><span class="n">out</span><span class="p">:</span><span class="w"> </span><span class="n">Io</span><span class="p">.</span><span class="n">File</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">.</span><span class="nf">stdout</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">try</span><span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="nf">writeAll</span><span class="p">(</span><span class="n">io</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;save complete&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://kristoff.it/blog/zig-new-async-io/"target="_blank" rel="noopener">Kristoff on Zig&rsquo;s new async I/O</a>. <code>io</code> is a capability passed explicitly, like <code>Allocator</code>.</p>
<p>I like this direction a lot.</p>
<p>But it is still in transition. The <a href="https://github.com/ziglang/zig/issues/23446"target="_blank" rel="noopener">tracking issue</a> remains open: there is a prototype, roadmap, discussion, parts landing gradually. It is not a stable foundation Bun could bet on today without cost.</p>
<p>So the argument here cannot be &ldquo;Zig already solved it&rdquo;. The correct formulation is: Zig is moving toward an async/I/O design that may be cleaner in some aspects than the traditional colored model. But Bun needed to make a decision with what exists now, not with what promises to exist later.</p>
<p>That is always cruel to young languages. But it is real.</p>
<h2>The rewrite process matters more than the language<span class="hx:absolute hx:-mt-20" id="the-rewrite-process-matters-more-than-the-language"></span>
    <a href="#the-rewrite-process-matters-more-than-the-language" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The best part of Jarred&rsquo;s post is not Rust. It is method.</p>
<p>The mental loop of the post is literally this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// Pseudocode, not real code:
</span></span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">task</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">((</span><span class="nx">task</span> <span class="o">=</span> <span class="nx">todoList</span><span class="p">.</span><span class="nx">pop</span><span class="p">()))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">task</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">feedback</span> <span class="o">=</span> <span class="kr">await</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span><span class="nx">review</span><span class="p">(</span><span class="nx">result</span><span class="p">),</span> <span class="nx">review</span><span class="p">(</span><span class="nx">result</span><span class="p">)]);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">await</span> <span class="nx">apply</span><span class="p">(</span><span class="nx">feedback</span><span class="p">,</span> <span class="nx">result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then compiler errors became a queue:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">~16,000 compiler errors left
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">error: deref *mut EventLoop before field access
</span></span><span class="line"><span class="cl">error[E0034]: multiple applicable items in scope
</span></span><span class="line"><span class="cl">error: event_loop/EventLoopTimer.rs: port Timespec::ns from bun.zig</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the per-crate cycle was explicit:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">- For each crate, run cargo check, group the output by file and save the errors to a file
</span></span><span class="line"><span class="cl">- Fix all the compiler errors within that crate
</span></span><span class="line"><span class="cl">- 2 adversarial reviewers for the crate&#39;s changes
</span></span><span class="line"><span class="cl">- 1 fixer applies the fixes</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Source: <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">Bun&rsquo;s official post</a>.</p>
<p>He did not tell Claude to rewrite an entire runtime and go to sleep. He built loops. Separated implementer from reviewer. Used different context for adversarial review. Did a trial run. Fixed the workflow when agents started tripping over each other. Banned dangerous commands. Split worktrees. Turned compiler errors into a queue. Ran the same TypeScript suite because the suite did not depend on the runtime language. Used multi-platform CI as judge.</p>
<p>That is the part everyone should study.</p>
<p>The model did not replace engineering. The model executed work inside an engineering process. When the process was bad, the result was bad. When the process improved, the result improved.</p>
<p>It is the detail the tribal discussion ignores. The story is not &ldquo;Claude rewrote Bun&rdquo;. The story is &ldquo;an engineer turned a giant migration into several verifiable loops and used Claude to accelerate each loop&rdquo;.</p>
<p>Much more interesting.</p>
<h2>So did Zig fail?<span class="hx:absolute hx:-mt-20" id="so-did-zig-fail"></span>
    <a href="#so-did-zig-fail" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No.</p>
<p>Zig did not fail because Bun chose Rust. Rust also did not become the universal answer because Bun chose Rust.</p>
<p>What the public evidence shows is more boring: that code, at that moment, was too expensive to evolve with the level of confidence the team wanted. Rust bought predictability. Bought compiler feedback. Bought ecosystem. Bought an explicit safe/unsafe boundary. Bought tooling. Bought an environment where AI agents can bang against compiler errors and move forward mechanically.</p>
<p>That is enough to justify the decision.</p>
<p>But it is not enough to prove Zig was technically unviable for Bun. Many cited problems also look like boundary problems: implicit ownership, overly permissive APIs, allocation without a visible contract, resources crossing layers without a type carrying the owner, tests that maybe did not capture difficult invariants, and the human cost of reviewing all of that.</p>
<p>Those things could have been attacked in Zig. Maybe an aggressive Zig restructuring would have solved a lot. Maybe not. Maybe it would cost more than the port. Maybe it would cost less, but deliver less confidence. We do not have the counterfactual.</p>
<p>And that is exactly why the conclusion needs to be calibrated.</p>
<p>I support Jarred&rsquo;s decision as a product and engineering decision. His post is one of the most useful reports I have ever seen about a large AI-assisted rewrite. But liking the decision does not force me to pretend it was the only possible technical conclusion.</p>
<p>And there is one important disclaimer. <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">Anthropic acquired Bun in December 2025</a>, and Jarred opens the post by saying that he and the Bun team now work there. Anthropic sells Claude Code. Jarred had every institutional incentive to use as much Claude as possible and turn this rewrite into a public demonstration of his new employer&rsquo;s product. That does not prove bad faith, nor does it invalidate a technical result. But it adds bias to the decision and to the way the case is presented. Ignoring that conflict of incentives would be as naive as ignoring Andrew&rsquo;s resentment over Bun leaving Zig.</p>
<p>The best reading is less dramatic: Bun chose to pay a large cost now to reduce recurring maintenance costs later. Rust was a defensible choice, maybe a great one for that team at that moment. Zig remained an excellent language, but would have required another kind of reengineering: more explicit invariants, less informal ownership, more tools enabled by default, harder boundaries.</p>
<p>Andrew could have written that. He could have defended Zig on technical ground. He could have said: &ldquo;Rust bought some real guarantees, but a large part of the problem was architecture and process; here is how Zig could have attacked the same causes.&rdquo; That would have been a useful post.</p>
<p>We do not need to declare a winner. Language does not replace architecture. It only changes which errors become cheap to detect and which still depend on human discipline.</p>
<p>Bun chose to trade part of that discipline for compiler. I think it is a good bet.</p>
<p>I do not think it buries Zig.</p>
]]></content:encoded><category>programming-languages</category><category>rust</category><category>zig</category></item><item><title>Porting 10K Lines of Python to Crystal with Claude: easy-subtitle</title><link>https://akitaonrails.github.io/en/2026/03/07/porting-10k-lines-of-python-to-crystal-with-claude-easy-subtitle/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/03/07/porting-10k-lines-of-python-to-crystal-with-claude-easy-subtitle/</guid><pubDate>Sat, 07 Mar 2026 22:00:00 -0300</pubDate><description>&lt;p&gt;In the &lt;a href="https://akitaonrails.github.io/en/2026/03/07/easy-ffmpeg-smart-wrapper-in-crystal/"&gt;previous article&lt;/a&gt; I showed why I picked Crystal for command-line CLIs. In this one I want to show a more ambitious case. It isn&amp;rsquo;t a tool from scratch anymore — it&amp;rsquo;s a feature-parity port of a 10,000-line Python open source project.&lt;/p&gt;
&lt;h2&gt;The problem: subtitles&lt;span class="hx:absolute hx:-mt-20" id="the-problem-subtitles"&gt;&lt;/span&gt;
&lt;a href="#the-problem-subtitles" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Anyone who maintains a movie and TV collection knows the pain. You download the MKV, but the embedded subtitle is out of sync. Or worse: there&amp;rsquo;s no subtitle at all in the language you want. So you go to OpenSubtitles, download a subtitle, and it&amp;rsquo;s 3 seconds ahead because it was made for a different release. The manual flow is:&lt;/p&gt;</description><content:encoded><![CDATA[<p>In the <a href="/en/2026/03/07/easy-ffmpeg-smart-wrapper-in-crystal/">previous article</a> I showed why I picked Crystal for command-line CLIs. In this one I want to show a more ambitious case. It isn&rsquo;t a tool from scratch anymore — it&rsquo;s a feature-parity port of a 10,000-line Python open source project.</p>
<h2>The problem: subtitles<span class="hx:absolute hx:-mt-20" id="the-problem-subtitles"></span>
    <a href="#the-problem-subtitles" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Anyone who maintains a movie and TV collection knows the pain. You download the MKV, but the embedded subtitle is out of sync. Or worse: there&rsquo;s no subtitle at all in the language you want. So you go to OpenSubtitles, download a subtitle, and it&rsquo;s 3 seconds ahead because it was made for a different release. The manual flow is:</p>
<ol>
<li>Extract subtitle tracks from the MKV with <code>mkvextract</code></li>
<li>Go to OpenSubtitles, look for a subtitle for the movie</li>
<li>Download, test, see it&rsquo;s out of sync</li>
<li>Run some sync tool (ffsubsync, alass)</li>
<li>Rename the file, move it to the right place</li>
<li>Repeat for every language, for every movie</li>
</ol>
<p>For someone with 10 movies, it&rsquo;s tolerable. For someone with hundreds, it&rsquo;s insanity. It&rsquo;s exactly the kind of thing that should be automated.</p>
<h2>Subservient: the Python solution<span class="hx:absolute hx:-mt-20" id="subservient-the-python-solution"></span>
    <a href="#subservient-the-python-solution" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Looking at what already existed, I found <a href="https://github.com/N3xigen/Subservient"target="_blank" rel="noopener">Subservient</a>. It&rsquo;s a Python project that automates exactly this flow: it extracts subtitles from MKVs, downloads them from OpenSubtitles via REST API, syncs them with ffsubsync, and cleans ads out of SRT files.</p>
<p>The project is complete. It has movie mode and series mode, smart sync (tests every candidate in parallel and picks the best one) and first-match (stops at the first one that works). It uses the OpenSubtitles hash for exact matching and cleans watermarks and ads with more than 30 regex patterns.</p>
<p>But it has the typical Python distribution problems:</p>
<ul>
<li><strong>7 pip dependencies</strong>: colorama, requests, langdetect, ffsubsync, platformdirs, pycountry, tqdm</li>
<li><strong>ffsubsync as the sync engine</strong>: which in turn depends on numpy, auditok, and a bunch more Python packages</li>
<li><strong>Interactive menu UI</strong>: good for manual use, terrible for scriptability</li>
<li><strong>Config in INI format</strong>: not the end of the world, but YAML is more ergonomic</li>
<li><strong>10,220 lines across 6 Python files</strong>: 2,700-line files with hundreds-of-lines functions each</li>
</ul>
<p>The point isn&rsquo;t that Python is bad for this. Subservient works. But installing and maintaining it in production is another story. If you want to run it on a headless server, you need Python 3.8+, pip, virtualenv (or you&rsquo;re going to pollute the system), and pray no dependency breaks with the next OS update.</p>
<h2>The experiment: porting it to Crystal with Claude<span class="hx:absolute hx:-mt-20" id="the-experiment-porting-it-to-crystal-with-claude"></span>
    <a href="#the-experiment-porting-it-to-crystal-with-claude" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Here&rsquo;s where it gets interesting. I wanted to test a hypothesis: can Claude take a large open source project, understand the architecture, and do a complete port to another language?</p>
<p>I&rsquo;m not talking about translating it file by file. I&rsquo;m talking about understanding what the project does, redesigning the architecture where it makes sense, and generating idiomatic code in Crystal.</p>
<p>What I did:</p>
<ol>
<li>Asked Claude to clone and analyze the Subservient repo</li>
<li>Explained the design decisions: use <a href="https://github.com/kaegi/alass"target="_blank" rel="noopener">alass</a> (a Rust binary, no Python dependencies) instead of ffsubsync, CLI subcommands instead of interactive menus, YAML instead of INI</li>
<li>Asked for a feature-parity port, with tests</li>
</ol>
<p>alass is an important detail. ffsubsync works fine, but it&rsquo;s a Python package that pulls in numpy and does audio analysis. alass does the same thing (subtitle synchronization through timing analysis), but it&rsquo;s a standalone Rust binary. Swapping one for the other eliminates the biggest Python dependency in the stack.</p>
<h2>The result: easy-subtitle<span class="hx:absolute hx:-mt-20" id="the-result-easy-subtitle"></span>
    <a href="#the-result-easy-subtitle" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Five commits. Less than 40 minutes from the first to the last.</p>
<table>
  <thead>
      <tr>
          <th>Commit</th>
          <th>Time</th>
          <th>What it did</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Initial implementation</td>
          <td>21:47</td>
          <td>Complete port: 42 src files, 16 test files, CI, install script</td>
      </tr>
      <tr>
          <td>Track shard.lock</td>
          <td>21:56</td>
          <td>Dependency lock for reproducible builds</td>
      </tr>
      <tr>
          <td>Prefer ~/.local/bin</td>
          <td>22:03</td>
          <td>Install script fix</td>
      </tr>
      <tr>
          <td>Add doctor command</td>
          <td>22:20</td>
          <td>New <code>doctor</code> command to validate the setup + bump v0.2.0</td>
      </tr>
      <tr>
          <td>Homebrew formula</td>
          <td>22:24</td>
          <td>Support for <code>brew install</code> and auto-update workflow</td>
      </tr>
  </tbody>
</table>
<p>The first commit already delivers a working project: 8 CLI commands, OpenSubtitles client with rate limiting, 76 passing tests, GitHub Actions with CI and release for Linux and macOS.</p>
<h3>Numbers<span class="hx:absolute hx:-mt-20" id="numbers"></span>
    <a href="#numbers" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Metric</th>
          <th>Subservient (Python)</th>
          <th>easy-subtitle (Crystal)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Source code</td>
          <td>10,220 lines (6 files)</td>
          <td>2,516 lines (42 files)</td>
      </tr>
      <tr>
          <td>Tests</td>
          <td>0</td>
          <td>800 lines (76 specs)</td>
      </tr>
      <tr>
          <td>Runtime dependencies</td>
          <td>7 pip packages + ffsubsync</td>
          <td>0 (just webmock for tests)</td>
      </tr>
      <tr>
          <td>Binary</td>
          <td>n/a (needs Python + deps)</td>
          <td>~6MB static</td>
      </tr>
      <tr>
          <td>Config</td>
          <td>INI</td>
          <td>YAML</td>
      </tr>
      <tr>
          <td>Sync engine</td>
          <td>ffsubsync (Python)</td>
          <td>alass (Rust)</td>
      </tr>
      <tr>
          <td>UI</td>
          <td>Interactive menu</td>
          <td>CLI subcommands</td>
      </tr>
      <tr>
          <td>Concurrency</td>
          <td>ThreadPoolExecutor</td>
          <td>Crystal fibers + channels</td>
      </tr>
  </tbody>
</table>
<p>The LOC difference is loud: 10,220 vs 2,516. But that&rsquo;s not all Crystal&rsquo;s doing. The original Python has monolithic files of thousands of lines, with a lot of duplication and UI logic mixed with business logic. The port separates the responsibilities into small, focused modules.</p>
<h3>Architecture of the port<span class="hx:absolute hx:-mt-20" id="architecture-of-the-port"></span>
    <a href="#architecture-of-the-port" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>easy-subtitle/
  src/easy_subtitle/
    cli/           # Router &#43; 9 commands (init, extract, download, sync, run, clean, scan, hash, doctor)
    core/          # Language map, SRT parser/writer/cleaner, video scanner
    acquisition/   # OpenSubtitles API client, auth, search, download, movie hash
    extraction/    # MKV track parsing, extraction, remuxing
    synchronization/  # alass runner, offset computation, smart/first-match strategies
    models/        # VideoFile, SubtitleCandidate, CoverageEntry</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Every module has a clear responsibility. The biggest file is 144 lines (config). In the original Python, <code>acquisition.py</code> alone has 2,726 lines.</p>
<h3>What each command does<span class="hx:absolute hx:-mt-20" id="what-each-command-does"></span>
    <a href="#what-each-command-does" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Generate the default config</span>
</span></span><span class="line"><span class="cl">easy-subtitle init
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Extract subtitles from inside MKVs</span>
</span></span><span class="line"><span class="cl">easy-subtitle extract /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Download subtitles from OpenSubtitles</span>
</span></span><span class="line"><span class="cl">easy-subtitle download -l en,pt /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Sync downloaded subtitles with the video</span>
</span></span><span class="line"><span class="cl">easy-subtitle sync /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Full pipeline: extract → download → sync</span>
</span></span><span class="line"><span class="cl">easy-subtitle run /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Clean ads/watermarks from SRTs</span>
</span></span><span class="line"><span class="cl">easy-subtitle clean /path/to/subtitles
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># See subtitle coverage by language</span>
</span></span><span class="line"><span class="cl">easy-subtitle scan --json /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Compute the OpenSubtitles hash (debug)</span>
</span></span><span class="line"><span class="cl">easy-subtitle <span class="nb">hash</span> /path/to/movie.mkv
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Validate the setup: config, credentials, dependencies</span>
</span></span><span class="line"><span class="cl">easy-subtitle doctor</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>doctor</code> is a command I added later. It checks whether the config exists, whether the API key is configured, tests login against the API, and checks whether <code>mkvmerge</code>, <code>mkvextract</code> and <code>alass</code> are on the PATH. It shows OS-specific install instructions when something is missing.</p>
<h3>Smart sync with fibers<span class="hx:absolute hx:-mt-20" id="smart-sync-with-fibers"></span>
    <a href="#smart-sync-with-fibers" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Smart sync is the part I most enjoyed seeing in the port. In the original Python, it uses <code>ThreadPoolExecutor</code> to run multiple candidates in parallel. In Crystal, the same logic is more natural with fibers and channels:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-crystal" data-lang="crystal"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="n">candidates</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">Path</span><span class="p">),</span> <span class="n">video</span> <span class="p">:</span> <span class="n">VideoFile</span><span class="p">)</span> <span class="p">:</span> <span class="n">SyncResult?</span>
</span></span><span class="line"><span class="cl">  <span class="n">channel</span> <span class="o">=</span> <span class="nb">Channel</span><span class="p">(</span><span class="n">SyncResult</span><span class="p">)</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">candidates</span><span class="o">.</span><span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">candidates</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">candidate</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="bp">spawn</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">result</span> <span class="o">=</span> <span class="n">sync_one</span><span class="p">(</span><span class="n">candidate</span><span class="p">,</span> <span class="n">video</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">channel</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">(</span><span class="n">SyncResult</span><span class="p">)</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">candidates</span><span class="o">.</span><span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">candidates</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">&lt;&lt;</span> <span class="n">channel</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">accepted</span> <span class="o">=</span> <span class="n">results</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">accepted?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">accepted</span><span class="o">.</span><span class="n">min_by</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">offset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Each subtitle candidate gets synced in a separate fiber (via <code>spawn</code>). The results come back through the <code>Channel</code>. At the end, it picks the accepted one with the smallest offset. No ThreadPoolExecutor, no futures, no callbacks.</p>
<h3>API rate limiting<span class="hx:absolute hx:-mt-20" id="api-rate-limiting"></span>
    <a href="#api-rate-limiting" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>OpenSubtitles requires throttling of 500ms between requests. The Crystal client implements that with a Mutex:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-crystal" data-lang="crystal"><span class="line"><span class="cl"><span class="no">RATE_LIMIT_MS</span> <span class="o">=</span> <span class="mi">500</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span> <span class="k">def</span> <span class="nf">throttle!</span> <span class="p">:</span> <span class="nb">Nil</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@mutex</span><span class="o">.</span><span class="n">synchronize</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">elapsed</span> <span class="o">=</span> <span class="nb">Time</span><span class="o">.</span><span class="n">utc</span> <span class="o">-</span> <span class="vi">@last_request_at</span>
</span></span><span class="line"><span class="cl">    <span class="n">remaining</span> <span class="o">=</span> <span class="no">RATE_LIMIT_MS</span> <span class="o">-</span> <span class="n">elapsed</span><span class="o">.</span><span class="n">total_milliseconds</span>
</span></span><span class="line"><span class="cl">    <span class="nb">sleep</span><span class="p">(</span><span class="n">remaining</span><span class="o">.</span><span class="n">milliseconds</span><span class="p">)</span> <span class="k">if</span> <span class="n">remaining</span> <span class="o">&gt;</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@last_request_at</span> <span class="o">=</span> <span class="nb">Time</span><span class="o">.</span><span class="n">utc</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Simple, thread-safe, no external library.</p>
<h3>Installation<span class="hx:absolute hx:-mt-20" id="installation"></span>
    <a href="#installation" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The static binary comes out of GitHub Actions and can be installed three ways:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Homebrew (macOS / Linux)</span>
</span></span><span class="line"><span class="cl">brew install akitaonrails/tap/easy-subtitle
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Install script</span>
</span></span><span class="line"><span class="cl">curl -fsSL https://raw.githubusercontent.com/akitaonrails/easy-subtitle/master/install.sh <span class="p">|</span> bash
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Or grab the binary directly from Releases</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>One binary. No Python, no pip, nothing.</p>
<h2>On porting things &ldquo;just because&rdquo;<span class="hx:absolute hx:-mt-20" id="on-porting-things-just-because"></span>
    <a href="#on-porting-things-just-because" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I&rsquo;ve always argued that porting software from one language to another just for the language fetish is a waste of time. How many projects have been rewritten in Rust &ldquo;just because&rdquo;? How much effort spent on rewrites that delivered no new value?</p>
<p>But I have to admit this experiment made me reconsider.</p>
<p>When the cost of porting drops from weeks/months to less than 40 minutes, the equation changes. Porting Subservient to Crystal with Claude wasn&rsquo;t an exercise in linguistic vanity. I wanted a static binary I could drop on a server and forget. No managing a Python runtime, no pip install breaking on the next system update.</p>
<p>And the result isn&rsquo;t a mechanical port. It&rsquo;s 2,516 lines across 42 files, against 10,220 in 6 monolithic ones. The port came with 76 tests the original didn&rsquo;t have, CI with automatic release for Linux and macOS, a Homebrew formula and an install script with checksum verification.</p>
<p>The point isn&rsquo;t that Python is bad. It&rsquo;s that the bar for &ldquo;is it worth porting?&rdquo; got ridiculously low. Feature-parity port with tests in less than an hour. Hard to argue against that.</p>
<p>The <a href="https://github.com/akitaonrails/easy-subtitle"target="_blank" rel="noopener">repository is here</a>. GPL-3.0, like the original.</p>
]]></content:encoded><category>crystal</category><category>programming-languages</category><category>vibe-coding</category></item><item><title>AI Agents: What Would Be the Best Programming Language for LLMs?</title><link>https://akitaonrails.github.io/en/2026/02/09/ai-agents-best-programming-language-for-llms/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/02/09/ai-agents-best-programming-language-for-llms/</guid><pubDate>Mon, 09 Feb 2026 20:20:30 +0000</pubDate><description>&lt;p&gt;I decided to run a silly little exercise: ask Claude and GPT Codex what they consider to be the characteristics of a programming language that would benefit editing by LLMs, without worrying about being friendly to human programmers.&lt;/p&gt;
&lt;p&gt;Language Design takes into account creating reasonably optimized, safe code, etc., but balances that with being easy for a human to read, understand, and edit later. Usually these two things tend to conflict: the more &amp;ldquo;friendly&amp;rdquo; for a human, the less efficient it is for a compiler/machine. Examples of that include dynamic languages like Python, Ruby, PHP, and Javascript.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I decided to run a silly little exercise: ask Claude and GPT Codex what they consider to be the characteristics of a programming language that would benefit editing by LLMs, without worrying about being friendly to human programmers.</p>
<p>Language Design takes into account creating reasonably optimized, safe code, etc., but balances that with being easy for a human to read, understand, and edit later. Usually these two things tend to conflict: the more &ldquo;friendly&rdquo; for a human, the less efficient it is for a compiler/machine. Examples of that include dynamic languages like Python, Ruby, PHP, and Javascript.</p>
<p>In my head, I imagine the &ldquo;best&rdquo; (always in quotes) for a machine are exactly the ones we humans consider to have the worst ergonomics: functional, strongly typed, highly bureaucratic or &ldquo;verbose&rdquo; languages, for example, Rust, OCaml, Lean, etc.</p>
<p>Coincidentally (again, ask it yourself), Claude and GPT reached more or less this same conclusion. That doesn&rsquo;t mean it&rsquo;s &ldquo;correct&rdquo;. It&rsquo;s what the model can deduce from its probabilities, with the information it has. It&rsquo;s a massive exercise in &ldquo;guesswork&rdquo;. But I think the arguments make a lot of sense.</p>
<p>I asked it to show me what a snippet of code in this &ldquo;AI-friendly&rdquo; language would look like, which resembles the AST of a Lisp, but with more features, like annotations, metadata, typing, etc. I think the closest thing to an AST — with proper tooling to give feedback — would be, more or less, what an LLM would prefer for editing.</p>
<p>See what you think:</p>
<h1>Update: Pact-Lang</h1><p>In the section below, you&rsquo;ll find Claude/GPT&rsquo;s reasoning and arguments about the ideal language. I liked the idea and decided to ask Claude to generate a prototype compiler that actually works, using what it itself described as the requirements.</p>
<p>It chose to do it in Rust, a language called <strong>PACT</strong> (it picked the name itself), and this initial version compiles <code>.pct</code> files and generates <code>.rs</code> files. I haven&rsquo;t even tested it yet, just let it do the project and threw it on GitHub.</p>
<ul>
<li><a href="https://github.com/akitaonrails/pact-lang"target="_blank" rel="noopener">Pact-Lang Repository</a></li>
</ul>
<p>On X, I commented that I wasn&rsquo;t impressed when Anthropic hyped that Claude managed to make a C compiler (using GCC as a comparison). Making a compiler isn&rsquo;t hard — it&rsquo;s one of the first subjects in a decent computer science degree; everyone who studied there has already done one.</p>
<p>And, to reinforce this point, Claude managed to make a compiler for a language that doesn&rsquo;t exist, which — according to it — is ideal for LLMs, but not for humans.</p>
<p>I don&rsquo;t know why nobody has discussed this angle yet. Leaving it as a curiosity.</p>
<p>I don&rsquo;t think — at least in the short term — this is something that will actually happen, but it seems inevitable to me: why spend so much trying to make the LLM adapt to a language made for humans, if the goal is to prevent humans from directly touching the code anymore?</p>
<p>The most obvious answer is going back to the beginning: getting humans to write very detailed software specifications. Until now this was super unproductive; we&rsquo;ve tried it several times over the years. But it was impractical to write specifications that another human would then need to convert into code: it was smarter to create a language that was easier to write and that, more or less, resembled a good specification (and so we invented strong types, annotations, unit tests, etc.).</p>
<p>But if &ldquo;generating code&rdquo; becomes a trivial task with the LLM, it&rsquo;s worth dedicating more time to the specification, the famous &ldquo;blueprint&rdquo;. I don&rsquo;t know how practical this would be, but it&rsquo;s a line of reasoning that, today, with LLMs, makes sense to at least explore.</p>
<p>So let&rsquo;s get to the reasoning Claude gave:</p>
<h1>Designing a Programming Language for AI Agents</h1><p>A thought experiment on what a programming language would look like if it were conceived for LLM-based coding agents rather than human programmers.</p>
<hr>
<h2>Summary<span class="hx:absolute hx:-mt-20" id="summary"></span>
    <a href="#summary" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><a href="#the-problem">The Problem</a></li>
<li><a href="#what-human-oriented-design-gets-wrong-for-llms">What Human-Oriented Design Gets Wrong for LLMs</a>
<ul>
<li><a href="#safeguards-that-become-redundant">Safeguards That Become Redundant</a></li>
<li><a href="#what-would-actually-help-an-llm">What Would Actually Help an LLM</a></li>
<li><a href="#the-counterintuitive-insight">The Counterintuitive Insight</a></li>
<li><a href="#the-real-question">The Real Question</a></li>
</ul>
</li>
<li><a href="#a-prototype-what-it-would-look-like">A Prototype: What It Would Look Like</a>
<ul>
<li><a href="#layer-0-the-human-specification">Layer 0: The Human Specification</a></li>
<li><a href="#layer-1-the-ai-native-format">Layer 1: The AI-Native Format</a></li>
<li><a href="#layer-2-the-human-projection">Layer 2: The Human Projection</a></li>
<li><a href="#layer-3-how-an-llm-would-edit-this">Layer 3: How an LLM Would Edit This</a></li>
<li><a href="#design-decisions-rationale">Design Decisions Rationale</a></li>
<li><a href="#main-takeaway">Main Takeaway</a></li>
</ul>
</li>
<li><a href="#existing-languages-how-close-are-we">Existing Languages: How Close Are We?</a>
<ul>
<li><a href="#tier-1-closest-to-the-vision">Tier 1: Closest to the Vision</a></li>
<li><a href="#tier-2-get-several-things-right">Tier 2: Get Several Things Right</a></li>
<li><a href="#tier-3-get-one-or-two-things-right">Tier 3: Get One or Two Things Right</a></li>
<li><a href="#the-uncomfortable-pattern">The Uncomfortable Pattern</a></li>
</ul>
</li>
</ul>
<hr>
<h2>The Problem<span class="hx:absolute hx:-mt-20" id="the-problem"></span>
    <a href="#the-problem" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>We design programming languages to be fast, safe, and mostly <strong>human-friendly</strong> — modularity, scope isolation, easy syntax, etc. But when the main &ldquo;user&rdquo; of a language is an LLM-based coding agent, the constraints change drastically. Humans have limited working memory, read linearly, and interpret visually. LLMs don&rsquo;t share those constraints — but they have others that current languages ignore.</p>
<hr>
<h2>What Human-Oriented Design Gets Wrong for LLMs<span class="hx:absolute hx:-mt-20" id="what-human-oriented-design-gets-wrong-for-llms"></span>
    <a href="#what-human-oriented-design-gets-wrong-for-llms" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Safeguards That Become Redundant<span class="hx:absolute hx:-mt-20" id="safeguards-that-become-redundant"></span>
    <a href="#safeguards-that-become-redundant" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>
<p><strong>Syntactic sugar</strong> — An LLM doesn&rsquo;t need <code>for x in list</code> to be prettier than <code>loop(list, fn(x) -&gt; ...)</code>. Both are equally easy to parse. The idea of &ldquo;readable syntax&rdquo; is a human concern. Lisp S-expressions or even raw AST notation would be enough.</p>
</li>
<li>
<p><strong>Naming conventions</strong> — camelCase vs snake_case, short vs descriptive names — they exist to help humans scan and remember. An LLM could work with UUIDs as identifiers if the <em>semantic graph</em> is available.</p>
</li>
<li>
<p><strong>Indentation/formatting</strong> — Entirely visual. Explicit block delimiters (or just an AST) are unambiguous. Python&rsquo;s significant whitespace is actually <em>harder</em> for LLMs because whitespace is hostile to tokenization.</p>
</li>
<li>
<p><strong>Boilerplate reduction</strong> — DRY exists because humans hate repetition and lose track of duplicated logic. An LLM doesn&rsquo;t get tired and doesn&rsquo;t get lost. Explicit repetition with guaranteed consistency checks can be <em>better</em> than implicit magic (think Rails conventions that hide behavior).</p>
</li>
<li>
<p><strong>Progressive disclosure of complexity</strong> — Languages hide things (default parameters, implicit conversions, operator overloading) so as not to overwhelm humans. That gets in the way of LLMs — hidden behavior means the model has to simulate what the runtime <em>actually does</em> versus what the code <em>appears to do</em>.</p>
</li>
</ul>
<h3>What Would Actually Help an LLM<span class="hx:absolute hx:-mt-20" id="what-would-actually-help-an-llm"></span>
    <a href="#what-would-actually-help-an-llm" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ol>
<li>
<p><strong>Rich, inline semantic metadata</strong> — Not comments (natural language is ambiguous), but <em>machine-readable</em> annotations of intent. &ldquo;This function is pure.&rdquo; &ldquo;This block must execute in &lt; 10ms.&rdquo; &ldquo;This invariant must hold: x &gt; 0 at exit.&rdquo; Current type systems are a weak version of this. The ideal is <strong>contracts, effects, and provenance</strong> as first-class citizens, not bolted on afterward.</p>
</li>
<li>
<p><strong>Bidirectional traceability</strong> — Every line of code should know <em>why it exists</em>. Link to the requirement, the test, the commit rationale. When editing a function, the language should say &ldquo;this exists because of requirement R-1234, it&rsquo;s tested by test T-56, and it&rsquo;s depended on by modules A, B, C&rdquo;. Current languages don&rsquo;t support this — it lives in external tools (Jira, git blame, grep).</p>
</li>
<li>
<p><strong>Formal, queryable dependency graphs as a language primitive</strong> — Not <code>imports</code> to chase. The ability to ask the runtime: &ldquo;what is the total transitive impact of changing this type?&rdquo; and get a precise answer. LSP is a makeshift approximation.</p>
</li>
<li>
<p><strong>Deterministic, total effect tracking</strong> — Every function should declare <em>everything</em> it can do: I/O, mutation, allocation, panic, non-termination. Haskell&rsquo;s IO monad is going in the right direction, but it&rsquo;s too coarse. The ideal: &ldquo;this function reads from the network, writes to this specific database table, and can throw these 3 types of error&rdquo;. That lets an LLM reason about changes <em>without running the code</em>.</p>
</li>
<li>
<p><strong>Native diff/patch semantics</strong> — Current languages represent <em>state</em> (the current source). LLMs work in <em>deltas</em> (edits). A language designed for LLMs could represent programs as a <em>history of transformations</em> with semantic meaning, not plain text files. Think: a program is a chain of refactorings, not a bunch of characters.</p>
</li>
<li>
<p><strong>Constraint-based specifications alongside implementation</strong> — Instead of just writing code and hoping tests catch bugs, every function would carry a formal specification. LLMs are much better at checking &ldquo;does this implementation satisfy this formal constraint?&rdquo; than &ldquo;does this code do what that vague comment says?&rdquo;.</p>
</li>
<li>
<p><strong>Elimination of ambiguous overloading</strong> — Every operation should have exactly one meaning in context. The fact that <code>+</code> means integer addition, float addition, string concatenation, and list concatenation depending on the types is a human convenience that creates inference load for LLMs.</p>
</li>
</ol>
<h3>The Counterintuitive Insight<span class="hx:absolute hx:-mt-20" id="the-counterintuitive-insight"></span>
    <a href="#the-counterintuitive-insight" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The language an LLM would <em>actually</em> like looks less like Python and more like a <strong>typed, total, effect-tracked AST format with embedded formal specs and full provenance metadata</strong> — basically a rich IR with the semantic density of something like Lean 4 or Idris, but without any concern for on-screen appearance.</p>
<p>The irony: <strong>this already exists in parts.</strong> LLVM IR, WebAssembly, Typed Racket, Dafny, F*. Nobody uses it directly because it&rsquo;s hostile to humans. An LLM-native language would essentially be a <em>very</em> rich IR that no human would want to write, paired with a human-facing projection (like a &ldquo;view layer&rdquo;) for when people need to read.</p>
<h3>The Real Question<span class="hx:absolute hx:-mt-20" id="the-real-question"></span>
    <a href="#the-real-question" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The deeper question isn&rsquo;t &ldquo;what language should LLMs use&rdquo; — it&rsquo;s <strong>should LLMs use textual languages at all?</strong> Text is a serialization format for human cognition. The ideal programming interface for LLMs may be direct manipulation of a semantic graph with formal verification at every step, where &ldquo;source code&rdquo; as plain text simply doesn&rsquo;t exist.</p>
<hr>
<h2>A Prototype: What It Would Look Like<span class="hx:absolute hx:-mt-20" id="a-prototype-what-it-would-look-like"></span>
    <a href="#a-prototype-what-it-would-look-like" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The same concept — a simple HTTP user service — shown in four layers: the human intent specification, the AI-native format, a human projection, and the semantic editing interface.</p>
<h3>Layer 0: The Human Specification<span class="hx:absolute hx:-mt-20" id="layer-0-the-human-specification"></span>
    <a href="#layer-0-the-human-specification" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If humans no longer write code — neither the native format (Layer 1) nor the readable projection (Layer 2) — what <em>exactly</em> do they write? The answer: <strong>a declarative specification of intent</strong>. It isn&rsquo;t code. It isn&rsquo;t pseudocode. It&rsquo;s a structured declaration of what the system must do, which constraints it must respect, and which guarantees it must offer. The human describes the <em>what</em> and the <em>why</em>. The AI decides the <em>how</em>.</p>
<p>This isn&rsquo;t free-form natural language (too ambiguous) nor a programming language (too detailed). It&rsquo;s something in between: a <strong>specification DSL</strong> where the vocabulary is restricted, the structure is formal, but the cognition required is <em>product design</em>, not <em>software engineering</em>.</p>
<p>For the same user service:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w"> </span><span class="l">SPEC-2024-0042</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Serviço de usuários&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="l">time-plataforma</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span><span class="l">rascunho</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">domain</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">User</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">campos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">nome</span><span class="p">:</span><span class="w"> </span><span class="l">obrigatório, texto, 1–200 caracteres</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">email</span><span class="p">:</span><span class="w"> </span><span class="l">obrigatório, formato email, único no sistema</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">gerado automaticamente, imutável</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">endpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">buscar-usuário</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">descrição</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Retorna um usuário pelo ID&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">entrada</span><span class="p">:</span><span class="w"> </span><span class="l">id do usuário (da URL)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">saídas</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">sucesso</span><span class="p">:</span><span class="w"> </span><span class="l">o usuário encontrado (200)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">não encontrado</span><span class="p">:</span><span class="w"> </span><span class="l">quando o ID não existe (404)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">ID inválido</span><span class="p">:</span><span class="w"> </span><span class="l">quando o formato do ID é incorreto (400)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restrições</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">tempo máximo de resposta</span><span class="p">:</span><span class="w"> </span><span class="l">50ms</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">somente leitura no banco</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">criar-usuário</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">descrição</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Cria um novo usuário&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">entrada</span><span class="p">:</span><span class="w"> </span><span class="l">nome e email (do corpo da requisição, JSON)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">saídas</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">sucesso</span><span class="p">:</span><span class="w"> </span><span class="l">o usuário criado (201)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">email duplicado</span><span class="p">:</span><span class="w"> </span><span class="l">quando o email já existe (409)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">validação falhou</span><span class="p">:</span><span class="w"> </span><span class="l">lista de erros de validação (422)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restrições</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">tempo máximo de resposta</span><span class="p">:</span><span class="w"> </span><span class="l">200ms</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">idempotente por email (mesma requisição não duplica)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">escrita no banco</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">qualidade</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">toda função deve ser total (sem crashes, sem loops infinitos)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">todo campo deve ter validação explícita</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">todo erro deve ser tipado e enumerado (sem exceções genéricas)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">rastreabilidade</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">testes requeridos</span><span class="p">:</span><span class="w"> </span><span class="l">cobertura de todos os caminhos de saída</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">dependências conhecidas</span><span class="p">:</span><span class="w"> </span><span class="l">api-router, admin-panel</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><strong>What this specification does <em>not</em> contain:</strong></p>
<ul>
<li>No implementation decisions (which database, which framework, which validation pattern)</li>
<li>No programming syntax (no types, no functions, no imports)</li>
<li>No serialization details (the JSON format is a constraint, not an instruction on how to serialize)</li>
<li>No concern for file structure, modules, or code organization</li>
</ul>
<p><strong>What the LLM does with this:</strong></p>
<ol>
<li>Interprets the specification and generates Layer 1 (the native AST format with all the metadata, effects, invariants, and provenance)</li>
<li>Automatically derives tests from the declared outputs</li>
<li>Computes the dependency graph from the known dependencies</li>
<li>Fills in provenance by linking each function back to the section of the spec that originated it</li>
<li>Generates the schema migration if the type is new</li>
</ol>
<p><strong>The fundamental inversion:</strong> in traditional programming, humans write the implementation and <em>hope</em> it matches the intent. Here, humans write the intent and the system <em>guarantees</em> that the implementation matches it — because the specification is formal enough to be verifiable, but high-level enough to be thought about in product terms, not in code.</p>
<p><strong>The specification DSL isn&rsquo;t YAML.</strong> The example above uses YAML as a familiar format, but the real representation could be anything structured — a form, a visual graph, a conversational interface with the agent. What matters is that the vocabulary is restricted to the domain (not the implementation), the constraints are explicit (not implicit), and the output is <em>machine-verifiable</em> against the generated implementation.</p>
<p>This changes the human&rsquo;s role from <strong>code author</strong> to <strong>constraint author</strong>. The programmer becomes a <em>specifier</em> — someone who thinks in terms of domain, invariants, and guarantees, not syntax, patterns, and boilerplate.</p>
<h3>Layer 1: The AI-Native Format<span class="hx:absolute hx:-mt-20" id="layer-1-the-ai-native-format"></span>
    <a href="#layer-1-the-ai-native-format" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This is what the LLM actually works with:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scheme" data-lang="scheme"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">module</span> <span class="nv">user-service</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:provenance</span> <span class="err">{</span><span class="nv">req:</span> <span class="s">&#34;SPEC-2024-0042&#34;</span><span class="o">,</span> <span class="nv">author:</span> <span class="s">&#34;agent:claude-v4&#34;</span><span class="o">,</span> <span class="nv">created:</span> <span class="s">&#34;2026-02-09T14:00:00Z&#34;</span><span class="err">}</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:version</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:parent-version</span> <span class="mi">6</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:delta</span> <span class="p">(</span><span class="nf">added-fn</span> <span class="nv">get-user-by-id</span> <span class="s">&#34;support single-user lookup endpoint&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">type</span> <span class="nv">User</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:invariants</span> <span class="p">[(</span><span class="nb">&gt; </span><span class="p">(</span><span class="nf">strlen</span> <span class="nv">name</span><span class="p">)</span> <span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="nf">matches</span> <span class="nv">email</span> <span class="o">#</span><span class="nv">/</span><span class="o">.</span><span class="nv">+@</span><span class="o">.</span><span class="nv">+</span><span class="err">\</span><span class="o">..</span><span class="nv">+/</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">field</span> <span class="nv">id</span>   <span class="nv">UUID</span>   <span class="nv">:immutable</span> <span class="nv">:generated</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">field</span> <span class="nv">name</span> <span class="nv">String</span> <span class="nv">:min-len</span> <span class="mi">1</span> <span class="nv">:max-len</span> <span class="mi">200</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">field</span> <span class="nv">email</span> <span class="nv">String</span> <span class="nv">:format</span> <span class="nv">:email</span> <span class="nv">:unique-within</span> <span class="nv">user-store</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">effect-set</span> <span class="nv">db-read</span>    <span class="p">[</span><span class="nv">:reads</span>  <span class="nv">user-store</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">effect-set</span> <span class="nv">db-write</span>   <span class="p">[</span><span class="nv">:writes</span> <span class="nv">user-store</span> <span class="nv">:reads</span> <span class="nv">user-store</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">effect-set</span> <span class="nv">http-respond</span> <span class="p">[</span><span class="nv">:sends</span> <span class="nv">http-response</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">fn</span> <span class="nv">get-user-by-id</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:provenance</span> <span class="err">{</span><span class="nv">req:</span> <span class="s">&#34;SPEC-2024-0042#section-3&#34;</span><span class="o">,</span> <span class="nv">test:</span> <span class="p">[</span><span class="s">&#34;T-101&#34;</span> <span class="s">&#34;T-102&#34;</span> <span class="s">&#34;T-103&#34;</span><span class="p">]</span><span class="err">}</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:effects</span>    <span class="p">[</span><span class="nv">db-read</span> <span class="nv">http-respond</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:total</span>      <span class="nv">true</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:latency-budget</span> <span class="mi">50</span><span class="nv">ms</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:called-by</span>  <span class="p">[</span><span class="nv">api-router/handle-request</span> <span class="nv">admin-panel/user-detail</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">param</span> <span class="nv">id</span> <span class="nv">UUID</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:source</span> <span class="nv">http-path-param</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:validated-at</span> <span class="nv">boundary</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">returns</span> <span class="p">(</span><span class="nf">union</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">ok</span>   <span class="nv">User</span>   <span class="nv">:http</span> <span class="mi">200</span> <span class="nv">:serialize</span> <span class="nv">:json</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">err</span>  <span class="nv">:not-found</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">id</span><span class="err">}</span> <span class="nv">:http</span> <span class="mi">404</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">err</span>  <span class="nv">:invalid-id</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">id</span><span class="err">}</span> <span class="nv">:http</span> <span class="mi">400</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">;; the logic itself — note how small it is compared to the metadata</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">validated-id</span> <span class="p">(</span><span class="nf">validate-uuid</span> <span class="nv">id</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">match</span> <span class="nv">validated-id</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nf">err</span> <span class="nv">_</span><span class="p">)</span>    <span class="p">(</span><span class="nf">err</span> <span class="nv">:invalid-id</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">id</span><span class="err">}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nf">ok</span>  <span class="nv">uuid</span><span class="p">)</span> <span class="p">(</span><span class="nf">match</span> <span class="p">(</span><span class="nf">query</span> <span class="nv">user-store</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">uuid</span><span class="err">}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                     <span class="p">(</span><span class="nf">none</span><span class="p">)</span>   <span class="p">(</span><span class="nf">err</span> <span class="nv">:not-found</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">uuid</span><span class="err">}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                     <span class="p">(</span><span class="nf">some</span> <span class="nv">u</span><span class="p">)</span> <span class="p">(</span><span class="nf">ok</span> <span class="nv">u</span><span class="p">)))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">fn</span> <span class="nv">create-user</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:provenance</span> <span class="err">{</span><span class="nv">req:</span> <span class="s">&#34;SPEC-2024-0041&#34;</span><span class="o">,</span> <span class="nv">test:</span> <span class="p">[</span><span class="s">&#34;T-090&#34;</span> <span class="s">&#34;T-091&#34;</span><span class="p">]</span><span class="err">}</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:effects</span>    <span class="p">[</span><span class="nv">db-write</span> <span class="nv">http-respond</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:total</span>      <span class="nv">true</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:idempotency-key</span> <span class="p">(</span><span class="nf">hash</span> <span class="p">(</span><span class="o">.</span> <span class="nv">input</span> <span class="nv">email</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:latency-budget</span> <span class="mi">200</span><span class="nv">ms</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">param</span> <span class="nv">input</span> <span class="err">{</span><span class="nv">:name</span> <span class="nv">String</span> <span class="nv">:email</span> <span class="nv">String</span><span class="err">}</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:source</span> <span class="nv">http-body</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:content-type</span> <span class="nv">:json</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:validated-at</span> <span class="nv">boundary</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">returns</span> <span class="p">(</span><span class="nf">union</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">ok</span>   <span class="nv">User</span>   <span class="nv">:http</span> <span class="mi">201</span> <span class="nv">:serialize</span> <span class="nv">:json</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">err</span>  <span class="nv">:duplicate-email</span> <span class="err">{</span><span class="nv">:email</span> <span class="p">(</span><span class="o">.</span> <span class="nv">input</span> <span class="nv">email</span><span class="p">)</span><span class="err">}</span> <span class="nv">:http</span> <span class="mi">409</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">err</span>  <span class="nv">:validation-failed</span> <span class="p">(</span><span class="nb">list </span><span class="nv">ValidationError</span><span class="p">)</span> <span class="nv">:http</span> <span class="mi">422</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">errors</span> <span class="p">(</span><span class="nf">validate-against</span> <span class="nv">User</span> <span class="nv">input</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nf">non-empty?</span> <span class="nv">errors</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nf">err</span> <span class="nv">:validation-failed</span> <span class="nv">errors</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nf">match</span> <span class="p">(</span><span class="nf">insert!</span> <span class="nv">user-store</span> <span class="p">(</span><span class="nf">build</span> <span class="nv">User</span> <span class="nv">input</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nf">err</span> <span class="nv">:unique-violation</span><span class="p">)</span> <span class="p">(</span><span class="nf">err</span> <span class="nv">:duplicate-email</span> <span class="err">{</span><span class="nv">:email</span> <span class="p">(</span><span class="o">.</span> <span class="nv">input</span> <span class="nv">email</span><span class="p">)</span><span class="err">}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nf">ok</span> <span class="nv">user</span><span class="p">)</span>               <span class="p">(</span><span class="nf">ok</span> <span class="nv">user</span><span class="p">))))))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Layer 2: The Human Projection<span class="hx:absolute hx:-mt-20" id="layer-2-the-human-projection"></span>
    <a href="#layer-2-the-human-projection" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This is auto-generated. It isn&rsquo;t source code — it&rsquo;s a read-only rendering, like a database&rsquo;s storage format and a query&rsquo;s output. No human writes this; it&rsquo;s projected from Layer 1 whenever someone needs to review.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># --- Auto-projetado a partir de user-service v7 ---</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Spec: SPEC-2024-0042 | Tests: T-101, T-102, T-103</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@effects</span><span class="p">(</span><span class="n">reads</span><span class="o">=</span><span class="s2">&#34;user_store&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nd">@budget</span><span class="p">(</span><span class="n">latency</span><span class="o">=</span><span class="s2">&#34;50ms&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nd">@total</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_user_by_id</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">UUID</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">User</span> <span class="o">|</span> <span class="n">NotFound</span> <span class="o">|</span> <span class="n">InvalidId</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">match</span> <span class="n">validate_uuid</span><span class="p">(</span><span class="nb">id</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="n">Err</span><span class="p">(</span><span class="n">_</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">InvalidId</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="nb">id</span><span class="p">)</span>          <span class="c1"># → 400</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="n">Ok</span><span class="p">(</span><span class="n">uuid</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">match</span> <span class="n">user_store</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">uuid</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="k">case</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="k">return</span> <span class="n">NotFound</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">uuid</span><span class="p">)</span> <span class="c1"># → 404</span>
</span></span><span class="line"><span class="cl">                <span class="k">case</span> <span class="n">user</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="k">return</span> <span class="n">user</span>              <span class="c1"># → 200</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Layer 3: How an LLM Would Edit This<span class="hx:absolute hx:-mt-20" id="layer-3-how-an-llm-would-edit-this"></span>
    <a href="#layer-3-how-an-llm-would-edit-this" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>An LLM wouldn&rsquo;t send a text diff. It would send a <strong>semantic operation</strong>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scheme" data-lang="scheme"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">edit</span> <span class="nv">user-service</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:operation</span> <span class="nv">add-field</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:target-type</span> <span class="nv">User</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:field</span> <span class="p">(</span><span class="nf">field</span> <span class="nv">role</span> <span class="p">(</span><span class="nf">enum</span> <span class="nv">:admin</span> <span class="nv">:member</span> <span class="nv">:guest</span><span class="p">)</span> <span class="nv">:default</span> <span class="nv">:member</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:reason</span> <span class="s">&#34;SPEC-2024-0055: role-based access control&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nv">:cascading-impacts</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; the language itself computes these impacts and asks for confirmation:</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - create-user needs to accept optional &#39;role&#39; parameter</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - get-user-by-id now returns &#39;role&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - 3 tests need to be updated: T-090, T-091, T-101</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - user-store schema migration required</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - admin-panel/user-detail consumes User — check compatibility</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nv">:confirm</span> <span class="nv">true</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The system responds with:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scheme" data-lang="scheme"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">edit-result</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:version</span> <span class="mi">8</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:parent</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:changes-applied</span> <span class="mi">4</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:changes-pending-review</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:migration-generated</span> <span class="s">&#34;M-0008-add-role-to-user&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:tests-invalidated</span> <span class="p">[</span><span class="s">&#34;T-090&#34;</span> <span class="s">&#34;T-091&#34;</span> <span class="s">&#34;T-101&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:tests-auto-updated</span> <span class="p">[</span><span class="s">&#34;T-101&#34;</span><span class="p">]</span>    <span class="c1">;; trivial: return format changed</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:tests-need-manual</span>  <span class="p">[</span><span class="s">&#34;T-090&#34;</span> <span class="s">&#34;T-091&#34;</span><span class="p">]</span>  <span class="c1">;; behavioral: creation changed</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:downstream-verified</span> <span class="p">[</span><span class="s">&#34;admin-panel/user-detail: compatible&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:downstream-warning</span>  <span class="p">[</span><span class="s">&#34;api-router: new field not yet exposed in list endpoint&#34;</span><span class="p">])</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Design Decisions Rationale<span class="hx:absolute hx:-mt-20" id="design-decisions-rationale"></span>
    <a href="#design-decisions-rationale" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Design Decision</th>
          <th>Equivalent in Human Languages</th>
          <th>Why It Helps LLMs</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Declarative spec of intent (Layer 0)</td>
          <td>Requirements doc + user stories</td>
          <td>Formal, verifiable input, no natural-language ambiguity</td>
      </tr>
      <tr>
          <td>S-expression AST</td>
          <td><code>if/else</code> blocks, braces</td>
          <td>No parsing ambiguity, trivial programmatic manipulation</td>
      </tr>
      <tr>
          <td><code>:provenance</code> on every node</td>
          <td>Git blame + Jira links</td>
          <td>Never has to ask &ldquo;why does this exist?&rdquo; — it&rsquo;s inline</td>
      </tr>
      <tr>
          <td><code>:effects</code> declarations</td>
          <td>Implicit side effects</td>
          <td>Knows exactly what a function touches without reading the body</td>
      </tr>
      <tr>
          <td><code>:total</code> annotation</td>
          <td>Hope + tests</td>
          <td>&ldquo;No crashes, no infinite loops&rdquo; verified by the compiler</td>
      </tr>
      <tr>
          <td><code>:called-by</code> graph</td>
          <td>Grep for uses</td>
          <td>Impact analysis is instant, not a search</td>
      </tr>
      <tr>
          <td><code>:latency-budget</code></td>
          <td>SLA in some document</td>
          <td>Performance constraints live in the code, not in tribal knowledge</td>
      </tr>
      <tr>
          <td>Semantic edit vs textual diff</td>
          <td><code>sed</code> / find-replace</td>
          <td>Declare intent, the system computes consequences</td>
      </tr>
      <tr>
          <td>Cascading impact analysis</td>
          <td>&ldquo;Did you remember to update X?&rdquo;</td>
          <td>The language tells you what broke — no guessing</td>
      </tr>
      <tr>
          <td>Explicit return types</td>
          <td>Exceptions thrown from anywhere</td>
          <td>Every possible outcome is enumerated — no surprises</td>
      </tr>
  </tbody>
</table>
<h3>Main Takeaway<span class="hx:absolute hx:-mt-20" id="main-takeaway"></span>
    <a href="#main-takeaway" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The ratio of <strong>metadata to logic</strong> is something like 3:1. In human languages, it&rsquo;s the reverse. That&rsquo;s the fundamental shift — an AI-native language is <em>mostly specification, provenance, and constraints</em> with a thin layer of computation. Logic is the easy part. Knowing <strong>why</strong>, <strong>what else is affected</strong>, and <strong>which guarantees must hold</strong> is where LLMs spend their reasoning budget.</p>
<p>This isn&rsquo;t a language for writing code. It&rsquo;s a language for <strong>maintaining systems</strong>. And with Layer 0, the loop closes: humans specify intent, AI generates and maintains the implementation, and formal verification guarantees that both agree. Code becomes an intermediate artifact — important for the machine, invisible for the human.</p>
<hr>
<h2>Existing Languages: How Close Are We?<span class="hx:absolute hx:-mt-20" id="existing-languages-how-close-are-we"></span>
    <a href="#existing-languages-how-close-are-we" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>An honest ranking of existing popular languages, based on how much each one already delivers natively of the ideal.</p>
<h3>Tier 1: Closest to the Vision<span class="hx:absolute hx:-mt-20" id="tier-1-closest-to-the-vision"></span>
    <a href="#tier-1-closest-to-the-vision" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><h4>Lean 4<span class="hx:absolute hx:-mt-20" id="lean-4"></span>
    <a href="#lean-4" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>The closest thing that exists today. Dependent types mean that specifications <em>are</em> the code — a function&rsquo;s type can literally say &ldquo;returns a sorted list whose length equals that of the input list&rdquo;. Proof obligations force total, verified logic. The macro system operates directly on the AST, which gets close to the &ldquo;semantic editing&rdquo; concept. The metaprogramming framework (Lean&rsquo;s <code>Elab</code> monad) lets you query and manipulate the proof environment programmatically.</p>
<p><strong>Missing pieces:</strong> no effect tracking, no provenance, no built-in dependency graph queries.</p>
<h4>F* (F-Star)<span class="hx:absolute hx:-mt-20" id="f-f-star"></span>
    <a href="#f-f-star" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>A Microsoft Research language. It has <em>exactly</em> the effect system described above — you declare <code>ST</code> for state, <code>IO</code> for I/O, <code>Pure</code> for pure computation, and the compiler enforces it. Refined types let you encode invariants like <code>:min-len 1</code> directly into the type. It can extract verified code to OCaml, F#, or C. It&rsquo;s the closest thing to &ldquo;lots of specification with little logic&rdquo;.</p>
<p>Almost nobody uses it outside research, which says a lot about the human-friendliness trade-off.</p>
<h4>Idris 2<span class="hx:absolute hx:-mt-20" id="idris-2"></span>
    <a href="#idris-2" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Similar to Lean, but with first-class <strong>quantitative type theory</strong> — the type system tracks <em>how many times</em> a value is used. That&rsquo;s a primitive form of resource/effect tracking. Elaborator reflection lets programs inspect and modify the type-checking process itself, which speaks to the idea of semantic editing.</p>
<h3>Tier 2: Get Several Things Right<span class="hx:absolute hx:-mt-20" id="tier-2-get-several-things-right"></span>
    <a href="#tier-2-get-several-things-right" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><h4>Rust<span class="hx:absolute hx:-mt-20" id="rust"></span>
    <a href="#rust" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Not for the reasons people usually cite. The borrow checker is essentially a <em>compiler-verified effect system</em> for memory — it tracks aliasing, mutation, and lifetime at the type level. The trait system with <code>Send</code>, <code>Sync</code>, <code>Unpin</code> is effect tracking by another name. The <code>Result&lt;T, E&gt;</code> convention with exhaustive <code>match</code> gives explicit returns. <code>cargo</code> provides a real dependency graph you can query.</p>
<p><strong>What&rsquo;s missing:</strong> formal specifications, provenance, totality checking, and the syntax is complex enough that LLMs burn tokens on lifetime gymnastics.</p>
<h4>Haskell<span class="hx:absolute hx:-mt-20" id="haskell"></span>
    <a href="#haskell" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>The classic of several of these ideas. Purity by default means effects are <em>always</em> explicit (<code>IO</code> monad). The type system is powerful enough to encode many invariants. <code>hlint</code> and typed holes give structured feedback.</p>
<p><strong>What&rsquo;s missing:</strong> the effects story is coarse (just <code>IO</code> vs pure — no granularity about <em>what kind</em> of I/O), no provenance, no built-in specification language, and the lazy evaluation model makes reasoning about performance genuinely hard even for LLMs.</p>
<h4>Dafny<span class="hx:absolute hx:-mt-20" id="dafny"></span>
    <a href="#dafny" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>A Microsoft language focused on verification. It has <code>requires</code>, <code>ensures</code>, <code>invariant</code> as first-class syntax — exactly the <code>:invariants</code> annotations described above. The verifier checks these at compile time. Loop termination is verified (<code>decreases</code> = totality). It&rsquo;s basically &ldquo;specification-heavy programming&rdquo;.</p>
<p><strong>Weakness:</strong> small ecosystem, no effect system, and it&rsquo;s oriented toward verifying algorithms more than building systems.</p>
<h3>Tier 3: Get One or Two Things Right<span class="hx:absolute hx:-mt-20" id="tier-3-get-one-or-two-things-right"></span>
    <a href="#tier-3-get-one-or-two-things-right" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><h4>Elixir/Erlang (BEAM)<span class="hx:absolute hx:-mt-20" id="elixirerlang-beam"></span>
    <a href="#elixirerlang-beam" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>A surprising pick, but: OTP&rsquo;s supervision trees are essentially a <strong>declarative dependency and failure graph</strong>. The process model gives natural effect isolation — each process is a boundary. Pattern matching with tagged tuples (<code>{:ok, result}</code> / <code>{:error, reason}</code>) is explicit union return. <code>@spec</code> and <code>@doc</code> are inline metadata. Hot code reloading is a primitive &ldquo;semantic patch&rdquo;.</p>
<p><strong>What&rsquo;s missing:</strong> formal verification, compile-time type enforcement (Dialyzer is optional and incomplete), no provenance.</p>
<h4>Scala 3<span class="hx:absolute hx:-mt-20" id="scala-3"></span>
    <a href="#scala-3" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>The work on the effect system (Caprese/capture checking) is going in the right direction. Union types, match types, and opaque types give expressive return specifications. Inline metaprogramming via <code>scala.quoted</code> operates on typed ASTs. But it carries a huge JVM complexity bundle.</p>
<h4>Ada/SPARK<span class="hx:absolute hx:-mt-20" id="adaspark"></span>
    <a href="#adaspark" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>The SPARK subset is formally verifiable with contracts (<code>Pre</code>, <code>Post</code>, <code>Contract_Cases</code>). Used in aerospace and defense, where &ldquo;proving it can&rsquo;t break&rdquo; is a real requirement. Very close to the concept of <code>:total</code> + <code>:invariants</code>. But the language is verbose, the ecosystem is small, and there&rsquo;s no effect tracking beyond what contracts can express.</p>
<h3>The Uncomfortable Pattern<span class="hx:absolute hx:-mt-20" id="the-uncomfortable-pattern"></span>
    <a href="#the-uncomfortable-pattern" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>What LLMs Want</th>
          <th>Who Has It</th>
          <th>Why It Isn&rsquo;t Mainstream</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Formal specs as code</td>
          <td>Lean, F*, Dafny</td>
          <td>Steep learning curve for humans</td>
      </tr>
      <tr>
          <td>Effect tracking</td>
          <td>F*, Haskell, Rust (partial)</td>
          <td>Increases annotation burden</td>
      </tr>
      <tr>
          <td>Totality checking</td>
          <td>Lean, Idris, Agda</td>
          <td>Rejects many &ldquo;useful&rdquo; programs</td>
      </tr>
      <tr>
          <td>Rich AST manipulation</td>
          <td>Lean, Lisp/Racket, Elixir macros</td>
          <td>Humans find macros confusing</td>
      </tr>
      <tr>
          <td>Exhaustive returns</td>
          <td>Rust, Haskell, OCaml</td>
          <td>Humans find <code>match</code> tedious vs exceptions</td>
      </tr>
  </tbody>
</table>
<p>Every feature exists somewhere. The reason no language combines them all is that <strong>each one adds cognitive load for humans</strong>. The history of mainstream language design is about <em>removing</em> the things LLMs find most useful, because humans experience them as friction.</p>
<p>That&rsquo;s the central tension: <strong>the ideal language for AI is the one humans keep rejecting.</strong></p>
<hr>
<h2>Addendum: Additional Thoughts<span class="hx:absolute hx:-mt-20" id="addendum-additional-thoughts"></span>
    <a href="#addendum-additional-thoughts" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Extra primitives that seem especially useful for LLMs<span class="hx:absolute hx:-mt-20" id="extra-primitives-that-seem-especially-useful-for-llms"></span>
    <a href="#extra-primitives-that-seem-especially-useful-for-llms" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><strong>Canonical, lossless AST serialization</strong> — A single, stable, deterministic, diffable, and hashable representation. That removes formatter drift and makes semantic caching trivial.</li>
<li><strong>Proof-carrying artifacts</strong> — The compiler emits machine-verifiable certificates tied to specs and effects, allowing incremental trust without rerunning the world.</li>
<li><strong>Capability-scoped effects</strong> — Effects should require explicit capability grants at the module boundary (not just declared in signatures). That gives the language a built-in permissions model.</li>
<li><strong>Spec/test duality</strong> — Specs should be executable and tests should be derivable from specs. The boundary between the two should be thin and programmatic.</li>
</ul>
<h3>Modularization for LLMs<span class="hx:absolute hx:-mt-20" id="modularization-for-llms"></span>
    <a href="#modularization-for-llms" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>LLMs handle monoliths better than humans, but <strong>modules still matter</strong> as <em>semantic boundaries</em>:</p>
<ul>
<li><strong>Effect scope</strong>: capabilities are granted at module boundaries.</li>
<li><strong>Ownership and invariants</strong>: modules define which data invariants they own and enforce.</li>
<li><strong>Dependency graphs</strong>: the language can cache and re-verify smaller units.</li>
<li><strong>Partial recompilation</strong>: the impact of a change is smaller when units are isolated.</li>
</ul>
<p>In an AI-native language, modularization should be <strong>constraint- and capability-driven</strong>, not file-driven. You can keep everything in a single file, but the <em>units</em> should still be explicit.</p>
<h3>Runtime observability as a language primitive<span class="hx:absolute hx:-mt-20" id="runtime-observability-as-a-language-primitive"></span>
    <a href="#runtime-observability-as-a-language-primitive" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A language for LLMs should treat <strong>structured tracing</strong> as a first-class thing:</p>
<ul>
<li>Every effect produces machine-readable trace events with causal links.</li>
<li>Edits can be validated against <em>behavioral deltas</em>, not just type diffs.</li>
<li>Provenance can extend into the runtime, letting you attach &ldquo;why&rdquo; to &ldquo;what happened&rdquo;.</li>
</ul>
<h3>Alternative framing<span class="hx:absolute hx:-mt-20" id="alternative-framing"></span>
    <a href="#alternative-framing" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Instead of a &ldquo;language&rdquo;, think: <strong>native graph IR + semantic edit protocol + human projection DSL</strong>. Text becomes a view, not the source. The primary interface is a semantic graph with checked constraints, safe capabilities, and proof-carrying metadata.</p>
]]></content:encoded><category>programming-languages</category><category>llms</category><category>coding-agents</category></item><item><title>[Manga-Downloadr] Porting from Crystal to Ruby (and a bit of JRuby)</title><link>https://akitaonrails.github.io/en/2016/06/06/manga-downloadr-porting-from-crystal-to-ruby-and-a-bit-of-jruby/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/06/06/manga-downloadr-porting-from-crystal-to-ruby-and-a-bit-of-jruby/</guid><pubDate>Mon, 06 Jun 2016 18:06:00 -0300</pubDate><description>&lt;p&gt;I have this old pet project of mine called &lt;a href="http://www.akitaonrails.com/2014/12/17/small-bites-downloader-para-mangareader-kindle-edition"target="_blank" rel="noopener"&gt;Manga Downloadr&lt;/a&gt; that I published in 2014. It was a very rough version. I was experimenting with Typhoeus asynchronous requests and in the end the code ended up becoming super messed up.&lt;/p&gt;
&lt;p&gt;The nature of the original Manga Downloader is no different from a web crawler: it fetches HTML pages, parses them in order to find collections of links and keeps fetching until a set of images is downloaded. Then I organize them in volumes, optimize them to fit the Kindle screen resolution, and compile them down into PDF files. This makes this project an interesting exercise in trying to make concurrent HTTP requests and process the results.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I have this old pet project of mine called <a href="http://www.akitaonrails.com/2014/12/17/small-bites-downloader-para-mangareader-kindle-edition"target="_blank" rel="noopener">Manga Downloadr</a> that I published in 2014. It was a very rough version. I was experimenting with Typhoeus asynchronous requests and in the end the code ended up becoming super messed up.</p>
<p>The nature of the original Manga Downloader is no different from a web crawler: it fetches HTML pages, parses them in order to find collections of links and keeps fetching until a set of images is downloaded. Then I organize them in volumes, optimize them to fit the Kindle screen resolution, and compile them down into PDF files. This makes this project an interesting exercise in trying to make concurrent HTTP requests and process the results.</p>
<p>A year later I was learning Elixir. The Manga Downloadr was a nice candidate for me to figure out how to implement the same thing in a different language. You can follow my learning process in <a href="http://www.akitaonrails.com/exmangadownloadr"target="_blank" rel="noopener">this series of posts</a>.</p>
<p>Finally, I&rsquo;ve been learning more about Crystal, a Ruby-inspired platform that can compile Ruby-like source code into LLVM-optimized native binaries. And as a bonus it features Go-like CSP channels and fibers to allow for concurrent code.</p>
<p>So I adapted my Elixir version to Crystal and the result is this code you can find over Github as <a href="https://github.com/akitaonrails/cr_manga_downloadr"target="_blank" rel="noopener">akitaonrails/cr_manga_downloadr</a>.</p>
<p>It works very well and performs really fast, limited mainly by how many requests MangaReader can respond to concurrently and the speed/reliability of the Internet connection. So, as my original Ruby version was terrible code, it was a good time to rewrite it. And as Crystal is surprisingly close to Ruby I decided to port it over.</p>
<blockquote>
  <p>The port was almost too trivial</p>

</blockquote>
<p>It was mostly copying and pasting the Crystal code without the Type annotations. And I had to replace the lightweight Fibers and Channel implementation for concurrency with traditional Ruby Threads.</p>
<p>The new version 2.0 for the Ruby version of the tool can be found in this Github repository: <a href="https://github.com/akitaonrails/manga-downloadr"target="_blank" rel="noopener">akitaonrails/manga-downloadr</a>.</p>
<h3>Ruby, Ruby everywhere!<span class="hx:absolute hx:-mt-20" id="ruby-ruby-everywhere"></span>
    <a href="#ruby-ruby-everywhere" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Moving between Ruby and Crystal is not so difficult. The Crystal team did a fantastic job implementing a very solid Standard Library (stdlib) that very closely resembles MRI Ruby&rsquo;s.</p>
<p>For example, let&rsquo;s compare a snippet from my Crystal version first:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">page_link</span> <span class="p">:</span> <span class="nb">String</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span> <span class="n">page_link</span> <span class="k">do</span> <span class="o">|</span><span class="n">html</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">images</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">xpath</span><span class="p">(</span><span class="s2">&#34;//img[contains(@id, &#39;img&#39;)]&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">as</span><span class="p">(</span><span class="no">XML</span><span class="o">::</span><span class="no">NodeSet</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_alt</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;alt&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_src</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;src&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">image_alt</span> <span class="o">&amp;&amp;</span> <span class="n">image_src</span>
</span></span><span class="line"><span class="cl">      <span class="n">extension</span>      <span class="o">=</span> <span class="n">image_src</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">last</span>
</span></span><span class="line"><span class="cl">      <span class="n">list</span>           <span class="o">=</span> <span class="n">image_alt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span><span class="o">.</span><span class="n">reverse</span>
</span></span><span class="line"><span class="cl">      <span class="n">title_name</span>     <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">4</span><span class="o">..-</span><span class="mi">1</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">chapter_number</span> <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">3</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">page_number</span>    <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">image_src</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">CrMangaDownloadr</span><span class="o">::</span><span class="no">Image</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">uri</span><span class="o">.</span><span class="n">host</span> <span class="n">as</span> <span class="nb">String</span><span class="p">,</span> <span class="n">uri</span><span class="o">.</span><span class="n">path</span> <span class="n">as</span> <span class="nb">String</span><span class="p">,</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">title_name</span><span class="si">}</span><span class="s2">-Chap-</span><span class="si">#{</span><span class="n">chapter_number</span><span class="si">}</span><span class="s2">-Pg-</span><span class="si">#{</span><span class="n">page_number</span><span class="si">}</span><span class="s2">.</span><span class="si">#{</span><span class="n">extension</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="k">raise</span> <span class="no">Exception</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;Couldn&#39;t find proper metadata alt in the image tag&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now let&rsquo;s check the ported Ruby version:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">page_link</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span> <span class="n">page_link</span> <span class="k">do</span> <span class="o">|</span><span class="n">html</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">images</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">css</span><span class="p">(</span><span class="s1">&#39;#img&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">image_alt</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;alt&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_src</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;src&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">image_alt</span> <span class="o">&amp;&amp;</span> <span class="n">image_src</span>
</span></span><span class="line"><span class="cl">      <span class="n">extension</span>      <span class="o">=</span> <span class="n">image_src</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">last</span>
</span></span><span class="line"><span class="cl">      <span class="n">list</span>           <span class="o">=</span> <span class="n">image_alt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span><span class="o">.</span><span class="n">reverse</span>
</span></span><span class="line"><span class="cl">      <span class="n">title_name</span>     <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">4</span><span class="o">..-</span><span class="mi">1</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">chapter_number</span> <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">3</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">page_number</span>    <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">image_src</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">Image</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">uri</span><span class="o">.</span><span class="n">host</span><span class="p">,</span> <span class="n">uri</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">title_name</span><span class="si">}</span><span class="s2">-Chap-</span><span class="si">#{</span><span class="n">chapter_number</span><span class="si">}</span><span class="s2">-Pg-</span><span class="si">#{</span><span class="n">page_number</span><span class="si">}</span><span class="s2">.</span><span class="si">#{</span><span class="n">extension</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="k">raise</span> <span class="no">Exception</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;Couldn&#39;t find proper metadata alt in the image tag&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s uncanny how similar they are, down to stdlib calls such as <code>URI.parse</code> or Array methods such as <code>split</code>.</p>
<blockquote>
  <p>Once you remove the Type annotations from the Crystal version, it&rsquo;s 99% Ruby.</p>

</blockquote>
<p>Ruby doesn&rsquo;t care if you&rsquo;re trying to call a method on a Nil object - at source code compiling time. Crystal does compile-time checks and if it feels like the method call will be over Nil, it won&rsquo;t even compile. So this is one big win to avoid subtle bugs.</p>
<p>In Rails we are used to the <code>#try</code> method. Ruby 2.3 introduced the <a href="http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/"target="_blank" rel="noopener">safe navigation operator</a>.</p>
<p>So, in Ruby 2.3 with Rails, both of the following lines are valid:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">obj</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:something</span><span class="p">)</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:something2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">obj</span><span class="o">&amp;.</span><span class="n">something</span><span class="o">&amp;.</span><span class="n">something2</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In Crystal we can do the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">obj</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">something</span><span class="p">)</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">something2</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So, it&rsquo;s close. Use with care.</p>
<p>As I mentioned before, Crystal is close to Ruby but it&rsquo;s not meant to be compatible, so we can&rsquo;t just load Rubygems without porting. In this example I don&rsquo;t have Nokogiri to parse the HTML. But this is where the stdlib shines: Crystal comes with good enough XML/HTML and JSON parsers. So we can parse HTML as XML and use plain XPath instead.</p>
<p>Instead of Ruby&rsquo;s <code>Net::HTTP</code> we have <code>HTTP::Client</code> (but their methods and semantics are surprisingly similar).</p>
<p>There are other differences, for example, this is the main file that requires all the others in Ruby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="vg">$LOAD_PATH</span><span class="o">.</span><span class="n">unshift</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">),</span> <span class="s2">&#34;lib&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/records.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/downloadr_client.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/concurrency.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/chapters.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/pages.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/page_image.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/image_downloader.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/workflow.rb&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is the Crystal version of the same manifest:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;./cr_manga_downloadr/*&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>On the other hand, we need to be a bit more explicit in each Crystal source code file, and declare the specific dependencies where needed. For example, in the <code>pages.cr</code> file it starts like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;./downloadr_client&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;xml&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Pages</span> <span class="o">&lt;</span> <span class="no">DownloadrClient</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="nb">String</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Crystal has less room for &ldquo;magic&rdquo; but it&rsquo;s able to maintain a high level of abstraction anyway.</p>
<h3>A Word about Types<span class="hx:absolute hx:-mt-20" id="a-word-about-types"></span>
    <a href="#a-word-about-types" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p>We can spend the next decade masturbating over all there is about Types, and it will be extremely boring.</p>

</blockquote>
<p>The only thing you must understand: the compiler must know the method signature of classes before you can use them. There is no Runtime component that can introspect objects on the fly, like in Ruby and other dynamic languages (even Objective-C/Swift can do more dynamic stuff than Crystal).</p>
<p>Most of the time the Crystal compiler will be smart enough to infer the types for you, so you don&rsquo;t need to be absolutely explicit. You should follow the compiler&rsquo;s lead to know when to use Type Annotations.</p>
<p>What may really scare you at first is the need for Type Annotations, to understand Generics and so forth. The compiler will print out every scary error dump that you will need to get used to. Most scary errors will usually be a Type Annotation missing or you trying to call a method on a possibly Nil object.</p>
<p>For example, if I change the following line in the <code>page_image_spec.cr</code> test file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># line 8:</span>
</span></span><span class="line"><span class="cl"><span class="n">image</span> <span class="o">=</span> <span class="no">CrMangaDownloadr</span><span class="o">::</span><span class="no">PageImage</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;www.mangareader.net&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="s2">&#34;/naruto/662/2&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># line 10:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># image.try(&amp;.host).should eq(&#34;i8.mangareader.net&#34;)</span>
</span></span><span class="line"><span class="cl"><span class="n">image</span><span class="o">.</span><span class="n">host</span><span class="o">.</span><span class="n">should</span> <span class="n">eq</span><span class="p">(</span><span class="s2">&#34;i8.mangareader.net&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The commented out line recognizes that the <code>image</code> instance might come as Nil, so we add an explicit <code>#try</code> call in the spec.</p>
<p>If we try to compile without this recognition, this is the error the compiler will dump on you:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ crystal spec                                                        [
Error in ./spec/cr_manga_downloadr/page_image_spec.cr:10: undefined method &#39;host&#39; for Nil (compile-time type is CrMangaDownloadr::Image?)

    image.host.should eq(&#34;i8.mangareader.net&#34;)
          ^~~~

=============================================================================

Nil trace:

  ./spec/cr_manga_downloadr/page_image_spec.cr:8

        image = CrMangaDownloadr::PageImage.new(&#34;www.mangareader.net&#34;).fetch(&#34;/naruto/662/2&#34;)
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>There is a large stack trace dump after that snippet above, but you only need to pay attention to the first few lines that already say what&rsquo;s wrong: &ldquo;undefined method &lsquo;host&rsquo; for Nil (compile-time type is CrMangaDownloadr::Image?)&rdquo;. If you know how to read, you shouldn&rsquo;t have any problems most of the time.</p>
<p>Now, the <code>Chapters</code>, <code>Pages</code>, <code>PageImage</code> (all subclasses of <code>DownloadrClient</code>) are basically the same thing: they do <code>HTTP::Client</code> requests.</p>
<p>This is how the <code>Pages</code> class is implemented:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Pages</span> <span class="o">&lt;</span> <span class="no">DownloadrClient</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="nb">String</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">chapter_link</span> <span class="p">:</span> <span class="nb">String</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">get</span> <span class="n">chapter_link</span> <span class="k">do</span> <span class="o">|</span><span class="n">html</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">nodes</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">xpath_nodes</span><span class="p">(</span><span class="s2">&#34;//div[@id=&#39;selectpage&#39;]//select[@id=&#39;pageMenu&#39;]//option&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">nodes</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">node</span><span class="o">|</span> <span class="o">[</span><span class="n">chapter_link</span><span class="p">,</span> <span class="n">node</span><span class="o">.</span><span class="n">text</span> <span class="n">as</span> <span class="nb">String</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;/&#34;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>#get</code> is a method from the <code>DownloadrClient</code> superclass that receives a String <code>chapter_link</code> and a block. The block receives a parsed <code>html</code> collection of nodes and we can play with it, returning an Array of Strings.</p>
<p>That&rsquo;s why we have the <code>(Array(String))</code> when inheriting from <code>DownloadrClient</code>. Let&rsquo;s see how the <code>DownloadrClient</code> superclass is implemented.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">DownloadrClient</span><span class="p">(</span><span class="n">T</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="n">uri</span> <span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="no">XML</span><span class="o">::</span><span class="no">Node</span> <span class="o">-&gt;</span> <span class="n">T</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">response</span> <span class="o">=</span> <span class="vi">@http_client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span>
</span></span><span class="line"><span class="cl">      <span class="k">when</span> <span class="mi">301</span>
</span></span><span class="line"><span class="cl">        <span class="n">get</span> <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">[</span><span class="s2">&#34;Location&#34;</span><span class="o">]</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span>
</span></span><span class="line"><span class="cl">      <span class="k">when</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl">        <span class="n">parsed</span> <span class="o">=</span> <span class="no">XML</span><span class="o">.</span><span class="n">parse_html</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">parsed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You can see that this class receives a Generic Type and it uses it as the return type for the yielded block in the <code>#get</code> method. The <code>XML::Node -&gt; T</code> is the declaration of the signature for the block, sending <code>XML::Node</code> and receiving whatever the type <code>T</code> is. At compile time, imagine this <code>T</code> being replaced by <code>Array(String)</code>. That&rsquo;s how you can create classes that can deal with any number of different Types without having to overload for polymorphism.</p>
<p>If you come from Java, C#, Go or any other modern statically typed language, you probably already know what a Generic is.</p>
<p>You can go very far with Generics, check out how our <code>Concurrency.cr</code> begins:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Concurrency</span><span class="p">(</span><span class="n">A</span><span class="p">,</span> <span class="n">B</span><span class="p">,</span> <span class="n">C</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">A</span><span class="p">)</span><span class="sc">?,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="n">A</span><span class="p">,</span> <span class="n">C?</span> <span class="o">-&gt;</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)?</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span> <span class="n">of</span> <span class="n">B</span>
</span></span><span class="line"><span class="cl">    <span class="n">collection</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">engine</span>  <span class="o">=</span> <span class="k">if</span> <span class="vi">@turn_on_engine</span>
</span></span><span class="line"><span class="cl">                  <span class="n">C</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="n">channel</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">      <span class="n">batch</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is how we use it in the <code>workflow.cr</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="kp">private</span> <span class="k">def</span> <span class="nf">fetch_pages</span><span class="p">(</span><span class="n">chapters</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span><span class="sc">?)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;Fetching pages from all chapters ...&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">reactor</span> <span class="o">=</span> <span class="no">Concurrency</span><span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="nb">String</span><span class="p">,</span> <span class="no">Pages</span><span class="p">)</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">reactor</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">chapters</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">link</span><span class="p">,</span> <span class="n">engine</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">engine</span><span class="o">.</span><span class="n">try</span><span class="p">(</span> <span class="o">&amp;.</span><span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In this example, imagine <code>A</code> being replaced by <code>String</code>, <code>B</code> also being replaced by <code>String</code> and C being replaced by <code>Pages</code> in the <code>Concurrency</code> class.</p>
<p>This is the &ldquo;first-version-that-worked&rdquo; so it&rsquo;s probably not very idiomatic. Either this could be solved with less Generics exercising or maybe I could simplify it with the use of Macros. But it&rsquo;s working quite ok as it is.</p>
<p>The pure Ruby version ends up just like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Concurrency</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">engine_klass</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">,</span> <span class="n">config</span> <span class="o">=</span> <span class="no">Config</span><span class="o">.</span><span class="n">new</span><span class="p">,</span> <span class="n">turn_on_engine</span> <span class="o">=</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">    <span class="n">collection</span><span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">mutex</span>   <span class="o">=</span> <span class="no">Mutex</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">      <span class="n">threads</span> <span class="o">=</span> <span class="n">batch</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This version is much &ldquo;simpler&rdquo; in terms of source code density. But on the other hand we would have to test this Ruby version a lot more because it has many different permutations (we even inject classes through <code>engine_klass</code>) that we must make sure respond correctly. In practice we should add tests for all combinations of the initializer&rsquo;s arguments.</p>
<p>In the Crystal version, because all types have been checked at compile-time, it was more demanding in terms of annotations; but we can be pretty sure that if it compiles, it will run as expected.</p>
<blockquote>
  <p>I&rsquo;m not saying that Crystal doesn&rsquo;t need any specs.</p>

</blockquote>
<p>Compilers can only go so far. But how much is &ldquo;so far&rdquo;? Whenever you&rsquo;re &ldquo;forced&rdquo; to add Type Annotations, I will state that those parts are either trying to be too smart or they are intrinsically complex. Those are parts that would require extra levels of tests in Ruby and, if we can add the annotations properly, we can have fewer tests (we don&rsquo;t need to cover most permutations) in the Crystal version (exponential complexity of permutations could go down to a linear complexity, I think).</p>
<h3>A Word about Ruby Threads<span class="hx:absolute hx:-mt-20" id="a-word-about-ruby-threads"></span>
    <a href="#a-word-about-ruby-threads" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The main concepts you must understand about concurrency in Ruby are these:</p>
<ul>
<li>MRI Ruby has a GIL, a Global Interpreter Lock, that forbids code from running concurrently.</li>
<li>MRI Ruby does have access and exposes native Threads since version 1.9. But even if you fire up multiple Threads, they will run sequentially because only one thread can hold the Lock at a time.</li>
<li>I/O operations are the exception: they do release the Lock for other threads to run while the operation is waiting to complete. The OS will signal the program through OS-level poll.</li>
</ul>
<p>This means that if your app is <strong>I/O intensive</strong> (HTTP requests, File reads or writes, socket operations, etc), you will have <em>some</em> concurrency. A web server, such as Puma for example, can take some advantage of Threads because a big part of the operations involves receiving HTTP requests and sending HTTP responses over the wire, which would make the Ruby process idle while waiting.</p>
<p>If your app is CPU intensive (heavy algorithms, data crunching, stuff that really makes the CPU hot) then you can&rsquo;t take advantage of native Threads, only one will run at a time. If you have multiple cores in your CPU, you can Fork your process to the number of cores available.</p>
<p>You should check out <a href="https://github.com/grosser/parallel"target="_blank" rel="noopener">grosser/parallel</a> to make it easy.</p>
<p>This is why Puma also has a &ldquo;worker&rdquo; mode. &ldquo;Worker&rdquo; is the name we usually give to forked children of processes.</p>
<p>In the case of this downloader process, it will perform thousands of HTTP requests to scrape the needed metadata from the MangaReader pages. So it&rsquo;s definitely much more I/O intensive than CPU intensive (the CPU intensive parts are the HTML parsing and later the image resizing and PDF compiling).</p>
<p>A sequential version of what has to be done, in Ruby, looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch_sequential</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">  <span class="n">engine</span>  <span class="o">=</span> <span class="vi">@turn_on_engine</span> <span class="p">?</span> <span class="vi">@engine_klass</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span> <span class="p">:</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span><span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">batch_results</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span><span class="o">&amp;.</span><span class="n">flatten</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">+=</span> <span class="p">(</span> <span class="n">batch_results</span> <span class="o">||</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If we have 10,000 links in the <code>collection</code>, we first slice it to what <code>@config.download_batch_size</code> defines and we iterate over those smaller slices, calling some block and accumulating the results. This is a naive algorithm, as you will find out in the next section, but bear with me.</p>
<p>In Elixir you can fire up micro-processes to make the HTTP requests in parallel. In Crystal you can fire up Fibers and wait for the HTTP requests to complete and signal the results through Channels.</p>
<p>Both are lightweight ways and you can have hundreds or even thousands running in parallel. Manga Reader will probably complain if you try that many at once, so the limit is not in the code, but in the external service.</p>
<p>To transform the sequential version into a concurrent one, this is what we can do in Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">A</span><span class="p">)</span><span class="sc">?,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="n">A</span><span class="p">,</span> <span class="n">C?</span> <span class="o">-&gt;</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)?</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span> <span class="n">of</span> <span class="n">B</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">spawn</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">engine</span>  <span class="o">=</span> <span class="k">if</span> <span class="vi">@turn_on_engine</span>
</span></span><span class="line"><span class="cl">                    <span class="n">C</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                  <span class="k">end</span>
</span></span><span class="line"><span class="cl">        <span class="n">reply</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">channel</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">reply</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">engine</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">close</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">reply</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">reply</span>
</span></span><span class="line"><span class="cl">        <span class="n">results</span><span class="o">.</span><span class="n">concat</span><span class="p">(</span><span class="n">reply</span><span class="o">.</span><span class="n">flatten</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span><span class="o">.</span><span class="n">close</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Processed so far: </span><span class="si">#{</span><span class="n">results</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">size</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fetching a huge collection and slicing it in smaller &lsquo;batches&rsquo; is easy. Now we have a smaller <code>batch</code> collection. For each item (usually a URI) we <code>spawn</code> a Fiber and call a block that will request and process the results. Once it&rsquo;s finished processing it sends the results over a <code>channel</code>.</p>
<p>Once we finish iterating over the batch and spawning that many Fibers we can &ldquo;wait&rdquo; for them by doing <code>channel.receive</code>, which will start receiving results as soon as the Fibers finish requesting/processing each URI.</p>
<p>We accumulate the results and go over the next batch of the collection until finished. The amount of concurrency is determined by the size of the batch (it&rsquo;s like <a href="http://www.akitaonrails.com/en/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate/"target="_blank" rel="noopener">what I did with &lsquo;poolboy&rsquo; over Elixir</a> where we start a fixed number of processes to run in parallel and avoid doing a Denial of Service to Manga Reader).</p>
<p>By the way, this Crystal implementation is similar to what you would do if you were in Go, using Channels.</p>
<p>In the Ruby version you can fire up native Threads - which has a lot of overhead to spawn! - and assume the HTTP requests will run almost all in parallel. Because it&rsquo;s I/O intensive, you can have them all in parallel. This is what it looks like:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span><span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">mutex</span>   <span class="o">=</span> <span class="no">Mutex</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span> <span class="o">=</span> <span class="n">batch</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="no">Thread</span><span class="o">.</span><span class="n">new</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">engine</span>  <span class="o">=</span> <span class="vi">@turn_on_engine</span> <span class="p">?</span> <span class="vi">@engine_klass</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span> <span class="p">:</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">        <span class="no">Thread</span><span class="o">.</span><span class="n">current</span><span class="o">[</span><span class="s2">&#34;results&#34;</span><span class="o">]</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span><span class="o">&amp;.</span><span class="n">flatten</span>
</span></span><span class="line"><span class="cl">        <span class="n">mutex</span><span class="o">.</span><span class="n">synchronize</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">          <span class="n">results</span> <span class="o">+=</span> <span class="p">(</span> <span class="no">Thread</span><span class="o">.</span><span class="n">current</span><span class="o">[</span><span class="s2">&#34;results&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="o">[]</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span><span class="o">.</span><span class="n">each</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:join</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Processed so far: </span><span class="si">#{</span><span class="n">results</span><span class="o">&amp;.</span><span class="n">size</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Threads are all initialized in a &ldquo;paused&rdquo; state. Once we instantiate those many Threads, we can <code>#join</code> on each of them and wait for them all to finish.</p>
<p>Once each Thread finishes the same URI request/process, the results must be accumulated in a global storage, in this case a simple array called <code>results</code>. But because we might have the chance of 2 or more threads trying to update the same array, we might as well synchronize the access (I&rsquo;m not sure if Ruby&rsquo;s Array access is already synchronized, but I guess not). To synchronize access we use a Mutex, which is a fine-grained lock, to make sure only 1 thread can modify the global array at once.</p>
<p>To prove that Ruby can support concurrent I/O operations, I have added 2 methods to the <code>Concurrent</code> class, the first is just <code>#fetch</code> and it&rsquo;s the Thread implementation above. The second is called <code>#fetch_sequential</code> and it is the sequential version also shown at the beginning of this section. And I added the following spec:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">it</span> <span class="s2">&#34;should check that the fetch implementation runs in less time than the sequential version&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">reactor</span> <span class="o">=</span> <span class="no">MangaDownloadr</span><span class="o">::</span><span class="no">Concurrency</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">MangaDownloadr</span><span class="o">::</span><span class="no">Pages</span><span class="p">,</span> <span class="n">config</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;/onepunch-man/96&#34;</span><span class="o">]</span> <span class="o">*</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">  <span class="no">WebMock</span><span class="o">.</span><span class="n">allow_net_connect!</span>
</span></span><span class="line"><span class="cl">  <span class="k">begin</span>
</span></span><span class="line"><span class="cl">    <span class="n">concurrent_measurement</span> <span class="o">=</span> <span class="no">Benchmark</span><span class="o">.</span><span class="n">measure</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">=</span> <span class="n">reactor</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">link</span><span class="p">,</span> <span class="n">engine</span><span class="o">|</span> <span class="n">engine</span><span class="o">&amp;.</span><span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">sequential_measurement</span> <span class="o">=</span> <span class="no">Benchmark</span><span class="o">.</span><span class="n">measure</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">=</span> <span class="n">reactor</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="ss">:fetch_sequential</span><span class="p">,</span> <span class="n">collection</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">link</span><span class="p">,</span> <span class="n">engine</span><span class="o">|</span> <span class="n">engine</span><span class="o">&amp;.</span><span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="sr">/\((.*?)\)$/</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">concurrent_measurement</span><span class="o">.</span><span class="n">to_s</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">cm</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="sr">/\((.*?)\)/</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">sequential_measurement</span><span class="o">.</span><span class="n">to_s</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">sm</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># expected for the concurrent version to be close to 10 times faster than sequential</span>
</span></span><span class="line"><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">sm</span><span class="o">[</span><span class="mi">1</span><span class="o">].</span><span class="n">to_f</span><span class="p">)</span><span class="o">.</span><span class="n">to</span> <span class="n">be</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="n">cm</span><span class="o">[</span><span class="mi">1</span><span class="o">].</span><span class="n">to_f</span> <span class="o">*</span> <span class="mi">9</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">ensure</span>
</span></span><span class="line"><span class="cl">    <span class="no">WebMock</span><span class="o">.</span><span class="n">disable_net_connect!</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Because it uses WebMock, I first disable it during this spec. I create a fake collection of 10 real links to MangaReader. And then I benchmark the Thread-based concurrent version and the plain sequential version. Because we have 10 links and they are all the same you can assume that the sequential version will be almost <strong>10 times slower</strong> than the Thread-based version. And this is exactly what this spec compares and proves (the spec fails if the concurrent version is not at least 9x faster).</p>
<p>To compare all versions of the Manga Downloadrs I let it download and compile an entire manga collection, in this case &ldquo;One-Punch Man&rdquo;, which has almost <strong>1,900 pages/images</strong>. I am just measuring the fetching and scraping processes and skipping the actual image downloading, image resizing and PDF generation as they take the majority of the time and the resizing and PDF part are all done by ImageMagick&rsquo;s mogrify and convert tools.</p>
<p>This is how long this new Ruby version takes to fetch and scrape almost 1,900 pages (using MRI Ruby 2.3.1):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>12,42s user 1,33s system 23% cpu 57,675 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how long the Crystal version takes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>4,03s user 0,40s system 7% cpu 59,207 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Just for fun I tried to run the Ruby version under JRuby 9.1.1.0. To run with JRuby just add the following line in the <code>Gemfile</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">ruby</span> <span class="s2">&#34;2.3.0&#34;</span><span class="p">,</span> <span class="ss">:engine</span> <span class="o">=&gt;</span> <span class="s1">&#39;jruby&#39;</span><span class="p">,</span> <span class="ss">:engine_version</span> <span class="o">=&gt;</span> <span class="s1">&#39;9.1.1.0&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Bundle install, run normally, and this is the result:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>47,80s user 1,99s system 108% cpu 45,967 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is how long the Elixir version takes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>11,38s user 1,04s system 85% cpu 14,590 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Reality Check!<span class="hx:absolute hx:-mt-20" id="reality-check"></span>
    <a href="#reality-check" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If you just look at the times above you might come to the wrong conclusions.</p>
<p>First of all, it&rsquo;s an <strong>unfair comparison</strong>. The Elixir version uses a very different algorithm than the Ruby and Crystal versions.</p>
<p>In Elixir I boot up a pool of processes, around 50 of them. Then I start 50 HTTP requests at once. Once each process finishes, it releases itself back to the pool and I can fire up another HTTP request from the queue of links. So it&rsquo;s a <strong>constant stream</strong> of at most 50 HTTP requests, constantly.</p>
<p>The Crystal and Ruby/JRuby versions slice the 1,900 links into batches of 40 links and then I fire up 40 requests at once. This implementation waits for all 40 to finish and they fire up another 40. So it&rsquo;s never a constant stream, it&rsquo;s bursts of 40 requests. So each batch is slowed down by the slowest request in the batch, not giving a chance for other requests to go through.</p>
<p>It&rsquo;s a difference in architecture. Elixir makes it much easier to do streams and Crystal, with its CSP style, makes it easier to do bursts. A better approach would be to queue up the 1,900 links and use something like Sidekiq.cr to go over one link at a time (spawning 40 fibers to serve as a &ldquo;pool&rdquo;, for example).</p>
<p>The Elixir version has a more efficient architecture, which is why it takes no more than 15 seconds to fetch all the image links while the Crystal version takes almost a full minute to finish (the accumulation of the slowest requests in each batch).</p>
<p>Now, you will be surprised that the Crystal version is actually a bit slower than the Ruby version! And you won&rsquo;t be too surprised to see JRuby being faster at 45 seconds!</p>
<p>This is more evidence that you should not dismiss Ruby (and that you should try JRuby more often). As I explained before, it does support concurrency in I/O operations and the applications I tested are all I/O heavy.</p>
<p>The difference is probably in the maturity of Ruby&rsquo;s <code>Net::HTTP</code> library against Crystal&rsquo;s <code>HTTP::Client</code>. I tried many tweaks in the Crystal version but I couldn&rsquo;t get much faster. I tried to do larger batches, but for some reason the application just hangs, pauses, and never releases. I have to Ctrl-C out of it and retry until it finally goes through. If someone knows what I am doing wrong, please don&rsquo;t forget to write in the comments section below.</p>
<p>Some of this is probably due to MangaReader&rsquo;s unreliable servers, they probably have some kind of DoS protection, throttling connections or something like this.</p>
<p>Anyway, when they go through, because the Ruby and Crystal algorithms are essentially the same, they take roughly the same time to complete. So what&rsquo;s missing is for me to evolve this algorithm to either use something like Sidekiq or implement an internal queue/pool of workers scheme.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The goal of this experiment was to learn more of Crystal&rsquo;s ability and how easy it would be to go back and forth with Ruby.</p>
<p>As you can see, there are many differences, but it&rsquo;s not so difficult. I may be missing something, but I stumbled upon some difficulties when pushing <code>HTTP::Client</code> + Fibers too hard, as I explained above. If you know what I am doing wrong, please let me know.</p>
<p>The difference between the Elixir vs Ruby/Crystal algorithms shows not just language performance differences, but also the importance of architecture and algorithms in the overall performance. This test was not conclusive, just a hint that my naive Fibers implementation needs some more work, and that Elixir&rsquo;s natural handling of parallel processes makes it easier to achieve higher levels of parallelism.</p>
<p>This serves as a taste of what Crystal can do, and also that Ruby is still in the game. But also that Elixir surely is something to look at closely.</p>
]]></content:encoded><category>anime-and-manga</category><category>programming-languages</category><category>ruby</category></item><item><title>Flirting with Crystal, a Rubyist Perspective</title><link>https://akitaonrails.github.io/en/2016/05/31/flirting-with-crystal-a-rubyist-perspective/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/05/31/flirting-with-crystal-a-rubyist-perspective/</guid><pubDate>Tue, 31 May 2016 18:21:00 -0300</pubDate><description>&lt;p&gt;If you&amp;rsquo;re following me you will see that I have been sidetracking to &lt;a href="https://akitaonrails.github.io/elixir"&gt;Elixir&lt;/a&gt; recently. I will write a big introduction before diving into Crystal, so bear with me.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll say that Erlang OTP and its internals intrigue me more than just the novelty of the language syntax. I believe that if you want to build highly distributed and also highly reliable systems you will either use Erlang (through Elixir) or you will end up replicating most of what Erlang already has with OTP.&lt;/p&gt;</description><content:encoded><![CDATA[<p>If you&rsquo;re following me you will see that I have been sidetracking to <a href="/elixir">Elixir</a> recently. I will write a big introduction before diving into Crystal, so bear with me.</p>
<p>I&rsquo;ll say that Erlang OTP and its internals intrigue me more than just the novelty of the language syntax. I believe that if you want to build highly distributed and also highly reliable systems you will either use Erlang (through Elixir) or you will end up replicating most of what Erlang already has with OTP.</p>
<p>This is true if you see that there are many Java/Scala-based distributed systems such as Hadoop, Spark, Cassandra, and they all could have benefitted from Erlang&rsquo;s OTP architecture. On the other hand, if you follow Cassandra you will see that even Java still has a hard time competing with C++ if you compare the clone called <a href="http://www.scylladb.com/"target="_blank" rel="noopener">ScyllaDB</a>.</p>
<p>I believe Elixir (perhaps with HiPE) can compete in the same league as Java for distributed systems, avoiding people leaving the Erlang platform for C or Java as <a href="https://www.infoq.com/news/2012/01/Katz-CouchDB-Couchbase-Server"target="_blank" rel="noopener">happened with CouchDB</a> back in 2012 because of lack of interest and exotic syntax. And because Java already has a big ecosystem, Clojure is in the game primarily because it can interface directly with it.</p>
<p>I think Go is also a Java contender but for other use cases, particularly in the systems tools space where you have lots of C/C++ and also glue code in Perl and Python. An obvious example being Docker orchestration. Yes, you can do microservices, crawlers and other stuff but I am not so keen to build big &ldquo;applications&rdquo; in it, although there is nothing preventing it. Again, it&rsquo;s just a personal opinion.</p>
<p>Rust is a big contender to low-level systems development. I think it can replace C in many use cases and be used to implement libraries and components to be used by other languages, with the added benefits of a modern language that tries to avoid security hazards such as the recent <a href="https://tonyarcieri.com/would-rust-have-prevented-heartbleed-another-look"target="_blank" rel="noopener">Heartbleed debacle</a>. But because of its &ldquo;Borrow system&rdquo; I find it extremely bureaucratic, especially if you&rsquo;re coming from highly dynamic languages such as Ruby, Python or even Swift or Elixir. I&rsquo;m not saying it&rsquo;s a bad thing, just something that takes way more time to become comfortable with than I expected.</p>
<p>Crystal is something between Rust and Go. LLVM is something you must have on your radar, because of <a href="http://appleinsider.com/articles/08/06/20/apples_other_open_secret_the_llvm_complier"target="_blank" rel="noopener">Apple&rsquo;s support</a> towards Swift it&rsquo;s better than ever. Crystal is <em>similar</em> to RubyMotion in terms of both being similar to Ruby but not fully compatible and both being front-end parsers to LLVM. RubyMotion, on the other hand, is closer to Ruby and can even use some Ruby libraries almost without changes.</p>
<p>You do have to take your hats off to Ary Borenszweig and his contributors. It&rsquo;s very impressive how far they got in such a short period of time and without having deep pockets from Mozilla, Apple or Google.</p>
<h3>Limitations<span class="hx:absolute hx:-mt-20" id="limitations"></span>
    <a href="#limitations" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crystal borrows heavily from Ruby but it&rsquo;s not a goal to reach any level of compatibility. It&rsquo;s a strong, statically typed language with Type Inference. It does not have any runtime component such as RubyMotion or Swift, so it has no notion of introspection or reflection over objects.</p>
<p>Mainly, you have neither &ldquo;#eval&rdquo; nor &ldquo;#send&rdquo;. The lack of runtime evaluation is not so bad as you do have compilation-time AST manipulation through a good enough system of Macros (more on that later).</p>
<p>But the lack of &ldquo;#send&rdquo; does hurt a bit. It&rsquo;s the one thing that makes Crystal further away from Ruby&rsquo;s dynamic object flexibility. But it&rsquo;s also understandable why it&rsquo;s not a priority to have it.</p>
<p>As of version 0.17.0, Crystal has one huge limitation: it&rsquo;s using a <a href="http://crystal-lang.org/2013/12/05/garbage-collector.html"target="_blank" rel="noopener">Boehm-Demers-Weiser conservative garbage collector</a>.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/asterite">@asterite</a> <a href="https://twitter.com/headius">@headius</a> I assumed as much... the Boehm GC, although handy for starting a new language, will only get you so far</p>&mdash; Jason Frey (@Fryguy9) <a href="https://twitter.com/Fryguy9/status/736990644082757632">May 29, 2016</a></blockquote>
<p>The language is currently implemented as a single-threaded process. This means that you <em>probably</em> can&rsquo;t max out all CPUs of your machine with just a single process. Although I may be wrong here, it&rsquo;s just my first impressions.</p>
<p>Charles Nutter (from JRuby fame) gives a warning about this:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/Fryguy9">@fryguy9</a> From their site: <a href="https://t.co/uOjIT0c8ji">https://t.co/uOjIT0c8ji</a> It does say something about "when" parallelism happens. Risky to start single-threaded.</p>&mdash; Charles Nutter (@headius) <a href="https://twitter.com/headius/status/736982955239870464">May 29, 2016</a></blockquote>
<p>It&rsquo;s a double-edged sword. Using a &ldquo;generic&rdquo; plug-and-play GC such as Boehm - which is not a bad thing in itself, but it&rsquo;s possibly not nearly as powerful as the JVM&rsquo;s own set of high performance GC such as the brand new G1GC.</p>
<p>Making the language single-threaded by default also means that the entire standard library and the whole ecosystem are built assuming there is no parallelism. So there are no race-conditions and hence, no usage of proper synchronization anywhere. If one day they decide to add multi-threading, all existing code will not be thread-safe. This is probably the meaning of Charles&rsquo; warning.</p>
<p>I don&rsquo;t think it&rsquo;s a pressing problem though. You probably won&rsquo;t have a highly parallel system built with Crystal but maybe it&rsquo;s not the use case. In that case you should be using Elixir + OTP, Scala + Akka, for example.</p>
<h3>Concurrency<span class="hx:absolute hx:-mt-20" id="concurrency"></span>
    <a href="#concurrency" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Not having access to native Threads doesn&rsquo;t mean that you can&rsquo;t have Concurrency. <a href="https://blog.golang.org/concurrency-is-not-parallelism"target="_blank" rel="noopener">Concurrency is not Parallelism</a>. Since <a href="http://crystal-lang.org/2015/04/30/crystal-0.7.0-released.html"target="_blank" rel="noopener">version 0.7.0</a> they have added support for non-blocking I/O and also Fibers and Channels.</p>
<p>In a nutshell, a Fiber is a special kind of coroutine. It&rsquo;s a &ldquo;piece of execution&rdquo; that can &ldquo;pause&rdquo; itself and yield control of the execution back to its caller. It can pause itself but it can&rsquo;t be paused from the outside like Erlang&rsquo;s Scheduler can pause its running processes, for example.</p>
<p>In this case we can have a &ldquo;Fiber Scheduler&rdquo; of sorts, that can &ldquo;resume&rdquo; other Fibers. It is currently single-threaded, of course. And it works as an Event Loop. Whenever you have a non-blocking I/O operation waiting for file reading, network packet sending and stuff like that, it can yield control back to other fibers to resume work.</p>
<p>This makes it work more or less like Node.js. Javascript is also a single-threaded language and Node.js is basically an implementation of an event loop. Functions declare other anonymous functions as callbacks and it works based on the I/O triggers to callback those functions on every tick of the reactor.</p>
<p>On top of that you can add <a href="https://github.com/crystal-lang/crystal/issues/1967"target="_blank" rel="noopener">&ldquo;Channels&rdquo;</a> as popularized by Google&rsquo;s Go language. You can start as many channels as you like. Then you can spawn Fibers in the Scheduler. Fibers can execute and keep sending messages through the Channels. Execution control is yielded to whoever is expecting to receive from the same Channels. Once one of them receives and executes, control is sent back to the Scheduler to allow other spawned Fibers to execute, and they can keep &ldquo;pinging&rdquo; and &ldquo;ponging&rdquo; like this.</p>
<p>Speaking of ping-pong, you have <a href="https://gobyexample.com/channel-directions"target="_blank" rel="noopener">this snippet in the &ldquo;Go by Example&rdquo;</a> site:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="s">&#34;fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">ping</span><span class="p">(</span><span class="nx">pings</span><span class="w"> </span><span class="kd">chan</span><span class="o">&lt;-</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pings</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nx">msg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">pong</span><span class="p">(</span><span class="nx">pings</span><span class="w"> </span><span class="o">&lt;-</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">pongs</span><span class="w"> </span><span class="kd">chan</span><span class="o">&lt;-</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">pings</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pongs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nx">msg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pings</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pongs</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nf">ping</span><span class="p">(</span><span class="nx">pings</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;passed message&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nf">pong</span><span class="p">(</span><span class="nx">pings</span><span class="p">,</span><span class="w"> </span><span class="nx">pongs</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="o">&lt;-</span><span class="nx">pongs</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is mostly the same thing but implemented in Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ping</span><span class="p">(</span><span class="n">pings</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">pings</span><span class="o">.</span><span class="n">send</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pong</span><span class="p">(</span><span class="n">pings</span><span class="p">,</span> <span class="n">pongs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">message</span> <span class="o">=</span> <span class="n">pings</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl">  <span class="n">pongs</span><span class="o">.</span><span class="n">send</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">pings</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">pongs</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">spawn</span> <span class="n">ping</span> <span class="n">pings</span><span class="p">,</span> <span class="s2">&#34;passed message&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">spawn</span> <span class="n">pong</span> <span class="n">pings</span><span class="p">,</span> <span class="n">pongs</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">pongs</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; &#34;passed message&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Same thing, but more pleasant to my eyes, again a personal opinion - which is expected if you&rsquo;re also a Rubyist.</p>
<p>So Crystal has Node.js/Javascript-like Event Loop in the form of a Fiber Scheduler and a Go-like Channel/CSP mechanism to coordinate concurrent processing. It&rsquo;s a &ldquo;cooperative multitasking&rdquo; mechanism. It&rsquo;s not good if you have CPU intensive processing, such as heavy number crunching, data processing. It works best for I/O intensive processing.</p>
<p>If you have a Fiber doing super heavy processing, it will block the Scheduler, as is also true for Node.js. The current implementation of the Scheduler is bound to a single native thread so it can&rsquo;t use other available native threads of the system for now.</p>
<p>Because the Channel implementation is quite new, it&rsquo;s not used throughout the standard library. But the standard implementation for <code>HTTP::Server</code> uses Fibers. And because it is compiled down to a native binary it&rsquo;s <strong>way faster</strong> than Javascript, as this <a href="https://github.com/sdogruyol/fast-http-server"target="_blank" rel="noopener">&ldquo;fast-http-server&rdquo;</a> shows:</p>
<ul>
<li>fast-http-server (Crystal)  18348.47rpm at 8.67ms avg. response time</li>
<li>http-server (Node.js)   2105.55rpm at 47.92ms avg. response time</li>
<li>SimpleHTTPServer (Python)   785.14rpm at 1.91ms avg. response time</li>
</ul>
<p>We&rsquo;re talking about an order of magnitude of 8 times faster than Node.js and more than 20 times faster than Python.</p>
<p>As usual, you shouldn&rsquo;t blindly trust <a href="https://github.com/kostya/benchmarks"target="_blank" rel="noopener">synthetic benchmarks</a> but depending on which kind of processing you decide to compare against, you will see Crystal being pretty on par with Rust, Go, Swift, D.</p>
<h3>Type Inference<span class="hx:absolute hx:-mt-20" id="type-inference"></span>
    <a href="#type-inference" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crystal is a statically typed language. But it won&rsquo;t require you to declare every single type beforehand. It&rsquo;s quite smart in its Type Inference so if you just type:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">b</span> <span class="o">=</span> <span class="s2">&#34;HELO&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It will know that &ldquo;a&rdquo; is an &ldquo;Int32&rdquo; and that &ldquo;b&rdquo; is a &ldquo;String&rdquo;. By the way, contrary to Ruby, all Strings must be double-quoted. But it becomes particularly more complicated when you&rsquo;re dealing with complicated data structures such as JSON.</p>
<p>In Ruby I can just parse a JSON String and immediately explore its structure like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">parsed_json</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;files&#34;</span><span class="o">].</span><span class="n">first</span><span class="o">[</span><span class="s2">&#34;id&#34;</span><span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I can&rsquo;t do this in Crystal, instead the <a href="http://crystal-lang.org/api/JSON.html#mapping%28properties%2Cstrict%3Dfalse%29-macro"target="_blank" rel="noopener">recommended approach</a> is to declare a schema-like structure like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FilesResponse</span>
</span></span><span class="line"><span class="cl">  <span class="no">JSON</span><span class="o">.</span><span class="n">mapping</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="ss">ok</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="no">Bool</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="ss">files</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="no">FilesItemResponse</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FilesItemResponse</span>
</span></span><span class="line"><span class="cl">  <span class="no">JSON</span><span class="o">.</span><span class="n">mapping</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nb">name</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="ss">filetype</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="ss">created</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="no">Time</span><span class="p">,</span> <span class="ss">converter</span><span class="p">:</span> <span class="no">Time</span><span class="o">::</span><span class="no">EpochConverter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="ss">timestamp</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="no">Time</span><span class="p">,</span> <span class="ss">converter</span><span class="p">:</span> <span class="no">Time</span><span class="o">::</span><span class="no">EpochConverter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="ss">permalink</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="c1"># parsed_json = JSON.parse(response.body)[&#34;files&#34;].first[&#34;id&#34;]</span>
</span></span><span class="line"><span class="cl"><span class="n">parsed_json</span> <span class="o">=</span> <span class="no">FilesResponse</span><span class="o">.</span><span class="n">from_json</span><span class="p">(</span><span class="vi">@body</span><span class="p">)</span><span class="o">.</span><span class="n">files</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">id</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I am very used to Ruby&rsquo;s duck typing and the ability to query objects in runtime. It&rsquo;s just not the same in Crystal and it can be quite tedious to change your mindset to think about types beforehand. The compiler will scream a hell of a lot during your development cycle until you become acquainted with this concept.</p>
<p>I did an experiment with JSON parsing and the result is a project I called <a href="https://github.com/akitaonrails/cr_slack_cleanup"target="_blank" rel="noopener">&ldquo;cr_slack_cleanup&rdquo;</a> which showcases both this idea of JSON Schemas as well as Fibers and Channels as I explained in the previous section.</p>
<p>Update: after I posted this article @LuisLavena stepped in to correct me: you can do it like Ruby, without schemas:</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr"><a href="https://twitter.com/AkitaOnRails">@AkitaOnRails</a> <a href="https://twitter.com/CrystalLanguage">@CrystalLanguage</a> re: JSON, you can parse it kinda-like Ruby, see JSON::Any: <a href="https://t.co/MD7PYy5AVH">https://t.co/MD7PYy5AVH</a></p>&mdash; Luis Lavena (@luislavena) <a href="https://twitter.com/luislavena/status/738005493189316608">June 1, 2016</a></blockquote>
<p>And it will be like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;json&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">data</span> <span class="o">=</span> <span class="s">&lt;&lt;-JSON
</span></span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;files&#34;</span><span class="p">:</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;photo.jpg&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="mi">99</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;another.jpg&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">JSON</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">obj</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">obj</span><span class="o">[</span><span class="s2">&#34;files&#34;</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="n">item</span><span class="o">[</span><span class="s2">&#34;id&#34;</span><span class="o">].</span><span class="n">as_i</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So yeah, JSON parsing can be almost identical to what you would do with Ruby, although he also recommends Schemas as it&rsquo;s a bit faster anyway.</p>
<h3>Macros<span class="hx:absolute hx:-mt-20" id="macros"></span>
    <a href="#macros" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The last important feature worth mentioning is the presence of Macros. As you don&rsquo;t have control over your code in runtime, you don&rsquo;t have &ldquo;eval&rdquo;, you don&rsquo;t have &ldquo;send&rdquo;, then you would not have any way to do proper metaprogramming.</p>
<p>Macros bring back some of Ruby&rsquo;s metaprogramming. For example, in Ruby, when you include a Module to a Class, there is the &ldquo;included?&rdquo; hook in the Module where you can add some metaprogramming such as &ldquo;class_eval&rdquo; stuff.</p>
<p>In Crystal, a Module does have an &ldquo;included&rdquo; hook but now it&rsquo;s a Macro. You can do something like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Foo</span>
</span></span><span class="line"><span class="cl">  <span class="n">macro</span> <span class="n">included</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">%</span> <span class="k">if</span> <span class="vi">@type</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;Bar&#34;</span> <span class="s">%}
</span></span></span><span class="line"><span class="cl"><span class="s">    def self.hello
</span></span></span><span class="line"><span class="cl"><span class="s">      puts &#34;Bar&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">    end
</span></span></span><span class="line"><span class="cl"><span class="s">    {% else %}</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">hello</span>
</span></span><span class="line"><span class="cl">      <span class="nb">puts</span> <span class="s2">&#34;HELLO&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">%</span> <span class="k">end</span> <span class="o">%</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Bar</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Foo</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Something</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Foo</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Bar</span><span class="o">.</span><span class="n">hello</span> <span class="c1"># =&gt; &#34;Bar&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Something</span><span class="o">.</span><span class="n">hello</span> <span class="c1"># =&gt; &#34;HELLO&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It feels like having &ldquo;ERB&rdquo; templates but for code. A macro is code building code in compile time. In the resulting AST it&rsquo;s as if you wrote the boring repetitive code yourself. The compiled native binary doesn&rsquo;t care. If you&rsquo;re from C it&rsquo;s like pre-processing but having control of an AST tree of Nodes instead of just manipulating the source code. You can even have something akin to the venerable &ldquo;#method_missing&rdquo;.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Foo</span>
</span></span><span class="line"><span class="cl">  <span class="n">macro</span> <span class="nb">method_missing</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{{</span><span class="n">pp</span> <span class="nb">name</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">hello_world</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; name = &#34;hello_world&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">bla_bla</span><span class="p">(</span><span class="s2">&#34;bla&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; name = &#34;bla_bla&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Any macro function can receive an AST as the argument and you can manipulate this AST in any way as you see fit. For example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>module Foo
  macro teste(ast)
    puts {{ast.stringify}}
  end
end

Foo.teste &#34;Hello World&#34;.split(&#34; &#34;).join(&#34; - &#34;)
# =&gt; (&#34;Hello World&#34;.split(&#34; &#34;)).join(&#34; - &#34;)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In this example you just get a normalized version of the code you passed as argument. And the code won&rsquo;t be &ldquo;executed&rdquo; at that location.</p>
<p>So, you can define new methods and call different versions of methods depending on the combination of elements that you get from the AST. One example of what is possible is this experiment from developer Oleksii that I bundled in this small project I called <a href="https://github.com/akitaonrails/cr_chainable_methods"target="_blank" rel="noopener">&ldquo;cr_chainable_methods&rdquo;</a> and we can build code that is very different from either Crystal or Ruby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">pipe</span> <span class="s2">&#34;Hello World&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Foo</span><span class="o">.</span><span class="n">split_words</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Foo</span><span class="o">.</span><span class="n">append_message</span><span class="p">(</span><span class="s2">&#34;Bar&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Bar</span><span class="o">.</span><span class="n">add_something</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Foo</span><span class="o">.</span><span class="n">join</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="n">unwrap</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(Yes, a subset of what a real Elixir Pipe can do.)</p>
<h3>Crystal Shards<span class="hx:absolute hx:-mt-20" id="crystal-shards"></span>
    <a href="#crystal-shards" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Finally, one aspect I like about Crystal is that it has task management and dependency management already figured out. No pointless discussions on which implementation of packages and dependencies we &ldquo;should&rdquo; be using.</p>
<p>You can just write a simple script into a file such as &ldquo;test.cr&rdquo; and run this file like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>crystal test.cr</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Or, you can build and compile to a native binary before executing like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>crystal build test.cr --release
./test</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And you can also start in a proper project structure and development lifecycle like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>crystal init app test
cd test
crystal build src/test.cr --release
./test
git add .
git commit -m &#34;initial commit&#34;
git push -u origin master</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It sets up a proper project directory structure, a Git repository is pre-initialized for you (why would you be programming without Git!?) with proper &ldquo;.gitignore&rdquo; file.</p>
<p>You will also find a &ldquo;shard.yml&rdquo; file where you can declare your application or library name, version, author, and also its dependencies like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">kemal</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.12.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">dependencies</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">radix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">github</span><span class="p">:</span><span class="w"> </span><span class="l">luislavena/radix</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.3.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">kilt</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">github</span><span class="p">:</span><span class="w"> </span><span class="l">jeromegn/kilt</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.3.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">author</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">Serdar Dogruyol &lt;dogruyolserdar@gmail.com&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This example is from the <a href="https://github.com/sdogruyol/kemal"target="_blank" rel="noopener">Kemal web framework</a> by Serdar. It depends on the Radix and Kilt libraries. You must run <code>crystal deps</code> and it will fetch from Github before you can compile.</p>
<p>More than that: every project receives a proper &ldquo;spec&rdquo; directory where you can write Rspec-like tests and you can run them using <code>crystal spec</code>. And the spec runner results will look like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ crystal spec
........................................................

Finished in 8.28 milliseconds
56 examples, 0 failures, 0 errors, 0 pending</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>All built-in, no external libraries needed.</p>
<p>And 8 milliseconds to run the entire test suite of a micro web framework? Not too shabby. If you add up start up time for the executable, it still is around 1 second of total running time. Pretty impressive.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crystal is not meant to be a Ruby replacement like JRuby. So it will never run existing and complicated Ruby projects such as Ruby on Rails or even Sinatra for that matter.</p>
<p>To me Crystal fills a gap in some use cases where I would use Rust or Go. It has the raw performance of an LLVM-optimized native binary, a reasonably fast Go-like concurrency system (and much faster and less resource intensive than Node.js), and the added benefit of a much more enjoyable Ruby-like syntax set.</p>
<p>You can even start experimenting with Crystal to build Ruby native extensions without the need of FFI and without having to write C. There is an attempted <a href="https://github.com/startram/activesupport.cr"target="_blank" rel="noopener">port of ActiveSupport</a> as a native extension to MRI Ruby, written as a proof of concept in Crystal.</p>
<p>If you&rsquo;re a Rubyist you will find yourself pretty much at home with most of the standard library. You will miss a thing or two but most of the time it will feel like Ruby. The <a href="http://crystal-lang.org/docs"target="_blank" rel="noopener">official Guide</a> is good enough to get started and there is a comprehensive <a href="http://crystal-lang.org/api"target="_blank" rel="noopener">API documentation</a> just to check if that favorite Ruby API is there or not, and if not what the replacements are. Most of the time you will think <em>&ldquo;hmm, in Ruby I would use Enumerator#each &hellip; and yes, sure enough, there is Enumerator#each as expected.&rdquo;</em> The API documentation could use some more love, so if you want to contribute this is a good place to start.</p>
<p>If you want to check out a curated list of Crystal libraries and applications, go straight to <a href="http://awesome-crystal.com/"target="_blank" rel="noopener">&ldquo;Awesome Crystal&rdquo;</a>. You can also see the more dynamic <a href="https://github.com/trending/crystal"target="_blank" rel="noopener">&ldquo;Trending Projects in Github&rdquo;</a>.</p>
<p>For the most part, your proficiency in Ruby will pay off to build small systems where raw performance and concurrency are really necessary. This is exactly the use case for Mike Perham&rsquo;s port of <a href="https://github.com/mperham/sidekiq.cr"target="_blank" rel="noopener">Sidekiq.cr</a>. Say you have gigabytes of files to crunch. Or you have thousands of external websites to crawl, parse and organize. Or you have petabytes of data in your database waiting to be number crunched. Those are tasks that MRI Ruby won&rsquo;t help with but Crystal may well be the quick answer.</p>
<p>You can even get one of the many micro web frameworks such as Kemal and deploy your HTTP API providers <a href="http://crystal-lang.org/2016/05/26/heroku-buildpack.html"target="_blank" rel="noopener">directly to Heroku</a> now. This will give you Go-levels of performance, and this is <strong>very</strong> compelling for me as I really also dislike Go&rsquo;s archaic smelling syntax.</p>
<p>So, Rust, Go, Javascript, all very fast, but with very questionable syntaxes and not so enjoyable. They are way more mature, and their ecosystems are much larger. But unless I really need to, I&rsquo;d rather choose Crystal for the use cases I described above, and I think you will find some good use for it too.</p>
<p>Update: after I posted, the creator of Crystal, @Asterite had a few things to add as well:</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr"><a href="https://twitter.com/AkitaOnRails">@AkitaOnRails</a> <a href="https://twitter.com/waj">@waj</a> so it&#39;s likely that we&#39;ll have something similar. We aren&#39;t there yet because we are not Google, Mozilla nor Apple</p>&mdash; Ary Borenszweig (@asterite) <a href="https://twitter.com/asterite/status/737778080085868545">May 31, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
]]></content:encoded><category>crystal</category><category>ruby</category><category>programming-languages</category></item><item><title>Elixir Pipe Operator for Ruby: Chainable Methods</title><link>https://akitaonrails.github.io/en/2016/02/18/elixir-pipe-operator-for-ruby-chainable-methods/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/02/18/elixir-pipe-operator-for-ruby-chainable-methods/</guid><pubDate>Thu, 18 Feb 2016 16:28:00 -0200</pubDate><description>&lt;p&gt;There have been &lt;a href="http://blog.molawson.com/elixir-pipes-in-ruby/"target="_blank" rel="noopener"&gt;recent&lt;/a&gt; &lt;a href="https://gist.github.com/pcreux/2f87847e5e4aad37db02"target="_blank" rel="noopener"&gt;discussions&lt;/a&gt; about how nice it would be to have something like the awesome Elixir Pipe Operator for Ruby.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t know what the &amp;ldquo;Pipe Operator&amp;rdquo; is in Elixir, take the following code:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;odd?&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;rsquo;s ugly, we all know that. In an Object Oriented language like Ruby we would do something like this:&lt;/p&gt;</description><content:encoded><![CDATA[<p>There have been <a href="http://blog.molawson.com/elixir-pipes-in-ruby/"target="_blank" rel="noopener">recent</a> <a href="https://gist.github.com/pcreux/2f87847e5e4aad37db02"target="_blank" rel="noopener">discussions</a> about how nice it would be to have something like the awesome Elixir Pipe Operator for Ruby.</p>
<p>If you don&rsquo;t know what the &ldquo;Pipe Operator&rdquo; is in Elixir, take the following code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Enum</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="no">Enum</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">,</span> <span class="o">&amp;</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)),</span> <span class="n">odd?</span><span class="p">))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s ugly, we all know that. In an Object Oriented language like Ruby we would do something like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">*</span> <span class="mi">3</span> <span class="p">}</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">select</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:odd?</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">reduce</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:+</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>But Elixir does not have objects, only functions, so how can we code more elegantly? The solution came up in the form of the so called &ldquo;Pipe Operator&rdquo; which takes the last returning value and passes it through as the first argument of the next function call, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Stream</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span> <span class="o">*</span> <span class="mi">3</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Stream</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">odd?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">sum</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So Ruby and Elixir &ldquo;feel&rdquo; the same when we are able to &ldquo;chain&rdquo; methods. In the Ruby world we don&rsquo;t have the &ldquo;need&rdquo; for an operator like that. But it would be nice to have a mechanism that we could use to make our code more expressive, or more testable, or more readable. For example, what if we could write something like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">multiple_each_element_by_three</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">filter_out_odd_elements</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">give_the_sum_of_all_elements</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Of course, this is a very constrained example with really bad method naming. But if we look at Mo Lawson&rsquo;s article I linked above, it becomes more interesting:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">keywords</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="n">kw</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/(^[^[:alpha:]]+|[^[:alpha:]]+$)/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="no">LegacySpanishCorrector</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">correct</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="n">kw</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/[^[:alpha:]\d&#39;_\-\/]/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="no">STOP_WORDS</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span> <span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ruby allows us to chain Enumerable methods one after the other, transforming the initial keywords list into &ldquo;something&rdquo; that is very difficult to infer just by looking at this code.</p>
<p>What about this other version?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">KeywordNormalizer</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">keywords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Collection</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">keywords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">strip_outer_punctuation</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">fix_spanish</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">clean_inner_punctuation</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">remove_stop_words</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">to_a</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is where he ends up by the end of his article: much more readable and each new isolated method is unit testable, resulting in more robust code.</p>
<p>The whole idea of this post is to introduce you to my new little gem <a href="https://rubygems.org/gems/chainable_methods"target="_blank" rel="noopener">&ldquo;Chainable Methods&rdquo;</a>. The <a href="https://github.com/akitaonrails/chainable_methods"target="_blank" rel="noopener">source code</a> is on Github, as usual, so please contribute.</p>
<p>My gem will allow you to write Lawson&rsquo;s last code like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">KeywordNormalizer</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">chain_from</span><span class="p">(</span><span class="n">keywords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">strip_outer_punctuation</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">fix_spanish</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">clean_inner_punctuation</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">remove_stop_words</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">to_a</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">unwrap</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You add the <tt>chainable_methods</tt> to your Gemfile as usual (you know the drill), then you can write Lawson&rsquo;s module like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">KeywordNormalizer</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">ChainableMethods</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">strip_outer_punctuation</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="n">el</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/(^[^[:alpha:]]+|[^[:alpha:]]+$)/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">fix_spanish</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="no">LegacySpanishCorrector</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">correct</span><span class="p">(</span><span class="n">el</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">clean_inner_punctuation</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="n">el</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/[^[:alpha:]\d&#39;_\-\/]/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">remove_stop_words</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="no">STOP_WORDS</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">el</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And that&rsquo;s it, now you can chain everything like I showed previously. The pattern is:</p>
<ol>
<li>Write a Module with class-level methods that receive at least one argument and extend the &lsquo;ChainableMethods&rsquo; module:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MyModule</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">ChainableMethods</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_a</span><span class="p">(</span><span class="n">argument1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_b</span><span class="p">(</span><span class="n">argument1</span><span class="p">,</span> <span class="n">argument2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_c</span><span class="p">(</span><span class="n">argument1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># yield a block passing the received argument</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ol start="2">
<li>Wrap an initial state that will be passed to the first method as its first argument:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">some_initial_state</span> <span class="o">=</span> <span class="s2">&#34;Hello World&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">MyModule</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">chain_from</span><span class="p">(</span><span class="n">some_initial_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># ...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ol start="3">
<li>Chain as many methods from the module or methods that the returning state recognizes:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyModule</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">chain_from</span><span class="p">(</span><span class="n">some_initial_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">upcase</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">method_a</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">method_b</span><span class="p">(</span><span class="n">argument2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">method_c</span> <span class="p">{</span> <span class="o">|</span><span class="n">foo</span><span class="o">|</span> <span class="n">foo</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;, &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">unwrap</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice that we do not need to pass the first argument to the methods within the &lsquo;MyModule&rsquo; module, it will get the result from the last call automatically.</p>
<ol start="4">
<li>Do not forget to call <tt>#unwrap</tt> as the last call to get the last result from the chain.</li>
</ol>
<h3>An Experiment<span class="hx:absolute hx:-mt-20" id="an-experiment"></span>
    <a href="#an-experiment" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>And that&rsquo;s it! I isolated this behavior only into modules that explicitly extend the &lsquo;ChainableMethods&rsquo; module instead of automagically enabling it at the BasicObject level as many would initially think, because we don&rsquo;t want a global &lsquo;method_missing&rsquo; dangling around unchecked.</p>
<p>This behavior makes use of &lsquo;method_missing&rsquo; so it&rsquo;s not going to be fast in a synthetic benchmark against a direct method call, for obvious reasons. The purpose is not to be fast, just expressive. So keep that in mind.</p>
<p>The use case is: whenever you have some kind of transformation, you will want a chain of unit testable, isolated functions, and this is how you can get it without too much hassle.</p>
<p>This is an experiment. Because I&rsquo;m using &lsquo;method_missing&rsquo; there may be side-effects I am not seeing right now, so please let me know in the <a href="https://github.com/akitaonrails/chainable_methods/issues"target="_blank" rel="noopener">Github Issues</a> and send feedback if it helped you out in some project.</p>
<p>Pull Requests are most welcome!</p>
]]></content:encoded><category>ruby</category><category>elixir-and-erlang</category><category>programming-languages</category></item><item><title>Erlang's Ping Pong (tut15) in Clojure and Elixir</title><link>https://akitaonrails.github.io/en/2015/12/09/erlang-s-ping-pong-tut15-in-clojure-and-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/09/erlang-s-ping-pong-tut15-in-clojure-and-elixir/</guid><pubDate>Wed, 09 Dec 2015 14:42:00 -0200</pubDate><description>&lt;p&gt;This is a very short post just because I thought it was fun. I was reading &lt;a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener"&gt;this very enlightening article&lt;/a&gt; on Clojure&amp;rsquo;s Quasar/Pulsar compared to Erlang and how they are trying to plug the holes on the JVM shortcomings.&lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re learning Erlang through its official documentation, the first thing you build in the &lt;a href="http://www.erlang.org/doc/getting_started/conc_prog.html#id67347"target="_blank" rel="noopener"&gt;chapters on Processes&lt;/a&gt; is a very simple Ping Pong code that looks like this:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tut15&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;export&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pong&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ping finished&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;()},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;receive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pong&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Ping received pong&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;receive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;finished&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Pong finished&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Ping_PID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Pong received ping&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Ping_PID&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tut15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tut15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Pong_PID&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;rsquo;s not pretty, it&amp;rsquo;s Prolog-ish. The Clojure article claims how close they got with lightweight threads (true green threads) and this is the same exercise done in Clojure:&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is a very short post just because I thought it was fun. I was reading <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">this very enlightening article</a> on Clojure&rsquo;s Quasar/Pulsar compared to Erlang and how they are trying to plug the holes on the JVM shortcomings.</p>
<p>When you&rsquo;re learning Erlang through its official documentation, the first thing you build in the <a href="http://www.erlang.org/doc/getting_started/conc_prog.html#id67347"target="_blank" rel="noopener">chapters on Processes</a> is a very simple Ping Pong code that looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">module</span><span class="p">(</span><span class="n">tut15</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span><span class="ni">export</span><span class="p">([</span><span class="n">start</span><span class="o">/</span><span class="mi">0</span><span class="p">,</span> <span class="n">ping</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="n">pong</span><span class="o">/</span><span class="mi">0</span><span class="p">]).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">ping</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nv">Pong_PID</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Pong_PID</span> <span class="o">!</span> <span class="n">finished</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;ping finished</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">ping</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span> <span class="nv">Pong_PID</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Pong_PID</span> <span class="o">!</span> <span class="p">{</span><span class="n">ping</span><span class="p">,</span> <span class="n">self</span><span class="p">()},</span>
</span></span><span class="line"><span class="cl">    <span class="k">receive</span>
</span></span><span class="line"><span class="cl">        <span class="n">pong</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Ping received pong</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[])</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ping</span><span class="p">(</span><span class="nv">N</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Pong_PID</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">pong</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">receive</span>
</span></span><span class="line"><span class="cl">        <span class="n">finished</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Pong finished</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="n">ping</span><span class="p">,</span> <span class="nv">Ping_PID</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;Pong received ping</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">            <span class="nv">Ping_PID</span> <span class="o">!</span> <span class="n">pong</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">pong</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">start</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Pong_PID</span> <span class="o">=</span> <span class="nb">spawn</span><span class="p">(</span><span class="n">tut15</span><span class="p">,</span> <span class="n">pong</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">    <span class="nb">spawn</span><span class="p">(</span><span class="n">tut15</span><span class="p">,</span> <span class="n">ping</span><span class="p">,</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="nv">Pong_PID</span><span class="p">]).</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s not pretty, it&rsquo;s Prolog-ish. The Clojure article claims how close they got with lightweight threads (true green threads) and this is the same exercise done in Clojure:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lisp" data-lang="lisp"><span class="line"><span class="cl"><span class="p">(</span><span class="nv">defsfn</span> <span class="nv">pong</span> <span class="nv">[]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nv">==</span> <span class="nv">n</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nb">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">!</span> <span class="ss">:pong</span> <span class="ss">:finished</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">println</span> <span class="s">&#34;ping finished&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nb">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">!</span> <span class="ss">:pong</span> <span class="nv">[:ping</span> <span class="nv">@self]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">receive</span>
</span></span><span class="line"><span class="cl">       <span class="ss">:pong</span> <span class="p">(</span><span class="nv">println</span> <span class="s">&#34;Ping received pong&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">recur</span> <span class="p">(</span><span class="nv">dec</span> <span class="nv">n</span><span class="p">)))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nv">defsfn</span> <span class="nv">pong</span> <span class="nv">[]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">receive</span>
</span></span><span class="line"><span class="cl">   <span class="ss">:finished</span> <span class="p">(</span><span class="nv">println</span> <span class="s">&#34;Pong finished&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="nv">[:ping</span> <span class="nv">ping]</span> <span class="p">(</span><span class="nb">do</span>
</span></span><span class="line"><span class="cl">                  <span class="p">(</span><span class="nv">println</span> <span class="s">&#34;Pong received ping&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                  <span class="p">(</span><span class="nv">!</span> <span class="nv">ping</span> <span class="ss">:pong</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                  <span class="p">(</span><span class="nv">recur</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nv">defn</span> <span class="nv">-main</span> <span class="nv">[]</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">register</span> <span class="ss">:pong</span> <span class="p">(</span><span class="nv">spawn</span> <span class="nv">pong</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">spawn</span> <span class="nv">ping</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:ok</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>People that like the Lisp-y aesthetics of programming directly in the AST representation through the structure of parenthesis based blocks will find this very pretty.</p>
<p>I personally spent years looking into code like this (Common Lisp, Elisp, Scheme, etc) and I still can&rsquo;t get used to it. Once you have a competent editor such as Emacs, that can deal with the proper parenthesis handling, it&rsquo;s easier, yes, but I still can&rsquo;t find the joy in doing this kind of hackish syntax.</p>
<p>Elixir is much more than a new syntax on top of Erlang, as the great book <a href="https://pragprog.com/book/cmelixir/metaprogramming-elixir"target="_blank" rel="noopener">Metaprogramming Elixir</a> will teach you. It opens up the entire Erlang BEAM AST through the usage of the <a href="http://elixir-lang.org/getting-started/meta/quote-and-unquote.html"target="_blank" rel="noopener">quote/unquote</a> mechanics, making programming directly into the AST through <a href="http://elixir-lang.org/getting-started/meta/macros.html"target="_blank" rel="noopener">&ldquo;Hygienic Macros&rdquo;</a> a breeze. It&rsquo;s really the best of both worlds of having a modern, good looking, joyful syntax and the same power a Lisp-y language gives you in terms of well behaved macros.</p>
<p>Now, this is the same example as above, in Elixir:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPingPongTut15</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">ping</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">pong_pid</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nb">send</span> <span class="n">pong_pid</span><span class="p">,</span> <span class="ss">:finished</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="s2">&#34;ping finished&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">ping</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">pong_pid</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nb">send</span> <span class="n">pong_pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:ping</span><span class="p">,</span> <span class="nb">self</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">receive</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:pong</span> <span class="o">-&gt;</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="s2">&#34;Ping received pong&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">ping</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">pong_pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">pong</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">receive</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:finished</span> <span class="o">-&gt;</span> <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="s2">&#34;Pong finished&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:ping</span><span class="p">,</span> <span class="n">ping_pid</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">IO</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="s2">&#34;Pong received ping&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">send</span> <span class="n">ping_pid</span><span class="p">,</span> <span class="ss">:pong</span>
</span></span><span class="line"><span class="cl">        <span class="n">pong</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">pong_pid</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="n">pong</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">spawn</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="n">ping</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">pong_pid</span><span class="p">)</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Because of the power of pattern matching in the function argument signatures, you can define 2 separated functions, avoiding the &ldquo;if&rdquo; as in the Clojure example. Of course, because it&rsquo;s Clojure and it has a complete macro system, and because its <a href="https://github.com/clojure/core.match"target="_blank" rel="noopener">core.match</a> has the proper pattern matching mechanisms, you can emulate the same thing through external libraries such as <a href="https://github.com/killme2008/defun"target="_blank" rel="noopener">defun</a> though.</p>
<p>This was just a simple exercise, I hope it shed some light on the basic similarities between these 3 languages. And as &ldquo;ugly&rdquo; as Erlang may feel, I still feel more comfortable with its quirks than nested parenthesis.</p>
]]></content:encoded><category>programming-languages</category><category>elixir-and-erlang</category><category>learning</category></item><item><title>[Off-Topic] Talking with a University Professor</title><link>https://akitaonrails.github.io/en/2014/12/08/off-topic-conversando-com-um-professor-universitario/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2014/12/08/off-topic-conversando-com-um-professor-universitario/</guid><pubDate>Mon, 08 Dec 2014 19:56:00 -0200</pubDate><description>&lt;p&gt;A few days ago, professor Rosenclever Gazono, from the University Center of Volta Redonda, sent me an email asking for some opinions that I thought were worth sharing. I&amp;rsquo;ve already met many professors during my visits to several universities around the country, and many have the same questions, so I hope this helps a little.&lt;/p&gt;
&lt;p&gt;Below, I&amp;rsquo;ll transcribe the professor&amp;rsquo;s questions and my answers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Professor:&lt;/strong&gt; My name is Rosenclever, I&amp;rsquo;m a university professor currently teaching Object-Oriented Analysis and Design (basically UML, Design Patterns, and Agile methodologies) and Object-Oriented Programming 2 (Java for Web with JPA and JSF)&lt;/p&gt;</description><content:encoded><![CDATA[<p>A few days ago, professor Rosenclever Gazono, from the University Center of Volta Redonda, sent me an email asking for some opinions that I thought were worth sharing. I&rsquo;ve already met many professors during my visits to several universities around the country, and many have the same questions, so I hope this helps a little.</p>
<p>Below, I&rsquo;ll transcribe the professor&rsquo;s questions and my answers.</p>
<p><strong>Professor:</strong> My name is Rosenclever, I&rsquo;m a university professor currently teaching Object-Oriented Analysis and Design (basically UML, Design Patterns, and Agile methodologies) and Object-Oriented Programming 2 (Java for Web with JPA and JSF)</p>
<p>Well, since you&rsquo;re a developer who today also plays the role of an entrepreneur, and I, on the other hand, am in academia trying to prepare professionals to work in the market, I always try to update my content with market demand, and it&rsquo;s very common to hear from IT managers, through the media, that there&rsquo;s a very big gap between what&rsquo;s taught in academia and what the market needs&hellip;</p>
<p>So I&rsquo;d like to ask for your contribution so that I can help my students leave better prepared for the market. I&rsquo;d like to know your opinion on the syllabuses of the courses I mentioned at the beginning of this email&hellip;</p>
<p><strong>Professor:</strong> 1) Does it make sense today to master UML diagrams?</p>
<p><strong>AkitaOnRails:</strong> &ldquo;Master&rdquo; perhaps not (in the sense of knowing every little detail of every diagram). But knowing they exist, having a general notion of each, and knowing the most useful ones like Use Cases, Sequence Diagram, State Diagram, Class Diagram, I think is appropriate and useful. And never as a way to design ALL the software, but a few parts that maybe become clearer if you diagram first.</p>
<p>Adding here in the post: plenty of people who never programmed professionally think that &ldquo;any and every&rdquo; type of diagramming and planning is unnecessary. They still imagine (without knowing it) that the so-called &ldquo;corporate&rdquo; world runs on mountains of diagrams and plans. That&rsquo;s almost never true.</p>
<p>Of course there are exceptions, but in the common case a programmer needs to communicate an idea through something beyond code. That matters most when explaining a complex architecture to the team before starting to code.</p>
<p>It doesn&rsquo;t mean diagramming 100% of the classes, 100% of the states, 100% of the sequences. It means diagramming what&rsquo;s most critical and hardest to understand, and letting the rest emerge naturally during programming.</p>
<p><strong>Professor:</strong> 2) Which Design Patterns and Agile concepts do you consider fundamental to be taught in academia? Or is that also not considered important?</p>
<p><strong>AkitaOnRails:</strong> Design Patterns are important, but as a reference. They&rsquo;re possible solutions, and they need to be evaluated case by case. In Agile, only worry about Extreme Programming, because all XP techniques matter. Scrum and Kanban can be introduced, but they&rsquo;re dispensable. About patterns:</p>
<ul>
<li><a href="/en/2013/05/10/a-lingua-portuguesa-brasileira-e-pessima-standard-vs-pattern">Brazilian Portuguese Can Confuse Us: Standard vs Pattern</a></li>
<li><a href="http://www.akitaonrails.com/2007/07/30/gof-design-patterns-sobreviveu-ao-teste-do-tempo"target="_blank" rel="noopener">GoF Design Patterns - Did it survive the test of time?</a></li>
<li><a href="http://www.akitaonrails.com/2006/10/30/design-patterns-representam-defeitos-nas-linguagens"target="_blank" rel="noopener">Design Patterns represent defects in Languages</a></li>
</ul>
<p>Adding here in the post: the important thing is to teach that nobody needs to invent everything from scratch all the time. That would be a redundant effort, since someone has probably already solved the problem before.</p>
<p>At the same time, it&rsquo;s worth explaining that what we know today is just what seems to work best, and none of it is final. If someone has a result that beats what we know, great, show it to everyone.</p>
<p>It became fashionable to talk about &ldquo;Inversion of Control,&rdquo; and few people know what it really means. They only know it seems to improve &ldquo;modularization,&rdquo; also without understanding why modularizing is a benefit in some cases and not so much in others.</p>
<p>And speaking of Agile, I recommend reading these posts of mine:</p>
<ul>
<li><a href="/en/2014/03/27/off-topic-lean-esta-morto-longa-vida-a-eficiencia">Lean is Dead, Long Live Efficiency</a></li>
<li><a href="/en/2014/09/28/off-topic-agile-a-verdade-por-tras-do-metodo">Agile: the Truth Behind the Method</a></li>
</ul>
<p><strong>Professor:</strong> 3) Does the OO Analysis and Design discipline still make sense today? What would be interesting to be addressed in it?</p>
<p><strong>AkitaOnRails:</strong> Of course, but today you have to emphasize that OO (object orientation) is one approach among several, and not always the &ldquo;best.&rdquo;</p>
<p>By the way, OO only gets interesting in academia through the eyes of languages that really explore OO, like Smalltalk and Ruby. It&rsquo;s worth showing the whole landscape: class-oriented programming in Java and C#, functional in Lisp and Scheme, prototype-oriented in JavaScript and Io, and programming with concurrency and actors in Go, Scala, and Elixir/Erlang.</p>
<p>Adding to the post: this is the kind of subject with no &ldquo;right&rdquo; answer. Any attempt to measure the strength of one side or the other always turns into a flamewar or a mediocre bikeshedding.</p>
<p>The reality is that software factories will keep using languages with good commercial tooling support, like IBM Websphere and Microsoft Visual Studio.NET. Tech startups and smaller, more &ldquo;best of breed&rdquo; technology-oriented companies will prefer open source solutions that sometimes look like a &ldquo;patchwork quilt.&rdquo;</p>
<p>Agencies and small producers will keep using whatever delivers in the shortest possible time, even if it&rsquo;s dirty. Advertising campaigns last very little, so you&rsquo;re left with the &ldquo;quick and dirty,&rdquo; the WordPress derivatives, and so on.</p>
<p><strong>Professor:</strong> 4) Is it definitely still worth teaching Java in academia? Do you think Ruby and Rails or even Python and Django are more appropriate?</p>
<p><strong>AkitaOnRails:</strong> Yes, it&rsquo;s worth it, as long as Java comes in as what it really is: the commercially most viable language. It isn&rsquo;t the only solution to everything nor the best example of an OO language.</p>
<p>Ruby and Python can be explored as dynamically typed languages, taking the chance to explain the difference between static and dynamic typing. But in academia, especially in Computer Science, I&rsquo;ve always been in favor of teaching dead languages like Smalltalk, Lisp, and Eiffel. That keeps the student from falling into the temptation of staying only with the language they learned in college. A bachelor&rsquo;s degree should prioritize foundation, and commercial use is the job of technical and technologist schools.</p>
<p>Adding to the post: Academia, especially in Computer Science chairs, must emphasize science. Going 100% toward the market creates a generation that becomes obsolete very quickly and, worse, that never learns to update itself on its own.</p>
<p>In a hypothetical scenario, if 100% of universities turned 100% toward what the &ldquo;market&rdquo; wants, in ten years we&rsquo;d have our entire computing field junked. Universities need to elevate the &ldquo;Science&rdquo; of Computer Science.</p>
<p>Let me leave posts I wrote about university and career topics:</p>
<ul>
<li><a href="/en/2009/04/17/off-topic-devo-fazer-faculdade">Should I go to college?</a></li>
<li><a href="/en/2014/05/02/off-topic-carreira-em-programacao-codificar-nao-e-programar">Career in Programming - Coding isn&rsquo;t Programming</a></li>
<li><a href="/en/2013/10/31/traducao-carta-para-um-jovem-programador-considerando-uma-startup">Letter to a Young Programmer Considering a Startup</a></li>
</ul>
<p><strong>Professor:</strong> Sorry for the long email, but unfortunately there was no opportunity for us to talk at the event&hellip;</p>
<p><strong>AkitaOnRails:</strong> Not at all, if the subject is relevant, it&rsquo;s worth discussing it. And I encourage everyone who attends events I&rsquo;m at to call me if they want to discuss ways we can help improve education. This subject will never be irrelevant.</p>
]]></content:encoded><category>learning</category><category>programming-languages</category><category>career</category><category>off-topic</category></item><item><title>[Off-Topic] The Humble Programmer, by Edsger W. Dijkstra</title><link>https://akitaonrails.github.io/en/2010/04/12/off-topic-o-programador-humilde-por-edsger-w-dijkstra/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2010/04/12/off-topic-o-programador-humilde-por-edsger-w-dijkstra/</guid><pubDate>Mon, 12 Apr 2010 08:49:00 -0300</pubDate><description>&lt;p&gt;&lt;a href="https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html"target="_blank" rel="noopener"&gt;&lt;img src="http://s3.amazonaws.com/akitaonrails/assets/2010/4/12/450px-Edsger_Wybe_Dijkstra_original.jpg" alt="" loading="lazy" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After writing my article on Info, &lt;a href="http://web.archive.org/web/20130723194737/http://info.abril.com.br:80/noticias/rede/gestao20/software/fabrica-de-software-e-uma-besteira/"target="_blank" rel="noopener"&gt;Software Factory Is Nonsense&lt;/a&gt;, I got a retweet with a very good link to a text I didn&amp;rsquo;t know: &lt;a href="https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html"target="_blank" rel="noopener"&gt;The Humble Programmer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The author is the great &lt;a href="https://en.wikipedia.org/wiki/Edsger_W._Dijkstra"target="_blank" rel="noopener"&gt;Edsger W. Dijkstra&lt;/a&gt;, best known for the seminal paper &lt;a href="https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD215.html"target="_blank" rel="noopener"&gt;A Case against the GO TO Statement&lt;/a&gt;. The Humble Programmer is the speech he gave when he received the 1972 Turing Award.&lt;/p&gt;
&lt;p&gt;The text is fantastic and worth reading in full, but I pulled a few excerpts to comment on. The most interesting thing is to read it with the late 60s in mind and see how much of what he hoped for the future is something we, almost four decades later, are still waiting for. I didn&amp;rsquo;t publish this one on Info for two reasons: first, it&amp;rsquo;s more aimed at programmers, and second, it&amp;rsquo;s one of my &amp;ldquo;Akita-sized&amp;rdquo; texts :-)&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2010/4/12/450px-Edsger_Wybe_Dijkstra_original.jpg" alt=""  loading="lazy" /></a></p>
<p>After writing my article on Info, <a href="http://web.archive.org/web/20130723194737/http://info.abril.com.br:80/noticias/rede/gestao20/software/fabrica-de-software-e-uma-besteira/"target="_blank" rel="noopener">Software Factory Is Nonsense</a>, I got a retweet with a very good link to a text I didn&rsquo;t know: <a href="https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html"target="_blank" rel="noopener">The Humble Programmer</a>.</p>
<p>The author is the great <a href="https://en.wikipedia.org/wiki/Edsger_W._Dijkstra"target="_blank" rel="noopener">Edsger W. Dijkstra</a>, best known for the seminal paper <a href="https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD215.html"target="_blank" rel="noopener">A Case against the GO TO Statement</a>. The Humble Programmer is the speech he gave when he received the 1972 Turing Award.</p>
<p>The text is fantastic and worth reading in full, but I pulled a few excerpts to comment on. The most interesting thing is to read it with the late 60s in mind and see how much of what he hoped for the future is something we, almost four decades later, are still waiting for. I didn&rsquo;t publish this one on Info for two reasons: first, it&rsquo;s more aimed at programmers, and second, it&rsquo;s one of my &ldquo;Akita-sized&rdquo; texts :-)</p>
<blockquote>
  <p>Two opinions about programming date from those days. I mention them now, I shall return to them later. The one opinion was that a really competent programmer should be puzzle-minded and very fond of clever tricks; the other opinion was that programming was nothing more than optimizing the efficiency of the computational process, in one direction or the other.</p>

</blockquote>
<p>Unfortunately, the image of the programmer has changed, but toward two extremes. On one side we have the super-programmers, the renowned authors. On the other, the profession itself became a commodity, an abundant and cheap consumer good, precisely because of what I discussed in the piece about software factories.</p>
<p>This artificial cheapening of the profession leads to decay and to a longer wait for research and progress in the field. The competition to lower the price almost always comes from globalization: shipping the task to regions where labor is cheaper, like India and China. Raising the technical quality of processes and technologies falls to second place.</p>
<p>That doesn&rsquo;t mean everyone in the profession is a line worker. Those who grew on their own and evolved hold respectable positions and tasks, but that owes more to individual effort.</p>
<blockquote>
  <p>The latter opinion was the result of the frequent circumstance that, indeed, the available equipment was a painfully pinching shoe, and in those days one often encountered the naive expectation that, once more powerful machines were available, programming would no longer be a problem, for then the struggle to push the machine to its limits would no longer be necessary and that was all what programming was about, wasn&rsquo;t it? But in the next decades something completely different happened: more powerful machines became available, not just an order of magnitude more powerful, even several orders of magnitude more powerful. But instead of finding ourselves in the state of eternal bliss of all programming problems solved, we found ourselves up to our necks in the <strong>software crisis</strong>! How come?</p>

</blockquote>
<p>At least today no one assumes programming will get simpler just because machines get better. But notice something I always repeat: plenty of people think software problems are recent, when the term &ldquo;software crisis&rdquo; was coined back in the 70s. And to this day it hasn&rsquo;t been solved.</p>
<blockquote>
  <p>The vision is that, well before the seventies have run to completion, we shall be able to design and implement the kind of systems that are now straining our programming ability, at the expense of only a few percent in man-years of what they cost us now, and that besides that, these systems will be <strong>virtually free of bugs</strong>. These two improvements go hand in hand. In the latter respect software seems to be different from many other products, where as a rule a higher quality implies a higher price. Those who want really reliable software will discover that they must find means of avoiding the majority of bugs to start with, and as a result the programming process will become cheaper. If you want more effective programmers, you will discover that they should not waste their time debugging, they should not introduce the bugs to start with. In other words: both goals point to the same change.</p>

</blockquote>
<p><em>&ldquo;More effective programmers &hellip; should not waste their time debugging&rdquo;</em>. That conclusion is almost four decades old. And yet today&rsquo;s programmers, trained in colleges to serve the &ldquo;factory&rdquo; and become tool workers, throw hysterical fits when their editor doesn&rsquo;t ship specific debugging features.</p>
<p><em>&ldquo;Those who want really reliable software will discover that they must find means of avoiding the majority of bugs.&rdquo;</em> That is, efficiency lives in stopping the bug from ever entering the code. Chasing it down afterward, as fast as possible, is the wrong game.</p>
<p>I know it sounds pedantic, but we have techniques for this, and most programmers don&rsquo;t use them. They aren&rsquo;t even taught in college. Look up <a href="http://web.archive.org/web/20100314121601/http://www.improveit.com.br/xp/livroxp"target="_blank" rel="noopener">Extreme Programming</a>.</p>
<p>To keep bugs from slipping in through carelessness (avoiding most of them, since zeroing out 100% is impossible), there are techniques like <a href="http://www.extremeprogramming.org/rules/testfirst.html"target="_blank" rel="noopener">Test First</a>, <a href="http://www.extremeprogramming.org/rules/pair.html"target="_blank" rel="noopener">Pair Programming</a>, <a href="http://www.extremeprogramming.org/rules/dedicated.html"target="_blank" rel="noopener">Continuous Integration</a>, <a href="http://www.extremeprogramming.org/rules/bugs.html"target="_blank" rel="noopener">Tests to Avoid Regression Bugs</a>, and <a href="http://www.extremeprogramming.org/rules/functionaltests.html"target="_blank" rel="noopener">Acceptance Tests</a>. Simple, efficient methods, and a good chunk of the market doesn&rsquo;t even know they exist.</p>
<blockquote>
  <p>Now for the economic need. Nowadays one often encounters the opinion that in the sixties programming has been an overpaid profession, and that in the coming years programmer salaries may be expected to go down. Usually this opinion is expressed in connection with the recession, but it could be a symptom of something different and quite healthy, viz. that perhaps the programmers of the past decade have not done so good a job as they should have done. Society is getting dissatisfied with the performance of programmers and of their products. But there is another factor of much greater weight. In the present situation it is quite usual that for a specific system, the price to be paid for the development of the software is of the same order of magnitude as the price of the hardware needed, and society more or less accepts that. But hardware manufacturers tell us that in the next decade hardware prices can be expected to drop with a factor of ten. If software development were to continue to be the same clumsy and expensive process as it is now, things would get completely out of balance. You cannot expect society to accept this, and therefore we must learn to program an order of magnitude more effectively. To put it in another way: as long as machines were the largest item on the budget, the programming profession could get away with its clumsy techniques, but that umbrella will fold rapidly.</p>

</blockquote>
<p>And this has been our challenge for the last few decades: &ldquo;cheapening&rdquo; the task of programming. The problem is you can take that the wrong way. One path is cheapening the labor, and all it takes is grabbing poorer countries that charge less. Another is cheapening the technology.</p>
<p>That second path works up to an inflection point, when we&rsquo;d need a new rung of sophistication and don&rsquo;t have it, because the cheapening itself cut the research and the innovation in the field. The whole argument turns on <em>&ldquo;how not to make more expensive what is already expensive today&rdquo;</em>. One way out is to advance the technology to automate manual tasks.</p>
<p>To this day there are still &ldquo;programmers&rdquo; who waste time opening the same windows and clicking the same &ldquo;next, next, next&rdquo; buttons every time they need to package a new version of the software. That&rsquo;s automatable, but since the guy was trained only to follow procedures, most don&rsquo;t think they&rsquo;re capable of turning that into a script. It&rsquo;s the case where the &ldquo;cheapening&rdquo; of labor and of training blocks the cheapening of processes and the advance of technology. It was a problem in the 70s and it still is in the 21st century.</p>
<blockquote>
  <p>Argument three is based on the constructive approach to the problem of program correctness. Today a usual technique is to make a program and then to test it. But: program testing can be a very effective way to show the presence of bugs, but is hopelessly inadequate for showing their absence. The only effective way to raise the confidence level of a program significantly is to give a convincing proof of its correctness. But one should not first make the program and then prove its correctness, because then the requirement of providing the proof would only increase the poor programmer&rsquo;s burden. On the contrary: the programmer should let correctness proof and program grow hand in hand. Argument three is essentially based on the following observation. If one first asks oneself what the structure of a convincing proof would be and, having found this, then constructs a program satisfying this proof&rsquo;s requirements, then these correctness concerns turn out to be a very effective heuristic guidance.</p>

</blockquote>
<p>I have to admit that here Dijkstra is talking about formal mathematical proofs. But I&rsquo;d like to stretch the concept. What he says is that the right way would be to first create a &ldquo;proof&rdquo; and only then implement the code that meets that &ldquo;proof&rsquo;s&rdquo; requirements.</p>
<p>I&rsquo;d venture that Dijkstra was practically a pioneer of the &ldquo;Test First&rdquo; idea in Extreme Programming, also known as TDD, or &ldquo;Test Driven Development.&rdquo; The cycle is simple: 1) write a test that describes the requirement; 2) run the test and watch it fail, since the code doesn&rsquo;t exist yet; 3) implement the minimum to make the test pass; 4) move on to the next requirement. Dijkstra already knew, almost four decades ago, that this order of development is what minimizes the volume of bugs down the line, on top of dozens of other benefits we discovered later.</p>
<blockquote>
  <p>Argument four has to do with the way in which the amount of intellectual effort needed to design a program depends on the program length. It has been suggested that there is some kind of law of nature telling us that the amount of intellectual effort needed grows with the square of program length. But, thank goodness, no one has been able to prove this law. And this is because it need not be true. We all know that the only mental tool by means of which a very finite piece of reasoning can cover a myriad cases is called &ldquo;abstraction&rdquo;; as a result the effective exploitation of his powers of abstraction must be regarded as one of the most vital activities of a competent programmer. In this connection it might be worth-while to point out that the purpose of abstracting is not to be vague, but to create a new semantic level in which one can be absolutely precise. (&hellip;) The by-product was the identification of a number of patterns of abstraction that play a vital role in the whole process of composing programs. Enough is now known about these patterns of abstraction that you could devote a lecture to about each of them.</p>

</blockquote>
<p>This point is more complex and has to do with the programmer&rsquo;s capacity for abstraction. The concept itself is &ldquo;abstract&rdquo; and hard to define. The first part is talent: someone with no talent for programming won&rsquo;t become a programmer, period.</p>
<p>Starting from the premise that the spark of talent exists, in come the thousands of hours of practice. And I mean practicing real code, experimenting with the most varied situations possible. Repeating procedures doesn&rsquo;t count.</p>
<p>Only then does intuition emerge from experience, and that&rsquo;s what lets you spot patterns in code, opportunities for refactoring and optimization, higher-level constructs to simplify the program, and so on. The path starts with the programmer following procedures, but it has to evolve quickly into experimentation. This is fundamental.</p>
<p>As a curiosity, part of what he describes is what we also know today as &ldquo;Design Patterns.&rdquo;</p>
<blockquote>
  <p>Now for the fifth argument. It has to do with the influence of the tool we are trying to use upon our own thinking habits. I observe a cultural tradition, which in all probability has its roots in the Renaissance, to ignore this influence, to regard the human mind as the supreme and autonomous master of its artefacts. But if I start to analyse the thinking habits of myself and of my fellow human beings, I come, whether I like it or not, to a completely different conclusion, viz. that the tools we are trying to use and the language or notation we are using to express or record our thoughts, are the major factors determining what we can think or express at all! The analysis of the influence that programming languages have on the thinking habits of its users, and the recognition that, by now, brainpower is by far our scarcest resource, they together give us a new collection of yardsticks for comparing the relative merits of various programming languages. <strong>The competent programmer is fully aware of the strictly limited size of his own skull</strong>; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague. (&hellip;) Another lesson we should have learned from the recent past is that the development of &ldquo;richer&rdquo; or &ldquo;more powerful&rdquo; programming languages was a mistake in the sense that these baroque monstrosities, these conglomerations of idiosyncrasies, are really unmanageable, both mechanically and mentally. I see a great future for very systematic and very modest programming languages.</p>

</blockquote>
<p>A good programmer recognizes his own limitations and looks for the tools that best fit the problems. Most language wars start with arguments like this. The trouble is that insisting on a <em>&ldquo;single language&rdquo;</em> and piling onto it everything everyone wants ends up creating a &ldquo;baroque&rdquo; monster, as Dijkstra puts it.</p>
<p>Programmers are people, and people have limited brains. We need to be able to express ourselves in code, and the more complicated the tool, the more of our mind goes into keeping the pieces in our head and the less is left for writing elegant code.</p>
<p>Look at Dijkstra himself. Up to the 70s he practically watched computers being born and followed almost everything up close, from machine language to Fortran, Lisp, and Algol. If back then he could know several languages in depth, I see no excuse today, with all the resources we have, for not knowing an order of magnitude more languages, technologies, and techniques.</p>
<p>He talks about more &ldquo;modest&rdquo; languages, and I&rsquo;d say those are today&rsquo;s dynamic high-level languages, like Ruby or Python. Abstractions that allow even larger systems with less complexity.</p>
<blockquote>
  <p>As an aside I would like to insert a warning to those who identify the difficulty of the programming task with the struggle against the inadequacies of our current tools, because they might conclude that, once our tools will be much more adequate, programming will no longer be a problem. Programming will remain very difficult, because once we have freed ourselves from the circumstantial cumbersomeness, we will find ourselves free to tackle the problems that are now well beyond our programming capacity.</p>

</blockquote>
<p>And since the 70s we know there&rsquo;s <strong>no silver bullet</strong>. No new tool will magically make programming orders of magnitude more efficient. There&rsquo;s no free lunch.</p>
<blockquote>
  <p>It has already taught us a few lessons, and the one I have chosen to stress in this talk is the following. We shall do a much better programming job, provided that we approach the task with a full appreciation of its tremendous difficulty, provided that we stick to modest and elegant programming languages, provided that we respect the intrinsic limitations of the human mind and approach the task as Very Humble Programmers.</p>

</blockquote>
<p>Programmers need to be Humble in the right sense of the word: to own their own limitations and create new techniques, technologies, and ways to do the same work with more quality and more efficiency, breaking rules and traditions and creating new standards. The programmers who only follow what they were taught, just the procedures, are <strong>arrogant</strong>: they think everything worth discovering has already been discovered.</p>
<p>I always <a href="https://blogoscoped.com/archive/2005-08-24-n14.html"target="_blank" rel="noopener">repeat</a> that a good programmer is dumb and lazy. Dumb because if he thinks he&rsquo;s smart he&rsquo;ll also think he already knows everything, and whoever knows everything stops researching. And lazy because an overly hardworking programmer repeats the same procedure every day with great diligence, while the lazy one gets tired of it, automates the work, and still has time left to rest.</p>
<p>I think Dijkstra would agree with that ;-)</p>
]]></content:encoded><category>software-engineering</category><category>programming-languages</category><category>off-topic</category></item><item><title>Off Topic: Never Trust TIOBE</title><link>https://akitaonrails.github.io/en/2008/04/13/off-topic-nunca-confie-no-tiobe/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2008/04/13/off-topic-nunca-confie-no-tiobe/</guid><pubDate>Sun, 13 Apr 2008 14:40:00 -0300</pubDate><description>&lt;p&gt;I have used TIOBE numbers in presentations and in articles on this very blog. I had a vague notion of how they put that index together, but never stopped to think hard about it. Does anyone here know how the TIOBE &amp;ldquo;Language Popularity&amp;rdquo; index is actually formed?&lt;/p&gt;
&lt;p&gt;Today I read &lt;a href="https://blog.timbunce.org/2008/04/12/tiobe-or-not-tiobe-lies-damned-lies-and-statistics/"target="_blank" rel="noopener"&gt;two&lt;/a&gt; &lt;a href="http://web.archive.org/web/20080527051237/http://contentment.org/2008/04/perl-is-not-going-away.html"target="_blank" rel="noopener"&gt;articles&lt;/a&gt; discussing exactly that. Both are right, and if you stop to think for 30 seconds, it was obvious I should never have used those numbers. Me, of all people, who has spoken more than once about the dangers of misused statistics! Let&amp;rsquo;s understand why.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I have used TIOBE numbers in presentations and in articles on this very blog. I had a vague notion of how they put that index together, but never stopped to think hard about it. Does anyone here know how the TIOBE &ldquo;Language Popularity&rdquo; index is actually formed?</p>
<p>Today I read <a href="https://blog.timbunce.org/2008/04/12/tiobe-or-not-tiobe-lies-damned-lies-and-statistics/"target="_blank" rel="noopener">two</a> <a href="http://web.archive.org/web/20080527051237/http://contentment.org/2008/04/perl-is-not-going-away.html"target="_blank" rel="noopener">articles</a> discussing exactly that. Both are right, and if you stop to think for 30 seconds, it was obvious I should never have used those numbers. Me, of all people, who has spoken more than once about the dangers of misused statistics! Let&rsquo;s understand why.</p>
<p>So, how is the TIOBE index formed?</p>
<p>It&rsquo;s a percentage, with no adjustment whatsoever, of the number of hits on 5 search engines for the terms +&quot;[language] programming&quot;. Let&rsquo;s look at examples with absolute numbers from Google alone:</p>
<ul>
<li>Java: 3.4 million hits</li>
<li>C: 2.2 million hits</li>
<li>C++: 1.7 million hits</li>
<li>PHP: 1.5 million hits</li>
<li>Perl: 0.9 million hits</li>
<li>Python: 584 thousand hits</li>
<li>C#: 558 thousand hits</li>
<li>Ruby: 363 thousand hits</li>
<li>Smalltalk: 28.4 thousand hits</li>
<li>Groovy: 15.7 thousand hits</li>
</ul>
<p>Now let&rsquo;s see Yahoo!:</p>
<ul>
<li>C: 12.3 million hits</li>
<li>Java: 9.6 million hits</li>
<li>C++: 4.9 million hits</li>
<li>PHP: 4.9 million hits</li>
<li>Perl: 2.8 million hits</li>
<li>Python: 2.2 million hits</li>
<li>C#: 1.9 million hits</li>
<li>Ruby: 1.6 million hits</li>
<li>Smalltalk: 103 thousand hits</li>
<li>Groovy: 31.3 thousand hits</li>
</ul>
<p>There are also exception lists for grouping or splitting terms. C#, for example, is also searched as &ldquo;CSharp&rdquo;, &ldquo;C-Sharp&rdquo;, &ldquo;C# 3.0&rdquo;, and the results of all of them are summed as &ldquo;C#&rdquo;. In the example above I simply searched for &ldquo;C# programming&rdquo;.</p>
<p>After that, they take each language&rsquo;s total results and divide by the total of the top 50. In my example, with the 10 in the list, C# comes out at 558 thousand / 11,249,100 = 4.9%. The same is done for Yahoo and the other search engines. The final percentages are summed and divided by 5. Here&rsquo;s what you get in my smaller example, using none of the exceptions and only the 2 major search engines:</p>
<ul>
<li>Java: 27%</li>
<li>C: 25%</li>
<li>C++: 13.6%</li>
<li>PHP: 12.7%</li>
<li>Perl: 7.47%</li>
<li>Python: 5.3%</li>
<li>C#: 4.87%</li>
<li>Ruby: 3.6%</li>
<li>Smalltalk: 0.3%</li>
<li>Groovy: 0.1%</li>
</ul>
<p>There you go: we just saw the easy way to manufacture indices. This methodology is absolutely broken, and you don&rsquo;t need to be a statistics expert to say so. It can&rsquo;t even be compared to a methodology considered &ldquo;serious&rdquo;, like an IBOPE poll, which is controversial anyway.</p>
<h2>The Problems<span class="hx:absolute hx:-mt-20" id="the-problems"></span>
    <a href="#the-problems" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Some of the problems that I, a statistics layman, can point out:</p>
<ul>
<li>
<p>Blind trust in third parties: the search engines are presumed to do a good job.</p>
</li>
<li>
<p>Duplicates count. The same article that gains notoriety gets re-published on hundreds of blogs (many of us bloggers are notorious for being mere echoes of primary sources). What should count as 1 suddenly becomes 100. Google App Engine, just launched, must be inflating Python&rsquo;s index, for example.</p>
</li>
<li>
<p>All 5 search engines weigh the same, because the index is just the simple average across all of them. There is no adjustment factor. And Yahoo!, in my experience, does a terrible job at search: &ldquo;C programming&rdquo; returned 2.2 million hits on Google and 12.3 million on Yahoo!. I doubt Yahoo&rsquo;s database is 10 times larger; most likely it&rsquo;s 10 times worse at filtering out noise. I wouldn&rsquo;t be surprised if it counted &ldquo;Objective C programming&rdquo; as a hit for &ldquo;C programming&rdquo;, something at the level of the basic <code>select * from table where text like &quot;C programming&quot;</code>.</p>
</li>
<li>
<p>The hit count depends on the exact search string. &ldquo;Ruby programming&rdquo; brought a mere 363 thousand hits on Google. But &ldquo;Ruby Rails&rdquo; brings <strong>8.1 million</strong> hits (!). I browsed all the way to page 63 and every link was genuinely about Ruby and Rails (no gemstones, no train tracks, so very little noise). With the same string, Yahoo returned 3.9 million, well above the previous 1.6 million. It becomes clear how hard it is to arrive at a reliable number for each platform.</p>
</li>
</ul>
<p>The complaint from both authors of the posts I linked is this: even with the results we saw, why did Python become last year&rsquo;s language of the year? Why did Ruby become the language of the year before that? By this method it&rsquo;s clear that Java, C, C++, Perl, and PHP are an order of magnitude above.</p>
<p>My assessment: the TIOBE Index assumes too much and lets too many errors slip through. In a measurement situation like this, the margin of error is larger than the number being measured. Drawing any conclusion from it is practically impossible.</p>
<p>Want an equally ridiculous methodology? Let&rsquo;s &ldquo;assume&rdquo; that a person who receives a political candidate&rsquo;s flyer and doesn&rsquo;t throw it on the street likes the candidate. We could elect a president by counting the flyers thrown on the street: whoever has the fewest flyers on the ground is the most liked and, therefore, the winner.</p>
<p>I&rsquo;ve talked about this in another article: <a href="/en/2008/3/1/off-topic-somos-matematicamente-ignorantes">We Are Mathematically Illiterate</a>. Unfortunately, unlike first-year algebra, statistics is still a very poorly understood field. I studied statistics at the Institute of Mathematics and <strong>Statistics</strong> at USP and I still know very little about it. People are constantly forced to &ldquo;swallow&rdquo; the numbers TV shows them, without knowing how to judge, at minimum, whether the criteria are adequate.</p>
<p>My recommendation: when in doubt, ignore the indices. I&rsquo;m no Dijkstra, but I&rsquo;d say <em>&ldquo;Statistics Considered Harmful&rdquo;</em>. As long as people don&rsquo;t understand what statistics are, showing them indices is just trying to lie with more substance. In the article I linked above, Tim quotes Mark Twain:</p>
<blockquote>
  <p>&ldquo;There are three kinds of lies: lies, damned lies, and statistics.&rdquo;</p>

</blockquote>
<p>For my part, I don&rsquo;t intend to mention TIOBE ever again in any article or presentation as an argument that Ruby is growing. In practice, I believe there is no reliable index today to evaluate that.</p>
]]></content:encoded><category>programming-languages</category><category>science</category><category>off-topic</category></item><item><title>Chatting with Evan Phoenix</title><link>https://akitaonrails.github.io/en/2008/02/11/chatting-with-evan-phoenix/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2008/02/11/chatting-with-evan-phoenix/</guid><pubDate>Mon, 11 Feb 2008 22:42:00 -0200</pubDate><description>&lt;p&gt;&lt;img src="http://s3.amazonaws.com/akitaonrails/assets/2008/2/11/524655980_8e68a871a0.jpg" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;It was &lt;a href="https://akitaonrails.github.io/en/2007/12/15/conversando-com-avi-bryant-parte-1"&gt;Avi&lt;/a&gt; &lt;a href="https://akitaonrails.github.io/en/2007/12/22/conversando-com-avi-bryant-parte-2"&gt;Bryant&lt;/a&gt; that evangelized the neat idea of “turtles all the way”, meaning that for a language to be called ‘complete’ it should be able to extend itself. So, the ideal world would have Ruby being extended in Ruby, not in C. JRuby goes as far as it can building up a sandbox for Ruby code to run under the JVM. As cool as it is, we still rely on Java to fully extend it.&lt;/p&gt;</description><content:encoded><![CDATA[<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/2/11/524655980_8e68a871a0.jpg" alt=""  loading="lazy" /></p>
<p>It was <a href="/en/2007/12/15/conversando-com-avi-bryant-parte-1">Avi</a> <a href="/en/2007/12/22/conversando-com-avi-bryant-parte-2">Bryant</a> that evangelized the neat idea of “turtles all the way”, meaning that for a language to be called ‘complete’ it should be able to extend itself. So, the ideal world would have Ruby being extended in Ruby, not in C. JRuby goes as far as it can building up a sandbox for Ruby code to run under the JVM. As cool as it is, we still rely on Java to fully extend it.</p>
<p>Enter <a href="http://rubini.us/"target="_blank" rel="noopener">Rubinius</a> and its author <strong>Evan Phoenix</strong> , currently a full-time employee for <a href="http://www.engineyard.com/"target="_blank" rel="noopener">EngineYard</a>. Rubinius borrows heavily from Smalltalk’s concepts of a virtual machine and does as little as possible in C just for the bootstrap and all the rest is developed over pure Ruby.</p>
<p>Rubinius answers lots of questions about going forward over the current Ruby MRI but also raises several other questions that I hope we can nail down today in this interview with Evan himself.</p>
<p>So let’s get started.</p>
<p><strong>AkitaOnRails:</strong> It is a tradition now in my interviews to ask about the background of the programmer. What was your path? I mean, what made you come in to the programming world and how did you get into Ruby?</p>
<p><strong>Evan Phoenix:</strong> I started programming in high-school with a friend of mine. We started a small computer company doing odds and ends and I really got into it. I had gotten into Linux a few years before that, so programming became a natural extension of using Linux.</p>
<p>When I applied to college, I declared myself as a Computer Science major on the application, so I’ve been doing this since about then I guess.</p>
<p>As for Ruby, I got into it in about 2002, when I first moved to Seattle. A friend had just found it and was writing some fun stuff in it and I picked it up on his suggestion. Eventually, I found the seattle.rb, and have been working in Ruby since.</p>
<p><a href="http://www.engineyard.com/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/2/11/ey_logo.png" alt=""  loading="lazy" /></a></p>
<p><strong>AkitaOnRails:</strong> EngineYard seems to have great faith in the future of Rubinius. How did this relationship start? Can you tell us what are EngineYard’s expectations regarding your work for them?</p>
<p><strong>Evan Phoenix:</strong> The relationship started mainly with them approaching me about working for them, on Rubinius. So they saw promise in Rubinius on their own, from my presentations and such, and approached me.</p>
<p>Their expectations are that I make Rubinius the best Ruby VM it can be. That’s my basic mandate with them. They’re pretty much the ideal stewards of the project. They allow me to work at the pace I want, they understand the rigors of the work I’m doing, and give me a lot of freedom.</p>
<p><strong>AkitaOnRails:</strong> Now let’s get into Rubinius. For non-starters how would you define Rubinius? We all know that this is an alternative Ruby implementation, so maybe it would be good to point out what makes it different than the MRI. What are the project main goals?</p>
<p><strong>Evan Phoenix:</strong> One of the primary goals is to have as much of the system written in Ruby itself. With MRI, none of the core classes are implemented in Ruby, all are in C. So we wanted a system that was easier to work with, and thus written in Ruby itself.</p>
<p>Another thing we’ve done is enrich the number of things which are objects, which adds considerable power. An easy example is the CompiledMethod class which Rubinius has. It contains the bytecode representation of a method and can be inspected, manipulated, etc, just like any other object. This opens a lot of new doors with regard to how problems can be solved.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/2/11/Picture_1_1.png" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> It’s not so obvious to the brazilian audience when we speak of “Squeak”, “Garnet”. Smalltalk is probably less known here than Ruby. The point is that it is well known that you were inspired by the way Squeak is implemented. This led to features like “Cuby”. Can you explain what is so great about the way Squeak is implemented and how it is helping you in the Rubinius project?</p>
<p><strong>Evan Phoenix:</strong> Well, we’re currently in the process of developing Garnet/Cuby, so those tools aren’t in use really yet. But almost the entire system architecture of Rubinius is modeled off the original Smalltalk-80 Virtual Machine. It defined things like CompiledMethod, etc, which I took name for name into Rubinius.</p>
<p>In addition, the execution model of Rubinius is almost identical to Squeak. A good example is the way in which the system calls methods and figures out how to keep track of where to return control to when a method call returns. All that information is kept in first class objects called MethodContexts. In a language such as C, that information is stored on a process’s memory stack. By keeping the data in first class objects, we’re able to query them directly to find out information about the running system. It also vastly simplifies how the garbage collector works.</p>
<p><strong>AkitaOnRails:</strong> Today you have shotgun. I read somewhere that you consider it ‘cheating’ because this is not an interpreter for Ruby written in Ruby, which will lead to Cuby. It’s more kind of a bootstrapping to ease development. What’s the current state of shotgun, can we use it outside its purpose of developing Rubinius?</p>
<p><strong>Evan Phoenix:</strong> Shotgun is a virtual machine written in C that provides instructions and primitive operations for running a Ruby-like language. Shotgun itself actually has no knowledge of parsing, compiling etc. It has a very simple way to load code in, and a way to execute it. You could easily write a new language which targets shotgun, in fact, <a href="/2007/06/21/chatting-with-ola-bini-jRuby-core-team-member/">Ola Bini</a> and I have talked a few times about writing a simple lisp-style language that would run directly on shotgun.</p>
<p>The primitive operations it provides are kinda like syscalls in a unix system. They provide very low level operations which are built on. For example, there is an add primitive which adds two Fixnum objects together and puts the result on the stack.</p>
<p><strong>AkitaOnRails:</strong> This is a personal curiosity. The MRI today uses a simple mark and sweep garbage collector (GC) whereas Java uses a highly customizable generational GC. What did you choose for memory management on the Rubinius VM?</p>
<p><strong>Evan Phoenix:</strong> Rubinius uses a generational GC, combining a copy collector for the young objects, with a mark/sweep collector for the old. It’s proved to work quite well for us. The trick has been tuning how long an object exists before it is promoted to the old object space. We’ve only tuned it a little. There is a lot more work that could be put into the GC, and it’s architected to only interact with the rest of the system at a few places, so its logic can be completely replaced if need be without disturbing anything else.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/2/11/421875792_79acde1fba.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> We have parties that favors green threads, we have parties that are all go native threads. How are you dealing with threading issues in Rubinius? Will we still have the same kind of global locking that MRI has today?</p>
<p><strong>Evan Phoenix:</strong> At the moment, Rubinius only has green threads, built on the first class MethodContext objects. It currently uses libevent to allow threads to wait on IO objects quickly.</p>
<p>The plan (likely for 2.0) is to support native threads as well as the current green threads. You’ll be able to allocate a pool of native threads that the green threads can all share. So perhaps you have 100 green threads, you could allocate 10 native threads for them to split time between. Because of the architecture, it’s trivial to migrate a green thread between native threads.</p>
<p>As for the global lock on native threads, no, we don’t consider a global lock a solution for using native threads. If there is a global lock, there is no reason to use them over green threads. Because so much of Rubinius is written in Ruby, we have an easier time of figuring out how to properly lock things to keep all the objects in the system safe. We haven’t really even started this work, but it’s been talked about quite a bit.</p>
<p><strong>AkitaOnRails:</strong> The current goal is probably to make Rubinius as compatible with the MRI 1.8 as possible. Did you already start any work toward YARV compatibility already? And speaking of YARV, it is the official virtual machine and bytecode compiler for the next release of Ruby. Is there any chance to make Rubinius and YARV byte-code compliant or this is not a short-term goal?</p>
<p><strong>Evan Phoenix:</strong> We’re actively working toward being 1.8.6 compliant. This a big goal because there is a lot of functionality in there. We’re going to do a 1.0 release of Rubinius once it’s able to run all the specs that 1.8.6 runs and properly runs Rails 1.2, which is a good benchmark.</p>
<p>No, we haven’t done any work yet toward YARV compatibility, but because of how things are architected, we’ll likely just be able to have —yarv flag that you pass to Rubinius to have it use YARV compatible versions of things.</p>
<p>We aren’t currently working toward a common bytecode format between Rubinius and YARV, mainly because there is nothing to be gained by doing that. YARV’s bytecode format is largely internal to YARV only, last I looked, you couldn’t easily save it out to a file. Rubinius operates primarily off .rbc files, which contain a serialized CompiledMethod, and thus contain bytecodes. That being said, they are actually similar and could be unified if we saw the need to.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/2/11/2211693208_bf14710240.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> Performance is always subject for a lot of discussion and it is still premature to talk about performance under Rubinius, as it is still work in progress. But I read in your blog that it is quite fast already, even surpassing MRI in many tests. How is performance evolving?</p>
<p><strong>Evan Phoenix:</strong> Currently, our performance is evolving slowly. This is mainly because it’s not a priority for people writing the kernel of Rubinius (the majority of the Ruby code which makes it up). As we get closer and closer to being compliant, we’ll begin tuning things. That being said, I do write code to make Rubinius faster here and there. We went ahead and implement fast math operations to speed up the simple math that people commonly do. We have a simple profiler which can easily be turned on to give developers feedback about how their code is run.</p>
<p><strong>AkitaOnRails:</strong> One of the main problems for any alternative implementation to Ruby is that it has a lot of C-based extensions, including a handful of core libraries. So if you want to stay “pure-Ruby” you have to re-implement all of them in Ruby. The JRuby guys had to go through something similar. Are there any cross-project collaborations already in this regard?</p>
<p><strong>Evan Phoenix:</strong> Actually, that’s not entirely true. We also saw that the landscape of Ruby includes a lot of C extensions. To make migration to Rubinius faster, we’ve written a layer into Rubinius which allows all those C extensions to run (nearly) unmodified, only requiring a simple recompile. These are so that things such as ImageScience, RMagick, postgres bindings, etc can all be used under Rubinius without having to be rewritten.</p>
<p><strong>AkitaOnRails:</strong> Still about C code, I know it is difficult to deliver a bytecode virtual machine and still be dependent on non-managed C code. Java has some jury rigging toward JNA, .NET has pinvoke. Do you plan something similar for Rubinius so that it can more or less easily speak with C libraries?</p>
<p><strong>Evan Phoenix:</strong> We actually directly support a mechanism called FFI (Foreign Function Interface), which allows a developer to bind a C function directly as a method call. Here is a simple example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">LibC</span>
</span></span><span class="line"><span class="cl"> <span class="n">attach_function</span> <span class="kp">nil</span><span class="p">,</span> <span class="ss">:puts</span><span class="p">,</span> <span class="o">[</span><span class="ss">:string</span><span class="o">]</span><span class="p">,</span> <span class="ss">:void</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">LibC</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;hello!&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The attach_function line is the primary interface into FFI. You simply indicate which library the function is in (in this case, nil is used because it’s included in the existing process), the name of the function (puts), the types of arguments it takes (just 1, a string), and finally, the type it returns (void, ie, nothing).</p>
<p>Using this, you can tie directly to C functions without having to write C wrapper code.</p>
<p><a href="http://rubini.us/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/2/11/Picture_2.png" alt=""  loading="lazy" /></a></p>
<p><strong>AkitaOnRails:</strong> You are very right when you say that formal language specs too early in the game can make it difficult to evolve the language. Ruby as been growing for 10 years now, and it feels that this is the right time to start thinking of something like this, so that we can have a baseline from where every other alternative implementation could start. Do you see any efforts towards this end?</p>
<p><strong>Evan Phoenix:</strong> We’ve been actively working to develop a set of specs (in <a href="http://rspec.info/"target="_blank" rel="noopener">rSpec</a> format) for 1.8. These form the primary mechanism with which we test how Rubinius is doing. These specs are being actively used by JRuby and IronRuby to test their respective Ruby environments. We’re currently in the process of moving these specs into a generic Ruby spec project to try and begin moving things toward a more formal standard.</p>
<p><strong>AkitaOnRails:</strong> Another recurring question is about Rails, of course. Any new Ruby implementation today will have to face the inevitable question: does it run Rails already? :-) Rails is serving kinda like a ‘mini test suite’ and baseline for most implementations. How is Rubinius performing on this matter?</p>
<p><strong>Evan Phoenix:</strong> When we run Rails, we know we’re close to a 1.0. :)</p>
<p><strong>AkitaOnRails:</strong> You have a very interesting approach toward open source management: everyone that sent you a good patch gains commit access to the repository. How is this working for you? Isn’t it a bit ad hoc or people tend to follow your lead and your list of priorities?</p>
<p><strong>Evan Phoenix:</strong> It’s actually working out great so far. By being spec/test oriented, new people have a place to start, either writing missing specs or making existing ones pass. Those become great intros into the whole system for them. They then begin to explore a bit more and commonly find more and more they’d love to help with. Because we have a lot of work to do in a lot of different places, people don’t have to worry about my priority list, since it’s usually involving things which others don’t need to be actively concerned with.</p>
<p>I really can’t say enough about the people who contribute. The one patch barrier has proved to work great because while we’ve now got over 60 committers, not once have I had to pull commit rights from someone. Sure, people mess up, but in every case, the person has corrected the behavior and/or code.</p>
<p><strong>AkitaOnRails:</strong> One byproduct of this commit access approach of yours is the integration of rSpec into the Rubinius project so that you have a nice requirements tool and test suite. Do you practice Behavior Driven Development? Or this is covering just a small sub-set of the project?</p>
<p><strong>Evan Phoenix:</strong> I’m largely a testing newbie, simply following the guidelines set by Brian Ford, the primary spec keeper of the project. It has proved to be a great tool for viewing how the project is progressing.</p>
<p><strong>AkitaOnRails:</strong> As many other Ruby projects, Rubinius is also managed over GIT. There’s been a lot of fuzz around Git and Ruby these days. Do you think there is a new trend here? Why did you decide to go with GIT?</p>
<p><strong>Evan Phoenix:</strong> Yeah, I think that GIT is picking up as a trend, because of the features it offers over subversion mainly.</p>
<p>The big reason we decided was it’s feature set. One big feature I love is local branches, allowing a person to manage a number of outstanding changes without trampling on each other.</p>
<p>We haven’t yet, but we plan to exercise the great merge / public branch capabilities.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/2/11/504590792_2819a866a8.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> We heard from Ezra’s <a href="http://podcast.RubyonRails.org/programs/1/episodes/ezra-zygmuntowicz"target="_blank" rel="noopener">recent interviews</a> about your new experiment on multi-VM. That would be a blessing for deployment without we having to deal with several processes in a mongrel cluster. Can you describe this feature and pin point its advantages?</p>
<p><strong>Evan Phoenix:</strong> It’s pretty experimental currently, but it will only get more stable over time. The idea is that you can spin up a whole new VM in the current process. This new VM is completely separate from other VMs, living in its own native thread and having its own garbage collector, etc.</p>
<p>There is a mechanism for VMs to communicate with each other though, and this allows them to coordinate work. This would mean that a primary thread could accept new connections, then pass them off to a new VM to actually process. This allows you to process connections in actual parallel, in addition, because the VMs are completely separated, this even works for non-thread safe applications like Rails.</p>
<p><strong>AkitaOnRails:</strong> Ezra also mentioned about a possible mod_rubinius in the making. How is it going? This definitely makes the Rubinius stack even stronger. Together with Merb, Rubinius plus multi-VM capabilities plus mod_rubinius would be a killer deployment package.</p>
<p><strong>Evan Phoenix:</strong> Yes, a mod_rubinius project is just beginning. The multi-VM code and mod_rubinius overlap a bunch, so you’ll be able to have VM pools for sites, all managed through apache. We’re hoping that mod_rubinius can really simplify the Rails/Ruby deployment picture. It will operate similarly to mod_python, allowing a VM to remain running between requests, and likely even run background tasks.</p>
<p><strong>AkitaOnRails:</strong> How long do you guess we are from a fully MRI-1.8 compatible release?</p>
<p><strong>Evan Phoenix:</strong> We still don’t have a firm date, mainly because what fully compliant MRI-1.8 is is still unknown. We are still adding specs, working to properly define 1.8. That being said, we’re really hitting a stride and progress is advancing rapidly. We’re very close to properly running Rubygems. Ezra recently was able to run Merb on top of Rubinius, running under webrick.</p>
<p>I’m going to be doing a 0.9 release this week, because we’ve made so much progress since 0.8. My big hope is that by RailsConf in May, we’ll be running Rails. But don’t hold me to that. This is open source after all. :)</p>
<p><strong>AkitaOnRails:</strong> Thanks, I think this is it.</p>
<p><strong>Evan Phoenix:</strong> Thanks for your patience!</p>
]]></content:encoded><category>interviews</category><category>ruby</category><category>programming-languages</category></item><item><title>Chatting with Avi Bryant - Part 2</title><link>https://akitaonrails.github.io/en/2007/12/22/chatting-with-avi-bryant-part-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2007/12/22/chatting-with-avi-bryant-part-2/</guid><pubDate>Sat, 22 Dec 2007 20:36:00 -0200</pubDate><description>&lt;p&gt;If you didn’t read it, take a look at &lt;a href="https://akitaonrails.github.io/en/2007/12/15/conversando-com-avi-bryant-parte-1"&gt;Part 1&lt;/a&gt; where we get to know more about Avi Bryant and his amazing product &lt;a href="http://www.dabbledb.com"target="_blank" rel="noopener"&gt;Dabble DB&lt;/a&gt;. In &lt;strong&gt;Part 2&lt;/strong&gt; Avi goes a little bit more in elaborating his technology opinions and points of view. It’s a very insightful read for every programmer.&lt;/p&gt;
&lt;p&gt;As I always say – and Avi is good at pointing out —, Ruby has its drawbacks – most of them being improved on Ruby 1.9, JRuby and Rubinius. Avi gives us good reasons why Smalltalk is yet another great platform to learn, bringing back decades of evolution and maturity. So, here goes, the unabridged version of the interview.&lt;/p&gt;</description><content:encoded><![CDATA[<p>If you didn’t read it, take a look at <a href="/en/2007/12/15/conversando-com-avi-bryant-parte-1">Part 1</a> where we get to know more about Avi Bryant and his amazing product <a href="http://www.dabbledb.com"target="_blank" rel="noopener">Dabble DB</a>. In <strong>Part 2</strong> Avi goes a little bit more in elaborating his technology opinions and points of view. It’s a very insightful read for every programmer.</p>
<p>As I always say – and Avi is good at pointing out —, Ruby has its drawbacks – most of them being improved on Ruby 1.9, JRuby and Rubinius. Avi gives us good reasons why Smalltalk is yet another great platform to learn, bringing back decades of evolution and maturity. So, here goes, the unabridged version of the interview.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/539949594_f012918cd3.jpg" alt=""  loading="lazy" /></p>
<p>And stay tuned! I hope to have Evan Phoenix, Hal Fulton, Peter Cooper and Adrian Holovaty as my next guests. Lots of material to begin 2008 in great style.</p>
<p><strong>AkitaOnRails:</strong> Some people still try to make the case in favor of Smalltalk as it being the <em>“purest OO language out there”</em>. Even this being a fact, do you think that this alone is compelling enough to make a case against every other language, like Ruby which is kind of a multi-paradigm language? I hope you’re not pesky about ‘language pureness’ :-)</p>
<p><strong>Avi Bryant:</strong> Actually, I am pesky about it – not from an academic or aesthetic point of view, but from a pragmatic one. “Pure” languages make a lot of things easier: development tools, VMs, and infrastructure like distributed object systems or transparent persistence all get much simpler and better when you have a small, consistent language.</p>
<p>One experience I had, for example, was being hired to port my client library for the GOODS object database from Smalltalk to Python; I spent a huge amount of time dealing with special cases in the Python semantics (is this a class? a type? a new-style class? implemented in python or in C?) that simply never came up in Smalltalk.</p>
<p>Ruby does pretty well here, better than most in the scripting language space – I wouldn’t call Ruby “multi-paradigm”, by the way – but it could stand to do better.</p>
<p><strong>AkitaOnRails:</strong> I think you once said that you only consider a language “finished” when it is fast enough to extend itself. This is very true for Smalltalk, Java and other platforms. Ruby is undoubtedly lacking in its current position. That was one of the reasons we now have parallel efforts like John Lam’s IronRuby, Charles and Thomas’ JRuby and Evan’s Rubinius. One of the most criticized points is that Ruby doesn’t have a formal spec for the language or its core libraries. You also said that the Java VM is not suited enough for Ruby as Strongtalk would be, for example. Does this still hold today as we see JRuby 1.1 on the horizon?</p>
<p><strong>Avi Bryant:</strong> It still holds. JRuby currently seems to benchmark on par with MRI, and the best numbers from any Ruby implementation are maybe 3X MRI on average. I think we can do <strong>25X MRI</strong> for basic language feature benchmarks (message sends, block evaluation, etc), which should be enough to make Ruby implementations of the standard libraries feasible. Even if the net result was a similarly performing system, the side benefits would be extremely valuable.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/200126067_a2fc0a9fec.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> I remember that you started the <a href="http://rubyforge.org/projects/smalltalk/"target="_blank" rel="noopener">Smalltalk.rb</a> initiative a while ago, and if I am not mistaken it was an effort to make Ruby translate into equivalent Smalltalk code so it could run in any of the available Smalltalk VM implementations out there. Is this project still going on, do you still think it is feasible to pursue such a goal?</p>
<p><strong>Avi Bryant:</strong> I still think it’s a worthwhile and realistic goal. The project is still going in the sense that I’m still talking to people about how to make this happen, but I’m not able to spend time writing code for it myself at the moment. I have some reason to hope this project will progress more concretely next year, but we’ll see.</p>
<p><strong>AkitaOnRails:</strong> Some time ago I read a snippet of an IRC transcript between you, Evan, Chad and even Charles. You started talking about standardizing primitives (they are C-based in MRI today). My bet is that Rubinius is the project destined to be the “next big thing” for the Ruby community. To me it seems that you and Evan collaborate a lot, is that correct?</p>
<p><strong>Avi Bryant:</strong> I wouldn’t say that, but <a href="http://rubini.us/"target="_blank" rel="noopener">Rubinius</a> is certainly the Ruby project that most closely aligns with my own interests and goals at the moment, and it’s great to see how much effort is being put into it. Engine Yard also deserves a lot of kudos for their financial support of the project.</p>
<p>I have high hopes that the work Evan and crew are doing will be of great benefit not just to Rubinius but to all of the Ruby implementations – which is why I was advocating for a standard primitive set.</p>
<p><strong>AkitaOnRails:</strong> You know a lot of people, I wonder if you ever talked with Koichi Sasada himself to input some of your ideas for the YARV project as it’s going to be the “official” Ruby virtual machine out there a few months from now.</p>
<p><strong>Avi Bryant:</strong> No, I don’t believe we’ve ever spoken. I would certainly love to meet him some day.</p>
<p><strong>AkitaOnRails:</strong> Then, we get one level up to applications. Your Seaside framework is pretty amazing. You’re very right about <a href="http://developer.apple.com/tools/webobjects/"target="_blank" rel="noopener">WebObjects</a> as well. As a Java programmer I always pitied that it never got mainstream. Today it’s mostly relegated to Apple related websites, while lesser Java frameworks took the front. Tapestry and Cayenne are trying to top WebObjects’ features but seems they are nowhere near yet. Do you still program some Java? What is your opinion on current mainstream frameworks as Spring, JSF? For non-starters, what was it in WebObjects that makes it different than the rest?</p>
<p><strong>Avi Bryant:</strong> I don’t do any work in Java any more (thankfully), so I can’t really give a qualified opinion on the state of frameworks there right now. However, the main thing I think WebObjects did right was to focus on the state and behavior of the application, rather than on the mechanics of URLs and HTML and HTTP requests.</p>
<p>So rather than worry about what a field was named, you would just say <em>“this field is bound to this instance variable in my model”</em>, and rather than worry about what URL a link went to, you’d say <em>“this link triggers this method on my page object”</em>.</p>
<p>Transitioning between pages was done by constructing a new page object and setting it up directly in Java, rather than constructing a URL that was going to be parsed to build a page. That directness and general style is something that very heavily influenced Seaside.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/504096000_40e4e00b75.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> Many people may not immediately understand it as a word that is new to most of them: <em>“continuations”</em>. Meaning that instead of manually controlling the flow between events, with manual marshaling and unmarshaling of objects, you program as a cohesive and continuing flow. Can you explain this in a way that a novice would understand? What are the benefits and drawbacks of this approach?</p>
<p><strong>Avi Bryant:</strong> Well, it’s really all about being able to get modal behavior in a web app. In a desktop application, for example, you might occasionally have a call like <em>“file = chooseFile()”</em> or <em>“color = pickColor()”</em> that opens a modal dialog, gets a result from the user, and keeps going from there.</p>
<p>Seaside’s use of continuations is just to allow exactly that kind of interaction in the context of the web. The tricky bit is that because of the backbutton, the user might go pick a different color from the same dialog later, and the <em>“pickColor()”</em> call has to return a second time. This is confusing to think about, but mostly it Just Works.</p>
<p>From an implementation point of view, what we’re doing is saving a copy of the stack at the point the call is made so that we can come back to it later, more than once if need be.</p>
<p><strong>AkitaOnRails:</strong> You started these concepts in the Ruby world with IOWA. Once upon a time there was a framework called Borges that was also discontinued. Then Koichi mentioned taking continuation support from the next Ruby. Do you think the Ruby world is taking a step back with lack of support for continuations?</p>
<p><strong>Avi Bryant:</strong> Continuations are a great abstraction, and allow a lot of interesting experimentation with language semantics. However, I wouldn’t consider it a tragedy if they disappeared from Ruby; there are more important things to worry about. From a web perspective, I think that AJAX is starting to replace a lot of the use cases where having server-side modal logic used to be important – Dabble DB, for example, <strong>barely</strong> uses Seaside’s continuation features at all.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/106824107_67074eaf0b.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> Your blog has a peculiar name, <a href="http://www.cincomsmalltalk.com/userblogs/avi/View.ssp"target="_blank" rel="noopener">HREF considered harmful</a>. You probably got your inspiration from Edsger Dijkstra/Niklaus Wirth’s <a href="http://en.wikipedia.org/wiki/Considered_Harmful"target="_blank" rel="noopener">Goto Statement Considered Harmful</a> paper where Edsger made his famous case against GOTO. At the assembler level it is impossible to not JMP all the time (at least not without some macro help) but at a C level you can avoid GOTO’s altogether. Even Basic had both GOTO and GOSUB and you could just ignore the former. I am being simplistic here, but now you’re kind of saying that URLs play a similar role in a way that they disrupt the normal flow, going back in unpredictable ways and so on. On the other hand the Internet would not be what it is without URLs. How do we deal with this conundrum?</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/461207306_3f23831032.jpg" alt=""  loading="lazy" /></p>
<p><strong>Avi Bryant:</strong> I don’t think it’s a conundrum. I think your analogy is a good one: it’s impossible to not JMP all the time, and it’s also impossible not to use URLs all the time, but we don’t have to always be aware of either one. Back when everyone wrote in assembler, people probably obsessed about having meaningful or “pretty” JMP labels the same way they obsess about having pretty URLs today. I think we need to allow ourselves to make the same leap up in abstraction level on the web that we did with higher level programming languages.</p>
<p>That’s not to say we should <strong>never</strong> be thinking about URLs: any website needs to export meaningful URLs to be referenced and used externally, just like any C library needs to export meaningfully labelled function names so that it can be linked into other programs. But that’s as far as it needs to go.</p>
<p><strong>AkitaOnRails:</strong> Components were a big deal in the early 90’s, with RAD tools and so forth. Today it seems that they are showing up slowly, with initiatives as JSF and ASP.NET. Rails initially packed a very crude support for ‘components’ but it was dropped very early on as a bad thing (because of the jury rigged way it was implemented). Nothing new came to replace it what feels like there wasn’t enough need for it at the Rails level. On the other hand Seaside is heavy in componentization. Can you expose a little bit about the differences in this approach?</p>
<p><strong>Avi Bryant:</strong> “Component” is one of those terms like “MVC” or “open” that gets used to mean so many different things it’s hard to talk seriously about it. One concrete feature of Seaside’s approach is that each piece of UI is quite independent of what other pieces of UI may be on the page.</p>
<p>There’s no central controller which is responsible for request processing, choosing a template, or so on – all of those responsibilities are distributed among many smaller objects. There’s also no chance of naming conflicts because you don’t directly name links or form fields, so you can have many copies of the same form/widget/etc on the same page without any confusion.</p>
<p>This allows a level of modularity that’s very hard to reproduce in most traditional frameworks.</p>
<p><strong>AkitaOnRails:</strong> Another thing that might shock some people is that there is no traditional templating system in Seaside, no code mangling between HTML tags all over the place. Instead all HTML is abstracted in a object hierarchy that renders itself. In the Rails world there are a few alternatives that attempt some of that, like HAML, Markaby. What do you think are the fundamental differences?</p>
<p><strong>Avi Bryant:</strong> The HTML rendering API is a very core piece of Seaside, and a huge amount of the framework was designed with that underlying approach in mind. I think that’s the main difference when compared with HAML or Markaby, which are meant to be dropped into other frameworks.</p>
<p>Once you get rid of templates and switch to programmatic HTML generation, the design space just completely changes, and you need to re-evaluate almost every other decision that you made – this was a process that I went through with Seaside between the initial release, which used templates, and the 2.x versions, which don’t.</p>
<p>So I think we’ll have to see a framework that takes, say, Markaby as a starting point and asks “where can we go from here” before we can make any real comparisons.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/627957460_48e4181526.jpg" alt=""  loading="lazy" /><br>
with Adrian Holovaty, from Django fame</p>
<p><strong>AkitaOnRails:</strong> You also touted the shared-nothing vs share-all dilemma. And you obviously have a strong case because this is not just techno-babble but you do have a successful product running on top of that vision. Do you still think it pays off to sacrifice a little bit of performance for more clean code and productivity? In a way it’s sort of what DHH himself said about Rails when people said it was slower than PHP or Perl. Actually this also has to do with your talk about being a “heretic” in a way that you’re doing things in a different way than we all are now. Can you elaborate on that?</p>
<p><strong>Avi Bryant:</strong> <em>“Share nothing”</em> is great advice for making the maximum use of your server resources. For sites like yahoo.com or Facebook, that’s clearly very important because your business depends on serving millions of users every day.</p>
<p><em>“Share everything”</em> is really hard on your servers, but it makes maximum use of your developer resources. For a startup like Dabble DB, where we have 4 developers, that’s really important. And since our revenue model only requires serving thousands of users every day, not millions, it’s ok to spend a little more per user on server hardware. It’s been a very worthwhile tradeoff for us.</p>
<p><strong>AkitaOnRails:</strong> And speaking of DHH, you’ve probably met on several occasions. You’re both opinionated guys with antagonist visions. Smalltalk vs Ruby. Seaside vs Rails. Share-all vs Share-nothing. One could feel tempted to draw you two as Superman vs Batman kind of dispute. Though I know that smart people don’t fight over pesky stuff. What do you think of him and his ideas, how do you think both communities could cooperate if there’s any chance for that?</p>
<p><strong>Avi Bryant:</strong> I have a lot of respect for David. The only time I’ve felt antagonistic towards him was this last summer at Foo Camp. We were playing a game of <a href="http://en.wikipedia.org/wiki/Mafia_%28game%29"target="_blank" rel="noopener">Werewolf</a>, and I was a werewolf and he was a villager. Now, as a werewolf the best thing you can do is get the person nearest you to trust you, and that happened to be DHH. And it worked – Kevin Rose was totally on to me but David managed to persuade him not to lynch me, and I ate them both and won the game. Pn3d!</p>
<p>Anyway. There’s probably a lesson in there somewhere. Also, in a fight between me and DHH, <a href="http://en.wikipedia.org/wiki/Neal_Stephenson"target="_blank" rel="noopener">Neal Stephenson</a> would win.</p>
<p><strong>AkitaOnRails:</strong> Finally, Brazil still has a young and growing community. Any closing regards for our audience??</p>
<p><strong>Avi Bryant:</strong> Olá and good luck! I’ve been told Rio is even more beautiful than my home of Vancouver, and I hope to visit some day.</p>
<p><a href="http://twit.tv/floss21"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/1057077803_3bd0155620.jpg" alt=""  loading="lazy" /></a><br>
<em>Don’t miss the TWiT podcast interview with Avi!</em></p>
]]></content:encoded><category>interviews</category><category>programming-languages</category><category>software-architecture</category></item><item><title>Chatting with John Lam (IronRuby)</title><link>https://akitaonrails.github.io/en/2007/11/12/chatting-with-john-lam-ironruby/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2007/11/12/chatting-with-john-lam-ironruby/</guid><pubDate>Mon, 12 Nov 2007 18:37:00 -0200</pubDate><description>&lt;p&gt;&lt;a href="http://www.iunknown.com/"target="_blank" rel="noopener"&gt;&lt;img src="http://s3.amazonaws.com/akitaonrails/assets/2007/11/12/n685772348_7884.jpg" alt="" loading="lazy" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It’s been a while since my last international interview, and I am back with none other than one of the people responsible for bringing Ruby to the .NET platform. That’s correct, I’ve been covering a lot about JRuby and Rubinius but we can’t neglect that one of the biggest platforms out there in the market is receiving the Ruby treatment as well. So I invited &lt;a href="http://www.iunknown.com/"target="_blank" rel="noopener"&gt;John Lam&lt;/a&gt; , who kindly answered several questions regarding this endeavor.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="http://www.iunknown.com/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/11/12/n685772348_7884.jpg" alt=""  loading="lazy" /></a></p>
<p>It’s been a while since my last international interview, and I am back with none other than one of the people responsible for bringing Ruby to the .NET platform. That’s correct, I’ve been covering a lot about JRuby and Rubinius but we can’t neglect that one of the biggest platforms out there in the market is receiving the Ruby treatment as well. So I invited <a href="http://www.iunknown.com/"target="_blank" rel="noopener">John Lam</a> , who kindly answered several questions regarding this endeavor.</p>
<p>Remembering that <a href="http://www.ironruby.net/"target="_blank" rel="noopener">IronRuby</a> – named after <a href="http://www.ironpython.com/"target="_blank" rel="noopener">IronPython</a>, the first of the main open source dynamic languages built on top of .NET – is a <a href="http://www.opensource.org/node/207"target="_blank" rel="noopener">true open source</a> project, and also has a 3rd party addon for Visual Studio.NET, so programmers used to the VS.NET workflow can get onboard with a lower learning curve ahead of them.</p>
<p>Despite opinions against Microsoft just for the sake of arguing, the fact remains that Java and .NET represent the biggest corporate development market today. And this is also a fact that the Ruby meme is spreading in a very fast way. Being built to run on top of both the JVM and the CLR represents Ruby being enabled for market niches that it wouldn’t reach otherwise, and this is a huge win. I talked a little bit about this in my article (in portuguese): <a href="/2007/10/23/para-eu-ganhar-o-outro-precisa-perder">For myself to win, the other one has to lose</a>. There are very intelligent people at Microsoft, John Lam being one of them.</p>
<p>That said, let’s get started:</p>
<p><strong>AkitaOnRails:</strong> First of all, I always ask my guests about their careers: what was your path up until now? Meaning, how did you get yourself involved in computing, what drives you into it?</p>
<p><strong>John Lam:</strong> I wrote a lot of software before I graduated from High School in 1986. I shipped a bunch of commercial software for the Commodore ‘platform’ (aka PET, VIC-20, C-64/128) back in the day. In 1986 I decided that there was no future in computing (which shows why folks shouldn’t take my opinions seriously :)) and decided to study the physical sciences in University. I graduated in 1995 with my doctorate in Organic Chemistry and decided that there was no future in chemistry (which, if you look at how the reverse engineering of life today is based a lot on cross-discipline interaction with other branches of life sciences, shows why folks shouldn’t take my opinions seriously :)).</p>
<p>Once I decided to work in computing again, I took up an obscure new development platform called Delphi which shipped in 1.0 form around the time that I re-entered the field. I spent a bunch of time working on integrating Delphi with COM, which is where I originally hung my hat. The dark side of COM pulled me into its orbit and I’ve been working on Microsoft related stuff since then.</p>
<p><strong>AkitaOnRails:</strong> I see that you’re at Microsoft since January 2007, is that correct? How did you get in there, were you hired with the IronRuby project in mind? Is this your main assignment today?</p>
<p><strong>John Lam:</strong> Yes, I started in January. Before joining the company, I worked on an open source bridge between Ruby and the CLR called RubyCLR. When I decided to start promoting that work at conferences, I crossed paths with the DLR team at Microsoft. Mahesh Prakriya graciously donated half of his talk at Tech Ed in 2006 so that I could talk about RubyCLR. After much hand-wringing/mind-conditioning I wound up accepting their offer to work full time on a Ruby implementation on top of the DLR and we (my wife, two kids and dog) arrived in Redmond in January.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/11/12/166261351_2b04ab1086.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> What were the motivations behind starting IronRuby? How did it start? Were you the original and sole developer of a .NET<br>
implementation of Ruby?</p>
<p><strong>John Lam:</strong> We want to demonstrate that the DLR is truly a multi-language dynamic language runtime, so we had to build a number of languages on top of it. Ruby certainly fit the criteria: dynamic, strong user community, lots of attention, so it made sense to implement it. IronRuby has definitely forced a lot of design changes to the DLR, making it less ‘Pythonic’ which is a good thing for other folks who are interested in building a dynamic language on top of our platform.</p>
<p>I’m definitely not the sole developer on IronRuby. We have two great devs working on the project: Tomas Matousek and John Messerly and they’re doing awesome work. Our tester, Haibo Luo, is doing great work to make sure that we ship a high quality implementation. Haibo’s work will be released along with IronRuby, which means that the community can also benefit from his test suite.</p>
<p><strong>AkitaOnRails:</strong> Not many people here are familiar with the DLR and Silverlight, can you briefly describe them for non-starters? What makes a ‘DLR’ different from a ‘CLR’ besides jumping from “C” to “D”? :-)</p>
<p><strong>John Lam:</strong> The DLR is a platform that layers on top of the CLR. This means that we can run anywhere that the CLR can run, which of course includes Silverlight. Silverlight is our cross-platform browser-based platform that runs on Windows, Mac and Linux (via the Moonlight project). We ship a cross-platform version of the CLR which means that you’ll be able to run DLR-based languages with just a 5MB initial download into your web browser.</p>
<p><strong>AkitaOnRails:</strong> This is delicate: I once read Ola Bini and Charles Nutter criticizing the “Microsoft Way”, meaning that it would be extremely hard – if not impossible – for you to write a 100% complete Ruby 1.8 implementation on top of .NET without being able to peek into the source code. The reason being the company policy toward open source licenses. Was that the case?</p>
<p><strong>John Lam:</strong> I think that IronPython is the existence proof that you can build a compatible implementation of a dynamic language without looking at the original source code. In our work with IronRuby, I can tell you that not being able to look at the source code hasn’t prevented us from figuring out how Ruby accomplishes some feature – it’s all about designing the right experiments.</p>
<p><strong>AkitaOnRails:</strong> I also saw that IronRuby is going to be released under open source-compatible license from Microsoft, making it probably the first true open source project ever from Microsoft, meaning that you would not only allow people to take a look at the source code but also to contribute back to it. How far is this?</p>
<p><strong>John Lam:</strong> The Microsoft Public License was officially sanctioned by the OSI in early October. IronRuby, IronPython and the DLR are all released under this license.</p>
<p>We’ve already accepted contributions back in from the community, and are continuing to accept more contributions from the community.</p>
<p>We’re actually not the first Open Source project at Microsoft that accepts contributions back from the community. That honor belongs to the ASP.NET <a href="http://www.asp.net/ajax/ajaxcontroltoolkit/samples/"target="_blank" rel="noopener">AJAX control toolkit</a> which is an enormously successful project with &gt; 1M downloads to date.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/11/12/166261286_d084c57e46.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> The DLR was originally conceived as an experimental way to run Python on top of .NET. Did this make it easier for a Ruby implementation or do you feel that Python and Ruby were different enough to guarantee lots of tweaks into the DLR to make it feasible?</p>
<p><strong>John Lam:</strong> It’s fair to call the original version of the DLR the “Python Language Runtime”. Adding Ruby to the mix forced a lot of additional changes to the DLR, the most significant being the refactoring of the runtime to migrate all of the Python specific idioms (which were thought to be general but turned out not to be the case) into IronPython.dll. What remains is a core which our the 4 languages developed by MSFT (IronRuby, IronPython, Managed JScript and VB) continue to use effectively today.</p>
<p><strong>AkitaOnRails:</strong> Is it a goal to be 100% compatible with MRI 1.8? Or are you going toward a sub-set of the Ruby implementation? This would make it possible to write .NET programs in Ruby, using the full set of .NET libraries and frameworks as ASP.NET, Windows.Form, LINQ, etc. Or are you planning on a full blown MRI compatible implementation, enough to be able to run an entire Rails application?</p>
<p><strong>John Lam:</strong> Our #1 priority is being as compatible as possible with MRI 1.8.×. The one feature that we will not support is continuations, which is consistent with JRuby. Other than that (and in the absence of a Ruby spec), compatibility will be judged by whether we can run real Ruby applications and community developed Ruby test suites. Obviously Rails is an extremely important Ruby application, which we will absolutely run on top of IronRuby.</p>
<p>.NET interop is our #2 priority. We will integrate with the .NET platform, and allow you to write Ruby applications using the rich set of .NET libraries. Obviously things like Silverlight, WPF, LINQ, WinForms, and ASP.NET will be supported.</p>
<p><strong>AkitaOnRails:</strong> Ruby grew as a collective and low profile effort that really exploded 3 years ago because of Rails. That’s one reason no one ever bothered to write a formal specification and test suite. You have Alioth, you have the current YARV test suite. Did you have a hard time because of this lack of formal specs? Are you in contact with people like Charles from JRuby, Koichi Sasada from YARV or Evan Phoenix from Rubinius? What do you think of all these efforts going on in parallel? Is there any way you can contribute with one another?</p>
<p><strong>John Lam:</strong> We’re using the Rubinius spec suite today, and plan on shipping it along with our sources to ensure that we catch compat bugs early in the process. I’m guilty of not participating as much as I would like to in talking more with Charlie, Evan and Koichi about collaborating on specs. We’re focused on getting the core language up and running today, and, like a lot of other fast-moving projects, have been neglecting to document the things we have learned. Once the pace of the project slows down a bit, I’ll be able to spend more time engaging with the other implementers.</p>
<p><strong>AkitaOnRails:</strong> Ruby has a lot of known deficiencies. Koichi is trying to improve on some of them for Ruby 1.9. How are you tackling problems like lack of native threading, simple mark and sweep garbage collector, no virtual machine and thus no byte-code compilation, performance issues, lack of native internationalization (Unicode), lots of C-based extensions.</p>
<p><strong>John Lam:</strong> We get a lot of things for ‘free’ by running on top of the CLR. Native threading is supported in IronRuby. We use the CLR garbage collector, which is a world-class GC. We compile natively to IL, and our IL can be GC’d as well which is goodness for long-running server based applications. We will obviously support CLR strings, which are Unicode strings and we will also support Ruby mutable strings which are based on byte arrays. We will not support C-based extensions at all, instead we will be porting the existing C-based libraries in the Ruby Standard Library to C# (or Ruby).</p>
<p><strong>AkitaOnRails:</strong> Are there any benchmarks comparing IronRuby with the MRI? Do you think you’re already outperforming it? Are there any issues today around Ruby performance over the DLR? Or more interestingly: is there any comparison between Ruby on DLR and C# or VB.NET over the CLR?</p>
<p><strong>John Lam:</strong> We are faster than MRI in the YARV benchmarks, but that number changes daily. One great thing about our team is that we have a performance regression test suite that we regularly run across all of our code. This makes it trivial to pinpoint a build that causes a regression in performance, even if we postpone that work for a while.</p>
<p>We have done zero performance tuning so far; we’re just using features that exist in the DLR today, like polymorphic inline caches for efficient call-site method caching. As our implementation matures, we will tune IronRuby to perform well on application-level benchmarks (not just microbenchmarks which are useful to us but not so interesting to real customers). The DLR (and CLR) is becoming faster all the time, and any dynamic language that is built on top of the DLR will benefit from these improvements.</p>
<p>Obviously, languages like C# and VB.NET are faster than Ruby today. But we are confident that we can significantly narrow that gap over time.</p>
<p><strong>AkitaOnRails:</strong> What about tooling? I saw you tweaked your Visual Studio.NET to have the Vibrant Ink that we – TextMate users – are used to, and it looks nice. I even saw a screenshot of code folding of Ruby inside of Visual Studio. How is its support for Ruby?</p>
<p><strong>John Lam:</strong> Right now our team isn’t involved in VS integration. However, the good folks at <a href="http://www.sapphiresteel.com/IronRuby-Visual-Designer"target="_blank" rel="noopener">Sapphire in Steel</a> are doing great work at integrating IronRuby into VS.</p>
<p><a href="http://www.sapphiresteel.com/IronRuby-Visual-Designer"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/11/12/editing_small.gif" alt=""  loading="lazy" /></a></p>
<p><strong>AkitaOnRails:</strong> You mentioned in an interview the possibility of Ruby running on top of Silverlight. How feasible is this today?</p>
<p><strong>John Lam:</strong> We demonstrated IronRuby running on top of Silverlight at RubyConf. The talk should be posted soon. We’re planning on rendezvousing with Silverlight in time for their next CTP (late this year / early next year). At that time, you will be able to code in Ruby that runs in your browser.</p>
<p><strong>AkitaOnRails:</strong> Microsoft is learning on how to deal with the open source community. Do you think the adoption of technologies as Python and Ruby are helping the company to understand the open source way? What do you think still has to improve? Meaning, if you could, what would you change?</p>
<p><strong>John Lam:</strong> Microsoft is a big company and we’re working very hard on working together with the Open Source community. IronRuby is one of the most visible Open Source projects in the company, and we’re helping to pave the way for other teams that want to engage with the Open Source community. Change happens incrementally at big companies and it’s been fun being a part of that change!</p>
<p><strong>AkitaOnRails:</strong> Silverlight was released over an open source license as well, so that the Mono guys were able to run it very quickly over Mono. Are you close to Miguel De Icaza? What do you think about Mono? An IronRuby running on top of it means a multi-platform Ruby that can run over Linux. If you ever get 100% compatibility with MRI and Rails, this means running over Apache+mod_mono on Linux and over IIS on Windows, everything developed under Orcas. Are there any plans for this kind of integration and end-to-end solution?</p>
<p><strong>John Lam:</strong> Actually, Silverlight is not released under an Open Source license. The DLR, IronPython and IronRuby are all released under the Microsoft Public License, which makes it possible for Miguel and his band of merry men to just ship it on top of their Moonlight implementation.</p>
<p>I love how pragmatic Miguel is. We showed IronRuby running on top of Mono at RubyConf and that was largely due to the Mono team fixing a bunch of bugs at the last minute so that we could demo (thanks!). DLR + Iron* tends to push the C# and the CLR pretty hard. We’ve uncovered bugs in the Microsoft C# compiler, and we’ve uncovered bugs in gmcs under Mono as well. But thankfully, both the C# team and the Mono team have been great at fixing those bugs to unblock us.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/11/12/481182714_62e1d07f7c.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> I think, that’s it for today. Any closing remarks for the Brazilian audience?</p>
<p><strong>John Lam:</strong> Thanks for sending along a great set of questions! Good luck with your Ruby adventures and I hope you’ll have a chance to check out IronRuby in the future.</p>
]]></content:encoded><category>interviews</category><category>ruby</category><category>programming-languages</category></item><item><title>Chatting with Ola Bini (JRuby Core Team Member)</title><link>https://akitaonrails.github.io/en/2007/06/21/chatting-with-ola-bini-jruby-core-team-member/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2007/06/21/chatting-with-ola-bini-jruby-core-team-member/</guid><pubDate>Thu, 21 Jun 2007 20:58:00 -0300</pubDate><description>&lt;p&gt;It’s been a few days already, since the historical release of JRuby 1.0, the first stable implementation of an alternative Ruby interpreter. And what other platform than Java to hold the power of Ruby?&lt;/p&gt;
&lt;p&gt;Meet &lt;strong&gt;Ola Bini&lt;/strong&gt; , a young, dynamic and important contributor to this amazing project. Ola is member of the JRuby Core Team and currently works at ThoughtWorks to help assure the continuing success of JRuby.&lt;/p&gt;
&lt;p&gt;I had the opportunity to chat for more than an hour with him. So, another great interview for our website.&lt;/p&gt;</description><content:encoded><![CDATA[<p>It’s been a few days already, since the historical release of JRuby 1.0, the first stable implementation of an alternative Ruby interpreter. And what other platform than Java to hold the power of Ruby?</p>
<p>Meet <strong>Ola Bini</strong> , a young, dynamic and important contributor to this amazing project. Ola is member of the JRuby Core Team and currently works at ThoughtWorks to help assure the continuing success of JRuby.</p>
<p>I had the opportunity to chat for more than an hour with him. So, another great interview for our website.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/511173503_15ef7dc203.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails</strong> : Hi. Ola, you’re ready?</p>
<p><strong>Ola Bini</strong> : Hi there. Yeah, sure, let’s get started!</p>
<p><strong>AkitaOnRails</strong> : Super! First of all, thank you very much for accepting my invitation, I understand you’re very busy. And that’s exactly why it will be interesting to talk to you, as you’re involved in a lot of exciting stuff.</p>
<p><strong>Ola Bini</strong> : hehe. Yeah, hopefully I’ll be able to tell you about it too.</p>
<p><strong>AkitaOnRails</strong> : Now, second, congratulations for both the achievement of the release of JRuby 1.0 and of you moving to London and becoming a <a href="http://ola-bini.blogspot.com/2007/03/thoughtworks.html"target="_blank" rel="noopener">Thoughtworker</a>.</p>
<p><strong>Ola Bini</strong> : Thank you! I’m very happy about both occurences.</p>
<p><strong>AkitaOnRails</strong> : I always start my interviews with a little overview on the origin of the programmer, or how did you get into the tech world. Young programmers have lots of questions and doubts and I think this helps getting them in perspective. Could you tell us how you started? What made you choose programming?</p>
<p><strong>Ola Bini</strong> : Well… I guess my father was an influence. I started programming Basic on an Apple IIc when I was 7 or 8 years old. I had fun from the beginning, so my father helped me out, he gave me my own computer and so on. So basic was what I did first, but C was the first real programming language I learned and actively used.</p>
<p><strong>AkitaOnRails</strong> : Game programming?</p>
<p><strong>Ola Bini</strong> : Yeah, game programming and demo programming the first few years. Picked up C, most of C++, Assembler and Pascal before I was 16. I actually dropped out of High School, since I got an offer to work as a programmer in Stockholm, which is how I started doing it professionally.</p>
<p><strong>AkitaOnRails</strong> : Just for fun?</p>
<p><strong>Ola Bini</strong> : Yes, the game and demo programming was just for fun.</p>
<p><strong>AkitaOnRails</strong> : Oh, so you started working on programming very early in your life.</p>
<p><strong>Ola Bini</strong> : Yes. I got a full time job programming Java at the age of 18.</p>
<p><strong>AkitaOnRails</strong> : So you jumped very fast from C to C++, Pascal and Java.</p>
<p><strong>Ola Bini</strong> : Yes. Java was easy after C and C++. Pascal I never really liked, but I had to learn it since most tutorials about graphics programming used it.</p>
<p><strong>AkitaOnRails</strong> : So your first job was around graphics?</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/img1098574794.jpg" alt=""  loading="lazy" /></p>
<p><strong>Ola Bini</strong> : Actually not. It was web programming with Java, JSP, PHP, ASP and other such technologies, for a department of <a href="http://ki.se/"target="_blank" rel="noopener">Karolinska Institutet</a>, doing web systems for distance learning.</p>
<p><strong>AkitaOnRails</strong> : Yes, by the way, you mentioned Stockholm. Are you from there? Sweden?</p>
<p><strong>Ola Bini</strong> : No, I’m from a small place called Kode, which is about 60km north of Gothenburg. Yes, I’m from Sweden. Moved to Stockholm at 18.</p>
<p><strong>AkitaOnRails</strong> : Wow, I believe your parents were very shocked having you dropping school to work so early. Have you ever graduated?</p>
<p><strong>Ola Bini</strong> : Well, not shocked really. I didn’t really learn much during that school period, so it didn’t come as a surprise to my parents. And nope, no graduation for me.</p>
<p><strong>AkitaOnRails</strong> : And I believe this was the 2000 internet bubble era? As you mentioned web tools.</p>
<p><strong>Ola Bini</strong> : Actually, it was 2001, directly after that bubble, so I was lucky to get a job.</p>
<p><strong>AkitaOnRails</strong> : I see. You have a lot of Java background, how did you start working on Java? And how did you become interested in Ruby?</p>
<p><strong>Ola Bini</strong> : Yes, I’ve never been fond of Java as a language, and I’ve always been interested in other languages. So I learned <a href="http://en.wikipedia.org/wiki/Lisp_programming_language"target="_blank" rel="noopener">Lisp</a> quite early and fell in love with it. A few years back, 3 or 4 I think, I discovered Ruby and tried to convince my employer that we should replace various perl and bash scripts in our environment with Ruby. That didn’t work so well, but I continued talking about Ruby and using it in my spare time, and one and a half year ago they finally caved in, we made a Rails pilot and it was a clear success.</p>
<p><strong>AkitaOnRails</strong> : Was there any particular event that led you to Ruby, or you found it by accident?</p>
<p><strong>Ola Bini</strong> : I think it was accident. Especially since I don’t remember the exact point of it at all.</p>
<p><strong>AkitaOnRails</strong> : Lisp has a completely different <a href="http://www.levenez.com/lang/history.html"target="_blank" rel="noopener">heritage</a> from structured languages like C. I guess you liked the <a href="http://en.wikipedia.org/wiki/Functional_programming"target="_blank" rel="noopener">functional</a> aspect of it?</p>
<p><strong>Ola Bini</strong> : Yes. And the malleability. I’m very fond of the macros in <a href="http://en.wikipedia.org/wiki/Common_Lisp"target="_blank" rel="noopener">Common Lisp</a>, and also like the very no-nonsense structure of a Lisp program. The fact that there is no real division between data and code. The functional aspects are pleasing, but not the main reason for my like of it.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/510521311_e2ca389c3e.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails</strong> : Coming back to JRuby. I read from your <a href="http://ola-bini.blogspot.com/2005/12/homegrown-dao-to-hibernate-to-rails.html"target="_blank" rel="noopener">blog</a> that you’re working on JRuby since early 2005? How did you become a JRuby Core Team member? Did you know <a href="http://headius.blogspot.com/"target="_blank" rel="noopener">Charles</a> or <a href="http://www.bloglines.com/blog/ThomasEEnebo"target="_blank" rel="noopener">Thomas</a> before?</p>
<p><strong>Ola Bini</strong> : Late 2005. And no, I didn’t know them from before. I started contributing small things first, and then last <a href="http://ola-bini.blogspot.com/2007/03/jruby-regular-expressions.html"target="_blank" rel="noopener">March</a> and <a href="http://ola-bini.blogspot.com/2007/04/on-activerecord-jdbc-performance.html"target="_blank" rel="noopener">April</a> I did some larger things. I continued helping out, and soon after <a href="http://www.tbray.org/ongoing/When/200x/2006/09/07/JRuby-guys"target="_blank" rel="noopener">Charles and Tom got hired by Sun</a> they decided that I would make a good addition to the core team.</p>
<p><strong>AkitaOnRails</strong> : Were you still working in JRuby on your spare time or did it become a full-time endeavour?</p>
<p><strong>Ola Bini</strong> : I have never been able to do JRuby full-time; sometimes I’ve been able to work on it during lulls in my regular work, but most of it has been strictly spare time. Until now, of course.</p>
<p><strong>AkitaOnRails</strong> : There are a lot of doubt and fear in the mind of inexperienced Java programmers here. They “fear” to have lost time learning Java. They “think” that Ruby is a lesser language, and so on and so forth. I think the problem is that most of them think that they only need to learn one language only. This is a two-fold question: first, you mentioned that you dislike Java. Can you elaborate on what exactly? And second, what do you like about Ruby?</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/504012154_839b1be79a.jpg" alt=""  loading="lazy" /></p>
<p><strong>Ola Bini</strong> : I would like to begin by saying this: it is very hard to be a good programmer while only knowing one language. Knowing several programming languages of different style is <a href="http://media.pragprog.com/titles/mjwti/generalist.pdf"target="_blank" rel="noopener">imperative</a> to use a language well. Knowing lisp makes me a better Java programmer. My former co-worker Lars have told me that learning Ruby makes him a better Java programmer. So, learning another language is in fact something you can do, and should do, to make you a better programmer in Java, for example.</p>
<p><strong>AkitaOnRails</strong> : I couldn’t expect a better answer.</p>
<p><strong>Ola Bini</strong> : So, Java: I think the language overly verbose, the type system is inconsistent and quite annoying at times, and there is no way to compress code. When refactoring, you end up with more lines of code for the same functionality. Also, there is lot of clutter in a typical program.</p>
<p>I don’t think <a href="http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html"target="_blank" rel="noopener">generics</a> have improved this situation. I think that Ruby have almost all the power I’m used to from Common Lisp. In some cases you can do amazing things with the <a href="http://ola-bini.blogspot.com/2006/09/ruby-metaprogramming-techniques.html"target="_blank" rel="noopener">metaprogramming</a> capabilities. Ruby is like clay, it can be formed into whatever you want, and Java is marble. Also, I like that Ruby have enough of a heritage from Perl to be pragmatic and easy to use; it’s fast to get started with and you are productive in a very short amount of time.</p>
<p><strong>AkitaOnRails</strong> : Ruby is an excellent language, no doubt. Ruby 1.9 is growing to become better. Are you updated in their development? I personally have a few things I’d like to have in a new version, as native UTF-16 top to bottom, a better and unified Datetime handling, at least less C dependency, a generational garbage collector, a better way for IDEs to introspect and debug into the code maybe as Smalltalk does. Now that you touched most of the language, what do you think you’d change if you had the opportunity? Or you’re satisfied with its current state?</p>
<p><strong>Ola Bini</strong> : No, I’m not satisfied, which is one of the reasons I work on JRuby. There are some features of the Ruby language which I think makes it hard in some cases. The file system operations aren’t abstract enough; very unix-centric.</p>
<p>The threading system is dangerous since it provides features a “real” threading system and GC can never give you. (things like <a href="http://jira.codehaus.org/browse/JRUBY-1157"target="_blank" rel="noopener">critical=</a> and <a href="http://www.rubycentral.com/book/ospace.html"target="_blank" rel="noopener">ObjectSpace</a> I would like to see gone).</p>
<p>So, a better GC, more tunability, a better threading system, no global locking. But right now, I feel that the fact that we have several implementations of Ruby coming up, the features will prove themselves. If <a href="http://blog.fallingsnow.net/rubinius/"target="_blank" rel="noopener">Rubinius</a> makes the right choices for these things, Rubinius will get more users than MRI/KRI in the long run. I agree about better UTF support, of course. Anyone who’s not from the English countries know this.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/279242203_f1b6d306d9_m.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails</strong> : I was trying to reach Evan Phoenix. Charles mentioned Rubinius as well. How do you think both projects can collaborate? What gaps Rubinius close for JRuby, for example?</p>
<p><strong>Ola Bini</strong> : We are trying to cooperate. Especially with testing and implementing more of the core library in Ruby. Evan is moving to LA this week, so that’s why you won’t reach him.</p>
<p><strong>AkitaOnRails</strong> : Yes, so I heard. And speaking of which, I would be personally terrified on working on something as big as JRuby. The part that frights me the most are the <a href="http://www.rubyinside.com/how-to-create-a-ruby-extension-in-c-in-under-5-minutes-100.html"target="_blank" rel="noopener">C-coded</a> ones. There are several core features of Ruby totally coded in C. How difficult was this to you? Can you tell us about some particular issue about this that left you scratching your head?</p>
<p><strong>Ola Bini</strong> : Well, yeah, parts of MRI can be fairly “interesting”. They do things like modifying the <a href="http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?id=1043284392&amp;answer=1046139290"target="_blank" rel="noopener">argc and argv</a> to varargs methods. The problem with the C code is that they heavily use pointers, all over the place. Sometimes they wrap non-Ruby objects in a <a href="http://www.oreillynet.com/ruby/blog/2006/01/the_ruby_value_1.html"target="_blank" rel="noopener">VALUE</a> pointer, which is just hackery. But it’s usually not that hard to figure it out, since I’ve been doing lots of C code in my days.</p>
<p><strong>AkitaOnRails</strong> : The JRuby implementation proved to be very successful but not without its hassles. <a href="http://headius.blogspot.com/2006/06/unicode-in-ruby-unicode-in-jruby.html"target="_blank" rel="noopener">Unicode</a>, <a href="http://ola-bini.blogspot.com/search/label/openssl"target="_blank" rel="noopener">SSL</a>, <a href="http://jira.codehaus.org/browse/JRUBY-1046"target="_blank" rel="noopener">Regex</a>, <a href="http://jira.codehaus.org/browse/JRUBY-561"target="_blank" rel="noopener">YAML</a>. For example, you described differences in the Java’s regex implementation and the Ruby MRI’s version at your blog. What were the most difficult parts on the porting process, feature-wise, from your point of view? Where was the heavy-lifting?</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/511173733_e5680a0442.jpg" alt=""  loading="lazy" /></p>
<p><strong>Ola Bini</strong> : There are basically two things that were the “worst” in getting something that works. The first was all the corner cases. Strange behaviors that isn’t really specified, but just behaves that way because of the implementation.</p>
<p>Have you tried using super within a block, where the surrounding method takes * args and you modify args? Threading proved problematic too. And the other main area is, as you say, extensions. It’s basically impossible to run anything significant without porting several extensions. Just a few number of these we needed, for example: strscan, yaml, zlib, enumerable and regexps are problematic. We have three different engines right now. The Java one, the <a href="http://jregex.sourceforge.net/"target="_blank" rel="noopener">JRegex</a> (which is the one we use), and one called REJ, which is the MRI regexp engine ported to Java. (REJ isn’t used)</p>
<p><strong>AkitaOnRails</strong> : Before JRuby actually started gaining traction (mainly because of Rails, I guess) they were pretty much stalled. It was trying to cover Ruby 1.8.2 if I remember correctly. So much of this stuff were not yet covered at that time?</p>
<p><strong>Ola Bini</strong> : Yes, that’s true. Most of the progress we have made, was done from January of 2006 and forward. The support was there. Large parts of the parser was working fine, but almost no extensions, and many parts of the language were still at the 1.6 level.</p>
<p><strong>AkitaOnRails</strong> : I remember reading in your blog about a way to run a <a href="http://ola-bini.blogspot.com/search/label/mongrel"target="_blank" rel="noopener">Mongrel Cluster</a> entirely inside a single JVM. Was that just a proof of concept or do you see Rails apps being deployed this way? What would be the advantage of using Mongrel in the JVM space? Or the main traction for Rails is the Goldspike plugin and the <a href="http://rubyforge.org/projects/jruby-extras/"target="_blank" rel="noopener">JRuby-Extras</a> tools?</p>
<p><strong>Ola Bini</strong> : I did it first as a proof-of-concept, but I think it’s a viable solution. But mostly, I thought it was interesting to do as something for people coming from the Ruby crowd. People who are used to the Mongrel approach. I wanted to give them a better solution than starting 10 JVM’s. And that’s in core, the jruby server is a generalized tool that can be used in other circumstances too.</p>
<p><strong>AkitaOnRails</strong> : But Goldspike doesn’t require Mongrel to run. So we have 2 ways of running Rails apps in the Java space.</p>
<p><strong>Ola Bini</strong> : Absolutely. And I think that the Goldspike route is the superior one. But Ruby is all about <a href="http://headius.blogspot.com/2006/06/mongrel-in-jruby.html"target="_blank" rel="noopener">choice</a> and flexibility.</p>
<p><strong>AkitaOnRails</strong> : I guess we will come to that again when talking about <a href="http://studios.thoughtworks.com/2007/5/7/mingle-to-run-on-jruby"target="_blank" rel="noopener">Mingle</a>. Which brings me to <a href="http://studios.thoughtworks.com/"target="_blank" rel="noopener">ThoughtWorks</a>. Tell us this story. How did you know them? How did you get on board?</p>
<p><strong>Ola Bini</strong> : Well, the name have been familiar for a long while. They’ve got a good reputation, as you know. About last April a friend of mine got hired. We talked some and it seemed like a good place. We decided that I should apply. She did a recommendation and I was there and interviewed in August. Everything went quite well, except that they didn’t have enough revenue at that point, so they couldn’t hire me. Then we took up contact again in February, started talking about Ruby and ThoughtWorks Studios. I came back, did a quick interview with Cyndi, and the rest was basically logistics.</p>
<p><strong>AkitaOnRails</strong> : <a href="http://studios.thoughtworks.com/2007/3/6/how-to-mingle"target="_blank" rel="noopener">Cyndi Mitchell</a>?</p>
<p><strong>Ola Bini</strong> : Yes. She’s one of my bosses. (The structures here aren’t really clear… =)</p>
<p><strong>AkitaOnRails</strong> : She presented a <a href="http://conferences.oreillynet.com/cs/rails2007/view/e_sess/14493"target="_blank" rel="noopener">keynote</a> at RailsConf if I remember correctly.</p>
<p><strong>Ola Bini</strong> : Yep. She did. She was also the one who put my name and number on the big screen. =) I was there with her and Roy and <a href="http://www.martinfowler.com/bliki/"target="_blank" rel="noopener">Martin Fowler</a>, and some other ThoughtWorkers.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/cyndy.png" alt=""  loading="lazy" /></p>
<p>There’s a flat structure, yes, which means I don’t have explicit bosses, but bosses that manage over different areas of my time, so to speak. So Chad Wathington takes care of my <a href="http://studios.thoughtworks.com/rubyworks"target="_blank" rel="noopener">RubyWorks</a> work, Cyndi handled all employee logistics for Studios, and Alexei Vorontsov is Project Manager for <a href="http://rubyworks.rubyforge.org/"target="_blank" rel="noopener">RubyWorks</a>. All of them bosses to me, so to speak.</p>
<p><strong>AkitaOnRails</strong> : Nice. I also read that you’re working a lot in Mingle. This is another important milestone as this is probably the first commercial JRuby app with total support from a big company. Please elaborate on what is Mingle and what are your responsabilities in this project.</p>
<p><strong>Ola Bini</strong> : Absolutely. So, Mingle is a an agile project management system. It aims to be more flexible and more close to the agile methods that agile people would love to practice more of. My responsibility is deceptively simple: it is my task to make sure that JRuby will work out, fix problems and help out when making the application bi-implementational, so it runs on both JRuby and MRI.</p>
<p><strong>AkitaOnRails</strong> : Does it have anything to do with <a href="http://cruisecontrol.sourceforge.net/overview.html"target="_blank" rel="noopener">CruiseControl</a>?</p>
<p><strong>Ola Bini</strong> : No, not at this point. As far as I know, there will be integration at some point, but I’m not involved in that.</p>
<p><strong>AkitaOnRails</strong> : Ok. You’re also involved in every single Java and Ruby conferences out there. I always wondered how guys like you find enough time to 1) actually present good stuff at the conferences; 2) find enough time to bring exciting open source projects as JRuby; 3) still have a day-job and be productive. You have to have 48 hours each day (and I’m probably disrupting your workflow right now =) How do you get yourself organized?</p>
<p><strong>Ola Bini</strong> : Actually, we try to share conferences between the core developers. For example, I will take <a href="http://javasymposium.techtarget.com/europe/europe_info.html"target="_blank" rel="noopener">TSSJS</a> while Charles and Tom take vacations. Presenting good stuff helps us share material among each other. Also, Charles and Tom, and now me, are actually working on this as our day job, where evangilizing is an important part.</p>
<p>Organization is harder. I usually keep lists on <a href="http://www.tadalist.com/"target="_blank" rel="noopener">tadalist</a>. That works well enough for me. That’s all the organization I’m actually using. But I tend to be highly chaotic, which works fine for me.</p>
<p><strong>AkitaOnRails</strong> : haha, but you have to be on the computer more hours than the average programmer to accomplish all this.</p>
<p><strong>Ola Bini</strong> : hehe. Well, yes. I’m not far away from computers during my waking hours, that’s for sure. Especially not with the book writing, which eats seriously into my time for other things. =/</p>
<p><strong>AkitaOnRails</strong> : And speaking of being workaholic and evangelization. You are also writing a new book about JRuby. It is scheduled to be released in a few months. Tell us more about it.</p>
<p><strong>Ola Bini</strong> : Well, it’s a <a href="http://ola-bini.blogspot.com/2007/06/book-update.html"target="_blank" rel="noopener">book</a> about JRuby on Rails. It’s aimed to be quite practical, giving concrete examples on how do achieve things. There is quite a lot of JRuby information buried in it, of course, but it’s not a general book about JRuby.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/pat.jpg" alt=""  loading="lazy" /></p>
<p><a href="http://www.apress.com/category.html?nID=155"target="_blank" rel="noopener">APress</a> will publish it, and according to the current schedule, it will be released in October. <a href="http://on-ruby.blogspot.com/"target="_blank" rel="noopener">Pat Eyler</a> is my tech reviewer. And that’s about it. =) It’s going to be about 250 pages long, containing 4 JRuby on Rails projects developed from scratch.</p>
<p><strong>AkitaOnRails</strong> : Haha, and I imagine that it is difficult to write about a moving target, isn’t it? JRuby is still evolving as well as plugins such as Goldspike, that you need for Java deployment.</p>
<p><strong>Ola Bini</strong> : Of course. In some cases I had to revise parts substantially, and in other cases I had to write lots of stuff for JRuby, just to get it finished for the book. So, in some ways, me writing the book has improved my work on JRuby too, giving me input on things that need to be fixed, and so on.</p>
<p><strong>AkitaOnRails</strong> : Yeah, do you see a JRuby 1.1 release coming soon?</p>
<p><strong>Ola Bini</strong> : Hopefully a JRuby 1.1 release will be out within 2 months. It’s possible that it will take longer, due to the summer being down time for some of us. But JRuby have a tendency to move fast and have many releases, so I should count on it within 2 months.</p>
<p><strong>AkitaOnRails</strong> : Rails brought a lot of interest in developing using Macs. <a href="http://www.pragmaticprogrammer.com/titles/textmate/"target="_blank" rel="noopener">Textmate</a> is the primary reason. I saw some of Charles presentations and I believe he was using Emacs. What are your primary tools for developing both JRuby and your Rails apps?</p>
<p><strong>Ola Bini</strong> : Actually, Charles and Tom usually use <a href="http://wiki.rubyonrails.org/rails/pages/HowtoUseVimWithRails"target="_blank" rel="noopener">vi</a> for presentations, and <a href="http://wiki.netbeans.org/wiki/view/RubyOnRails"target="_blank" rel="noopener">Netbeans</a> when doing development. I’m more oldfashioned. I work on a Mac, but I’m too much of a Lispnic to use anything else than <a href="http://dima-exe.ru/rails-on-emacs"target="_blank" rel="noopener">Emacs</a>. So Emacs, with my something like 10.000 lines of E-lisp code is my main tool.</p>
<p><strong>AkitaOnRails</strong> : Hey, won’t you release your Emacs bundle as an open source project as well? :-)</p>
<p><strong>Ola Bini</strong> : hehe. No, I don’t think so. It’s full of cruft and many things which are highly specialized for me. That’s really the point of Emacs: you create your own little world.</p>
<p><strong>AkitaOnRails</strong> : I think that lack of stronger tools and IDEs are still an obstacle for mass adoption by switchers coming from Java or .NET. Do you feel the same way or do you see adoption growing regardless of that?</p>
<p><strong>Ola Bini</strong> : I think you are right, but I don’t see it as a rational objection to switching. But there is coming lots of good support for Ruby now, if you want it. Eclipse, Netbeans, <a href="http://www.jetbrains.com/idea/training/demos/rails.html"target="_blank" rel="noopener">IntelliJ</a>, jBuilder. There are many options and they seem to get better each day.</p>
<p><strong>AkitaOnRails</strong> : But, you’re not dropping Emacs any time soon ;-)</p>
<p><strong>Ola Bini</strong> : hehe. Nope. That’s not going to happen.</p>
<p><strong>AkitaOnRails</strong> : And finally, what do you see in your future with JRuby? Any other personal project that you want to invest more time? Or you’re 100% full-time on JRuby for the time being?</p>
<p><strong>Ola Bini</strong> : Well, I believe that JRuby and JRuby related projects is going to take much of my time. If I get some spare time, I would probably try to concentrate on my music; but if you wanted computer related <a href="http://ola-bini.blogspot.com/2006/09/limits-of-power-what-lisp-can-do-but.html"target="_blank" rel="noopener">projects</a> there are three things I would want to do: Jio, a Java implementation of Io; CLaJ, a new Common Lisp implementation on Java, based on <a href="http://en.wikipedia.org/wiki/Lisp_in_Small_Pieces"target="_blank" rel="noopener">Lisp In Small Pieces</a>, with the explicit goal of being both performant, maintainable and compliant. And finally, I would love to be able to do more AI things.</p>
<p><strong>AkitaOnRails</strong> : You say you don’t like Java that much, but 2 out of 3 are Java related :-)</p>
<p><strong>Ola Bini</strong> : You have to make a difference between Java, the language and Java, the platform. The platform is outstanding, and I want all languages to have a base like that. And yes, the projects are Java based, which means I’ll sacrifice myself for the sake of others… =)</p>
<p><strong>AkitaOnRails</strong> : Good point. And as Steve Jobs would say <em>“there’s One More Thing™”</em>: I try to bring a lot of advise from experienced people like you to programmers beggining their careers. They have lots of questions. What would you recommend for this new generation of programmers? (Although I think you are very young yourself.)</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/515994249_5a5bc1b92d.jpg" alt=""  loading="lazy" /></p>
<p><strong>Ola Bini</strong> : This shouldn’t come as a big surprise after this interview: my advice would be to learn more than one language. Simple enough. Something functional, or with a desperately different concurrency model. Lisp, Smalltalk, Erlang and Ruby are all good choices for a Java/C/C++ programmer to learn. And yeah, I guess I am. I’m 25 now.</p>
<p><strong>AkitaOnRails</strong> : As Dave Thomas said in the Pragmatic Programmer: learn at least one <a href="http://www.pragmaticprogrammer.com/loty/"target="_blank" rel="noopener">new language every year</a>.</p>
<p><strong>Ola Bini</strong> : Absolutely. That’s good advice. I can’t top it. =)</p>
<p><strong>AkitaOnRails</strong> : By the way, out of curiosity, do you speak English in Sweden?</p>
<p><strong>Ola Bini</strong> : No, not very much. I speak English when the other person doesn’t understand Swedish, or if someone in our company doesn’t understand Swedish (out of courtesy).</p>
<p><strong>AkitaOnRails</strong> : I thought so, you have very good English.</p>
<p><strong>Ola Bini</strong> : Thanks. We work with English from the 4th grade in Sweden, so most have a passing ability at least.</p>
<p><strong>AkitaOnRails</strong> : That’s another thing I <a href="/articles/2007/04/14/off-topic-seja-arrogante">evangelize</a> here in Brazil: to learn English as best as you can. Liking it or not, English is the language of the technology world.</p>
<p><strong>Ola Bini</strong> : Yes, without question. English is very important. And after that, I guess Chinese is. Based on simple numbers.</p>
<p><strong>AkitaOnRails</strong> : Absolutely. How’s your mandarim? :-)</p>
<p><strong>Ola Bini</strong> : hehe. My mandarin and cantonese are totally nonexistent. But I guess that’ll come… =)</p>
<p><strong>AkitaOnRails</strong> : I guess ThoughtWorks has an office at China as well, doesn’t it?</p>
<p><strong>Ola Bini</strong> : Yeah, we have two. It’s possible I’ll move there for 6 months next year. But I don’t have any facility for human languages. English is the only language I know well, except for Swedish.</p>
<p><strong>AkitaOnRails</strong> : No way, tell me about it. That’s a scoop!</p>
<p><strong>Ola Bini</strong> : Hehe, it’s possible, but not certain. We are looking at where the RubyWorks team should be placed, and China is probably the best location for me. I will move to San Francisco later on, but I need to work for ThoughtWorks for 1 year to get an L1 visa first.</p>
<p><strong>AkitaOnRails</strong> : China? Best for you? how come?</p>
<p><strong>Ola Bini</strong> : Since most of the RubyWorks team will be there or in Bangalore.</p>
<p><strong>AkitaOnRails</strong> : Ruby, with JRuby, is getting near its birth place (Japan). Curious. Well, I guess I already disrupted your work hours a lot. Another great interview is finished ;-) Any closing remarks for the brazilian audience?</p>
<p><strong>Ola Bini</strong> : hehe. Thanks. Nope, I think I’ve said it all now! =) I hope to get to Brazil at some point or another, so don’t be too harsh on me when I arrive!</p>
<p><strong>AkitaOnRails</strong> : Cool. Its been very nice talking to you. I hope we can exchange more ideas later on. Thank you very much!</p>
<p><strong>Ola Bini</strong> : Thanks! Keep me posted on what happens. Bye.</p>
]]></content:encoded><category>interviews</category><category>ruby</category><category>programming-languages</category></item></channel></rss>