<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>The Zen of Coding</title>
    <link>https://joel.id</link>
    <description>Finding flow</description>
    <language>en</language>
    <atom:link href="https://joel.id/blog/rss.xml" rel="self" type="application/rss+xml"/><lastBuildDate>Sun, 19 Jul 2026 09:00:00 +0000</lastBuildDate>
    <item>
      <title>Why Habu</title>
      <link>https://joel.id/why-habu/</link>
      <guid isPermaLink="true">https://joel.id/why-habu/</guid>
      <pubDate>Sun, 19 Jul 2026 09:00:00 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator>
      <category>Habu</category>
      <category>GPU</category>
      <category>Compilers</category>
      <category>Triton</category><description><![CDATA[]]></description>
    </item>
    <item>
      <title>GPU Kernels &amp;amp; Compilers: A Compressed Interview Course</title>
      <link>https://joel.id/kernel-compiler-interview-course/</link>
      <guid isPermaLink="true">https://joel.id/kernel-compiler-interview-course/</guid>
      <pubDate>Sat, 27 Jun 2026 09:00:00 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator>
      <category>GPU</category>
      <category>Compilers</category>
      <category>Machine Learning</category><description><![CDATA[]]></description>
    </item>
    <item>
      <title>Hacking LLDB for a great Zig debugging experience</title>
      <link>https://joel.id/hacking-lldb-for-a-great-zig-debugging-experience/</link>
      <guid isPermaLink="true">https://joel.id/hacking-lldb-for-a-great-zig-debugging-experience/</guid>
      <pubDate>Thu, 18 Dec 2025 08:43:35 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator><description><![CDATA[<p>Discuss it on <a rel="external" href="https://x.com/joelreymont/status/2001574396096340146?s=20">Twitter</a>, <a rel="external" href="https://www.reddit.com/r/Zig/comments/1ppmanm/hacking_lldb_for_a_great_zig_debugging_experience/">Reddit</a> or <a rel="external" href="https://news.ycombinator.com/item?id=46310523">Hacker News</a>.</p>
<p>Zig is better than C. And that’s great — until you try to debug it.</p>
<p>If you’ve ever dropped into LLDB while debugging Zig, you already know the pain: slices look like random structs, optionals are unreadable, error unions feel hostile, and expressions like <code>slice[0]</code> just… don’t work.</p>
<p>This post introduces <a rel="external" href="https://github.com/joelreymont/zdb"><strong>zdb</strong></a>, an LLDB plugin that makes debugging Zig feel <em>normal</em> — without rebuilding LLDB or messing with Python scripts.</p>
<h2 id="the-problem">The Problem</h2>
<p>Here’s a simple Zig program (from <code>test/test_types.zig</code> in the <a rel="external" href="https://github.com/joelreymont/zdb">zdb repo</a>):</p>
<pre class="giallo z-code"><code data-lang="zig"><span class="giallo-l"><span class="z-keyword">const</span><span class="z-variable"> std</span><span class="z-keyword z-operator z-assignment z-support z-function"> = @import</span><span>(</span><span class="z-string">&quot;std&quot;</span><span>);</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">const</span><span class="z-variable"> Color</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> enum</span><span> { </span><span class="z-variable">red</span><span>,</span><span class="z-variable"> green</span><span>,</span><span class="z-variable"> blue</span><span>,</span><span class="z-variable"> yellow</span><span> };</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">const</span><span class="z-variable"> TestStruct</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> struct</span><span> {</span></span>
<span class="giallo-l"><span class="z-variable">    name</span><span class="z-punctuation z-separator z-key-value">: []</span><span class="z-keyword">const u8</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">    values</span><span class="z-punctuation z-separator z-key-value">: []</span><span class="z-keyword">i32</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">    optional_value</span><span class="z-punctuation z-separator z-key-value z-keyword z-operator">: ?</span><span class="z-keyword">i32</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">    error_result</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> MyError</span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">i32</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">    shape</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> Shape</span><span>,</span></span>
<span class="giallo-l"><span>};</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword z-storage">pub fn</span><span class="z-entity z-name z-function"> main</span><span>() </span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">void</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword">    var</span><span class="z-variable"> gpa</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> std</span><span>.</span><span class="z-variable">heap</span><span>.</span><span class="z-entity z-name z-type">GeneralPurposeAllocator</span><span>(.{}){};</span></span>
<span class="giallo-l"><span class="z-keyword z-control">    defer</span><span class="z-variable"> _</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> gpa</span><span>.</span><span class="z-entity z-name z-function">deinit</span><span>();</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> allocator</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> gpa</span><span>.</span><span class="z-entity z-name z-function">allocator</span><span>();</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-comment">    // Test slices</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> string_slice</span><span class="z-punctuation z-separator z-key-value">: []</span><span class="z-keyword">const u8</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-string"> &quot;Hello, zdb debugger!&quot;</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> int_slice</span><span class="z-punctuation z-separator z-key-value">: []</span><span class="z-keyword">const i32</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-bitwise"> = &amp;</span><span>[</span><span class="z-variable">_</span><span>]</span><span class="z-keyword">i32</span><span>{ </span><span class="z-constant z-numeric">1</span><span>,</span><span class="z-constant z-numeric"> 2</span><span>,</span><span class="z-constant z-numeric"> 3</span><span>,</span><span class="z-constant z-numeric"> 4</span><span>,</span><span class="z-constant z-numeric"> 5</span><span> };</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-comment">    // Test optionals</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> some_value</span><span class="z-punctuation z-separator z-key-value z-keyword z-operator">: ?</span><span class="z-keyword">i32</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-constant z-numeric"> 42</span><span>;</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-comment">    // Test std library</span></span>
<span class="giallo-l"><span class="z-keyword">    var</span><span class="z-variable"> list</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> std</span><span>.</span><span class="z-entity z-name z-type">ArrayListUnmanaged</span><span>(</span><span class="z-keyword">i32</span><span>) </span><span class="z-keyword z-operator z-assignment">=</span><span> .</span><span class="z-variable">empty</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword z-control">    try</span><span class="z-variable"> list</span><span>.</span><span class="z-entity z-name z-function">appendSlice</span><span>(</span><span class="z-variable">allocator</span><span>,</span><span class="z-keyword z-operator z-bitwise"> &amp;</span><span>.{ </span><span class="z-constant z-numeric">10</span><span>,</span><span class="z-constant z-numeric"> 20</span><span>,</span><span class="z-constant z-numeric"> 30</span><span> });</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-variable">    std</span><span>.</span><span class="z-variable">debug</span><span>.</span><span class="z-entity z-name z-function">print</span><span>(</span><span class="z-string">&quot;Breakpoint here!</span><span class="z-constant z-character z-escape">\n</span><span class="z-string">&quot;</span><span>, .{});</span><span class="z-comment">  // breakpoint here</span></span>
<span class="giallo-l"><span>}</span></span></code></pre>
<p>Now stop at the breakpoint and inspect things in <em>stock</em> LLDB:</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>(lldb) frame variable string_slice int_slice color test_struct list</span></span>
<span class="giallo-l"><span>([]u8) string_slice = (ptr = &quot;Hello, zdb debugger!&quot;, len = 20)</span></span>
<span class="giallo-l"><span>([]i32) int_slice = {</span></span>
<span class="giallo-l"><span>  ptr = 0x00000001000da244</span></span>
<span class="giallo-l"><span>  len = 5</span></span>
<span class="giallo-l"><span>}</span></span>
<span class="giallo-l"><span>(test_types.Color) color = blue</span></span>
<span class="giallo-l"><span>(test_types.TestStruct) test_struct = {</span></span>
<span class="giallo-l"><span>  name = (ptr = &quot;test object&quot;, len = 11)</span></span>
<span class="giallo-l"><span>  values = {</span></span>
<span class="giallo-l"><span>    ptr = 0x000000016fdfe588</span></span>
<span class="giallo-l"><span>    len = 3</span></span>
<span class="giallo-l"><span>  }</span></span>
<span class="giallo-l"><span>  optional_value = (data = 42, some = &#39;\x01&#39;)</span></span>
<span class="giallo-l"><span>  error_result = (value = 100, tag = 0)</span></span>
<span class="giallo-l"><span>  shape = {</span></span>
<span class="giallo-l"><span>    payload = { ... }</span></span>
<span class="giallo-l"><span>    tag = circle</span></span>
<span class="giallo-l"><span>  }</span></span>
<span class="giallo-l"><span>}</span></span>
<span class="giallo-l"><span>(array_list.Aligned(i32,null)) list = {</span></span>
<span class="giallo-l"><span>  items = {</span></span>
<span class="giallo-l"><span>    ptr = 0x0000000100180000</span></span>
<span class="giallo-l"><span>    len = 3</span></span>
<span class="giallo-l"><span>  }</span></span>
<span class="giallo-l"><span>  capacity = 32</span></span>
<span class="giallo-l"><span>}</span></span></code></pre>
<p>This is <em>technically</em> correct — but not very useful.</p>
<p>And if you try to write Zig-like expressions:</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>(lldb) p int_slice[0]</span></span>
<span class="giallo-l"><span>                  ^</span></span>
<span class="giallo-l"><span>                  error: type &#39;[]i32&#39; does not provide a subscript operator</span></span></code></pre>
<p>LLDB has no idea that a slice is really <code>(ptr, len)</code>. It just sees a struct.</p>
<h2 id="existing-solutions-and-why-they-re-not-great">Existing Solutions (And Why They’re Not Great)</h2>
<p>LLDB lets you register Python-based type summaries:</p>
<pre class="giallo z-code"><code data-lang="python"><span class="giallo-l"><span class="z-punctuation z-definition z-comment z-comment"># zig_formatters.py</span></span>
<span class="giallo-l"><span class="z-keyword z-control">import</span><span> lldb</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-storage">def</span><span class="z-entity z-name z-function"> slice_summary</span><span>(</span><span class="z-variable z-parameter z-function z-language z-python">valobj</span><span>,</span><span class="z-variable z-parameter z-function z-language z-python"> internal_dict</span><span>):</span></span>
<span class="giallo-l"><span>    ptr</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-punctuation z-separator z-period z-python"> valobj.</span><span class="z-meta z-function-call z-generic z-python">GetChildMemberWithName</span><span class="z-punctuation z-definition z-arguments z-begin z-python">(</span><span class="z-string">&#39;ptr&#39;</span><span class="z-punctuation z-definition z-arguments z-end z-python">)</span></span>
<span class="giallo-l"><span>    length</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-punctuation z-separator z-period z-python"> valobj.</span><span class="z-meta z-function-call z-generic z-python">GetChildMemberWithName</span><span class="z-punctuation z-definition z-arguments z-begin z-python">(</span><span class="z-string">&#39;len&#39;</span><span class="z-punctuation z-definition z-arguments z-end z-python z-punctuation z-separator z-period z-python">).</span><span class="z-meta z-function-call z-generic z-python">GetValueAsUnsigned</span><span class="z-punctuation z-definition z-arguments z-begin z-python z-punctuation z-definition z-arguments z-end z-python">()</span></span>
<span class="giallo-l"><span class="z-keyword z-control z-storage">    return f</span><span class="z-string">&#39;len=</span><span class="z-constant z-character z-format z-placeholder z-other z-python">{</span><span>length</span><span class="z-constant z-character z-format z-placeholder z-other z-python">}</span><span class="z-string"> ptr=</span><span class="z-constant z-character z-format z-placeholder z-other z-python">{</span><span class="z-punctuation z-separator z-period z-python">ptr.</span><span class="z-meta z-function-call z-generic z-python">GetValue</span><span class="z-punctuation z-definition z-arguments z-begin z-python z-punctuation z-definition z-arguments z-end z-python">()</span><span class="z-constant z-character z-format z-placeholder z-other z-python">}</span><span class="z-string">&#39;</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-storage">def</span><span class="z-entity z-name z-function"> __lldb_init_module</span><span>(</span><span class="z-variable z-parameter z-function z-language z-python">debugger</span><span>,</span><span class="z-variable z-parameter z-function z-language z-python"> internal_dict</span><span>):</span></span>
<span class="giallo-l"><span class="z-punctuation z-separator z-period z-python">    debugger.</span><span class="z-meta z-function-call z-generic z-python">HandleCommand</span><span class="z-punctuation z-definition z-arguments z-begin z-python">(</span></span>
<span class="giallo-l"><span class="z-string">        &#39;type summary add -F zig_formatters.slice_summary -x &quot;</span><span class="z-constant z-character z-escape">\\</span><span class="z-string">[</span><span class="z-constant z-character z-escape">\\</span><span class="z-string">].*&quot;&#39;</span></span>
<span class="giallo-l"><span class="z-punctuation z-definition z-arguments z-end z-python">    )</span></span>
<span class="giallo-l"><span class="z-support z-function">    print</span><span class="z-punctuation z-definition z-arguments z-begin z-python">(</span><span class="z-string">&#39;[python] Registered slice formatter&#39;</span><span class="z-punctuation z-definition z-arguments z-end z-python">)</span></span></code></pre>
<p>That gets you nicer output:</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>(lldb) frame variable string_slice int_slice</span></span>
<span class="giallo-l"><span>([]u8) string_slice = len=20 ptr=0x00000001000e3fde</span></span>
<span class="giallo-l"><span>([]i32) int_slice = len=5 ptr=0x00000001000da244</span></span></code></pre>
<p>But expressions still don’t work:</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>(lldb) p int_slice[0]</span></span>
<span class="giallo-l"><span>error: type &#39;[]i32&#39; does not provide a subscript operator</span></span></code></pre>
<p><strong>Problems:</strong></p>
<ul>
<li>You’re writing Python to debug systems code</li>
<li>Crossing Python ↔ C++ boundaries is slow</li>
<li>No expression support</li>
<li>No string content display</li>
</ul>
<h3 id="zig-lldb-the-nuclear-option">zig-lldb (The Nuclear Option)</h3>
<p>Jacob Young’s <a rel="external" href="https://github.com/jacobly0/llvm-project/tree/lldb-zig"><strong>zig-lldb</strong></a> fork adds a full <code>TypeSystemZig</code> to LLDB.</p>
<p>It’s <em>excellent</em>. It’s also a full LLDB fork.</p>
<p><strong>Upsides</strong></p>
<ul>
<li>Real Zig semantics</li>
<li>Expressions work</li>
<li>Variables view works</li>
</ul>
<p><strong>Downsides</strong></p>
<ul>
<li>Rebuild LLDB (bring snacks!)</li>
<li>Track upstream changes forever</li>
<li>Not usable with system or Homebrew LLDB</li>
</ul>
<h2 id="enter-zdb">Enter zdb</h2>
<p>zdb takes a different approach: <em>how far can we go without rebuilding LLDB?</em></p>
<p>zdb is a <strong>native C++ LLDB plugin</strong> that:</p>
<ol>
<li>Registers real type formatters (no Python)</li>
<li>Rewrites expressions so Zig syntax works</li>
<li>Runs on stock LLDB</li>
</ol>
<p>Load it:</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>(lldb) plugin load libzdb.dylib</span></span>
<span class="giallo-l"><span>[zdb] Loaded 19 formatters + expression syntax</span></span></code></pre>
<p>Now things look… sane:</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>(lldb) frame variable string_slice int_slice</span></span>
<span class="giallo-l"><span>([]u8) string_slice = &quot;Hello, zdb debugger!&quot; {</span></span>
<span class="giallo-l"><span>  ptr = 0x00000001000e3fde &quot;Hello, zdb debugger!&quot;</span></span>
<span class="giallo-l"><span>  len = 20</span></span>
<span class="giallo-l"><span>}</span></span>
<span class="giallo-l"><span>([]i32) int_slice = len=5 ptr=0x1000da244 {</span></span>
<span class="giallo-l"><span>  ptr = 0x00000001000da244</span></span>
<span class="giallo-l"><span>  len = 5</span></span>
<span class="giallo-l"><span>}</span></span></code></pre>
<p>And expressions finally behave:</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>(lldb) p int_slice[0]</span></span>
<span class="giallo-l"><span>(int) $0 = 1</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>(lldb) p list[0]</span></span>
<span class="giallo-l"><span>(int) $1 = 10</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>(lldb) p test_struct.optional_value.?</span></span>
<span class="giallo-l"><span>(int) $2 = 42</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>(lldb) p test_struct.error_result catch 0</span></span>
<span class="giallo-l"><span>(int) $3 = 100</span></span></code></pre>
<p>That alone makes day-to-day debugging dramatically better.</p>
<h2 id="how-this-works-a-k-a-the-dark-arts">How This Works (a.k.a. The Dark Arts)</h2>
<p>LLDB has two APIs:</p>
<ul>
<li><strong>Public API</strong> (<code>SBTarget</code>, <code>SBValue</code>, etc.)</li>
<li><strong>Internal API</strong> (<code>TypeCategoryImpl</code>, <code>FormatManager</code>, etc.)</li>
</ul>
<p>The public API is… polite but limited.</p>
<p>The internal API does the real work — and isn’t exported.</p>
<p>So zdb cheats!</p>
<h3 id="step-1-find-internal-symbols">Step 1: Find Internal Symbols</h3>
<p>They’re not exported, but they’re <em>there</em>:</p>
<pre class="giallo z-code"><code data-lang="shellscript"><span class="giallo-l"><span class="z-entity z-name z-function">nm</span><span class="z-constant"> -C</span><span class="z-string"> liblldb.dylib</span><span class="z-keyword z-operator"> |</span><span class="z-entity z-name z-function"> grep</span><span class="z-string"> AddTypeSummary</span></span>
<span class="giallo-l"><span class="z-entity z-name z-function">0000000000360f38</span><span class="z-string"> t lldb_private::TypeCategoryImpl::AddTypeSummary</span><span>(</span><span class="z-support z-function">...</span><span>)</span></span></code></pre><h3 id="step-2-compute-the-base-address">Step 2: Compute the Base Address</h3>
<p>We anchor off a known exported symbol:</p>
<pre class="giallo z-code"><code data-lang="cpp"><span class="giallo-l"><span class="z-storage z-keyword z-operator z-arithmetic z-cpp">void*</span><span> ref </span><span class="z-keyword z-operator z-assignment z-cpp">=</span><span class="z-entity z-name z-function"> dlsym</span><span class="z-punctuation z-separator z-delimiter">(handle,</span><span class="z-string"> &quot;_ZN4lldb10SBDebugger10InitializeEv&quot;</span><span>);</span></span>
<span class="giallo-l"><span class="z-storage">uintptr_t</span><span> base </span><span class="z-keyword z-operator z-assignment z-cpp">=</span><span> (</span><span class="z-storage">uintptr_t</span><span>)ref </span><span class="z-keyword z-operator z-arithmetic z-cpp">-</span><span> reference_offset;</span></span></code></pre>
<p>Now every internal symbol is just <code>base + offset</code>.</p>
<h3 id="step-3-call-it-with-the-right-abi">Step 3: Call It with the Right ABI</h3>
<p>ARM64 calling conventions matter. <code>shared_ptr</code> gets passed <em>indirectly</em>:</p>
<pre class="giallo z-code"><code data-lang="cpp"><span class="giallo-l"><span class="z-keyword">using</span><span class="z-entity z-name z-type"> AddTypeSummaryFn</span><span class="z-keyword z-operator z-assignment z-cpp z-storage"> = void</span><span> (</span><span class="z-keyword z-operator z-arithmetic z-cpp">*</span><span>) (</span></span>
<span class="giallo-l"><span class="z-storage z-keyword z-operator z-arithmetic z-cpp">    void*</span><span class="z-punctuation z-separator z-delimiter"> this_ptr,</span></span>
<span class="giallo-l"><span class="z-storage z-keyword z-operator z-arithmetic z-cpp">    const char*</span><span class="z-punctuation z-separator z-delimiter"> name_ptr,</span></span>
<span class="giallo-l"><span class="z-storage">    size_t</span><span class="z-punctuation z-separator z-delimiter"> name_len,</span></span>
<span class="giallo-l"><span class="z-storage">    int</span><span class="z-punctuation z-separator z-delimiter"> match_type,</span></span>
<span class="giallo-l"><span>    SharedPtrLayout</span><span class="z-keyword z-operator z-arithmetic z-cpp">*</span><span> sp</span></span>
<span class="giallo-l"><span>);</span></span></code></pre>
<p>Mess this up and you get instant crashes.</p>
<h3 id="step-4-rewrite-expressions">Step 4: Rewrite Expressions</h3>
<p>zdb intercepts expressions and rewrites them:</p>
<table><thead><tr><th>Zig</th><th>Becomes</th></tr></thead><tbody>
<tr><td><code>slice[n]</code></td><td><code>slice.ptr[n]</code></td></tr>
<tr><td><code>list[n]</code></td><td><code>list.items.ptr[n]</code></td></tr>
<tr><td><code>opt.?</code></td><td><code>opt.data</code></td></tr>
<tr><td><code>err catch x</code></td><td><code>(err.tag == 0 ? err.value : x)</code></td></tr>
</tbody></table>
<p>Users never see this. They just type Zig.</p>
<h2 id="offset-tables">Offset Tables</h2>
<p>Offsets change between LLDB versions, so zdb uses JSON tables:</p>
<pre class="giallo z-code"><code data-lang="json"><span class="giallo-l"><span>{</span></span>
<span class="giallo-l"><span class="z-punctuation z-support z-type z-property-name z-json">  &quot;version&quot;</span><span>:</span><span class="z-string"> &quot;21.1.7&quot;</span><span>,</span></span>
<span class="giallo-l"><span class="z-punctuation z-support z-type z-property-name z-json">  &quot;reference_symbol&quot;</span><span>:</span><span class="z-string"> &quot;_ZN4lldb10SBDebugger10InitializeEv&quot;</span><span>,</span></span>
<span class="giallo-l"><span class="z-punctuation z-support z-type z-property-name z-json">  &quot;reference_offset&quot;</span><span>:</span><span class="z-string"> &quot;0x4c670&quot;</span><span>,</span></span>
<span class="giallo-l"><span class="z-punctuation z-support z-type z-property-name z-json">  &quot;symbols&quot;</span><span>: {</span></span>
<span class="giallo-l"><span class="z-punctuation z-support z-type z-property-name z-json">    &quot;TypeCategoryImpl::AddTypeSummary&quot;</span><span>: {</span><span class="z-punctuation z-support z-type z-property-name z-json"> &quot;offset&quot;</span><span>:</span><span class="z-string"> &quot;0x35ff38&quot;</span><span> }</span></span>
<span class="giallo-l"><span>  }</span></span>
<span class="giallo-l"><span>}</span></span></code></pre>
<p>There’s a script to generate these automatically:</p>
<pre class="giallo z-code"><code data-lang="shellscript"><span class="giallo-l"><span class="z-entity z-name z-function">python3</span><span class="z-string"> tools/dump_offsets.py liblldb.dylib</span><span class="z-keyword z-operator"> &gt;</span><span class="z-string"> lldb-21.1.7.json</span></span></code></pre><h2 id="limitations-yes-there-are-some">Limitations (Yes, There Are Some)</h2>
<p>The big missing piece is <strong>Variables View expansion</strong>.</p>
<p>That requires registering C++ synthetic children via <code>std::function</code>. Unfortunately, ABI mismatches between plugin and LLDB builds make this extremely fragile.</p>
<p>So:</p>
<ul>
<li>CLI expressions work</li>
<li>GUI tree expansion does not</li>
</ul>
<p>For most Zig debugging, this turns out to be fine.</p>
<h2 id="comparison">Comparison</h2>
<table><thead><tr><th>Feature</th><th>Stock LLDB</th><th>Python</th><th>zig-lldb</th><th>zdb</th></tr></thead><tbody>
<tr><td>Install</td><td>Built-in</td><td>Script</td><td>Rebuild</td><td>Plugin</td></tr>
<tr><td>Summaries</td><td>✗</td><td>✓</td><td>✓</td><td>✓</td></tr>
<tr><td><code>slice[n]</code></td><td>✗</td><td>✗</td><td>✓</td><td>✓</td></tr>
<tr><td>Optionals</td><td>✗</td><td>✗</td><td>✓</td><td>✓</td></tr>
<tr><td>Variables view</td><td>✗</td><td>Partial</td><td>✓</td><td>✗</td></tr>
<tr><td>Performance</td><td>—</td><td>Slow</td><td>Fast</td><td>Fast</td></tr>
</tbody></table>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>zdb is intentionally pragmatic.</p>
<p>It’s not as pure as zig-lldb, and it’s definitely more cursed than Python formatters — but it hits a sweet spot:</p>
<ul>
<li>Works with stock LLDB</li>
<li>Makes Zig readable</li>
<li>Makes expressions usable</li>
</ul>
<p>For everyday Zig debugging, that’s usually all you want.</p>
<p>Your contributions are welcome — especially offset tables for new LLDB releases!</p>
<p><em>Thanks to Jacob Young for <a rel="external" href="https://github.com/jacobly0/llvm-project/tree/lldb-zig">zig-lldb</a>, which proved that proper Zig debugging support is possible and inspired this lighter-weight alternative.</em></p>
]]></description>
    </item>
    <item>
      <title>AI will write your next compiler!</title>
      <link>https://joel.id/ai-will-write-your-next-compiler/</link>
      <guid isPermaLink="true">https://joel.id/ai-will-write-your-next-compiler/</guid>
      <pubDate>Wed, 26 Nov 2025 13:50:59 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator><description><![CDATA[<p>Discuss on <a rel="external" href="https://news.ycombinator.com/item?id=46057430">Hacker News</a>, <a rel="external" href="https://x.com/joelreymont/status/1993679163291496574?s=20">Twitter</a> or <a rel="external" href="https://www.reddit.com/r/programming/comments/1p78a8h/ai_will_write_your_next_compiler/">Reddit</a>.  Also, <a href="https://joel.id/resume/">consider hiring me</a>!</p>
<h3 id="it-was-a-dark-and-stormy-night">It was a dark and stormy night</h3>
<p>Well, a gloomy Kyiv morning.</p>
<p>I opened my laptop and discovered that my Claude Code account had been <em>disabled</em>. Not only that — I’d been refunded the unused portion of my $200/month Max plan and downgraded to the Free tier. I had 10–15 active Claude Code Web sessions working across multiple open-source projects, and everything ground to a halt.</p>
<p>Sensing impending catastrophe, I navigated to the billing page and paid $200 again… only to watch it refunded a few minutes later.</p>
<h3 id="how-it-started">How it started</h3>
<p>A couple of weeks earlier, I suddenly found myself with $1000 in Claude Web credits and just one week to burn them. I decided to use the free money for the greater good: push AI as far as it could and try to improve existing open-source projects.</p>
<p>So I started hacking.</p>
<p>I tried adding <a rel="external" href="https://github.com/ocaml/ocaml/pull/14353">REPL history navigation and editing</a> to <a rel="external" href="https://ocaml.org/manual/5.4/debugger.html">ocamldebug</a>, the OCaml bytecode debugger. It went surprisingly well — except I missed that line-based breakpoints exist already.</p>
<p>Then I tried adding native binary debugging to OCaml. That also worked. Yes, my implementation is fully working. Yes, it’s tested!</p>
<p>Yes, <a rel="external" href="https://github.com/ocaml/ocaml/pull/14369">dumping a huge PR on unsuspecting OCaml maintainers</a> was a stunt and I regret it now. I also apologize for burdening the OCaml maintainers with it!</p>
<p>I should have started a discussion or <a rel="external" href="https://github.com/joelreymont/zig/pull/1">opened a tiny PR against your own repo first</a> to validate the idea, like I did with Zig.</p>
<p>Except that experiment didn’t go well either.</p>
<p>I spent two weeks building a non-LLVM ARM64 backend for Zig… only to discover such a backend already existed. Then I found a small compiler bug, wrote a minimal patch, included detailed examples—and got slapped down because Zig has a strict <em>no-LLM contributions</em> policy.</p>
<p>The moral: <a href="https://joel.id/build-your-dreams/">go play in your own playground</a>!</p>
<h3 id="growing-compilers">Growing compilers</h3>
<p>Some insist that AI “steals” rather than creates. For example, someone claimed my DWARF debugging support was copied from OxCaml. I <a rel="external" href="https://github.com/ocaml/ocaml/pull/14369#issuecomment-3556624486">disagree</a> — and I think the premise doesn’t matter. Humans copy each other’s code constantly. Writers read books to write new books. Training on existing code is no different.</p>
<p>AI <em>can</em> write a compiler using the knowledge it’s trained on.</p>
<p>I’m putting money where my mouth is: I’m working on two such projects now:</p>
<ul>
<li>A Common Lisp compiler targeting embedded and bare-metal environments.</li>
<li>A source-to-source compiler from Ghidra’s <a rel="external" href="https://ghidra.re/ghidra_docs/languages/html/sleigh.html">Sleigh</a> to Rust plugins for <a rel="external" href="https://binary.ninja">Binary Ninja</a>.</li>
</ul>
<p>So what does it look like when a model writes a Lisp compiler?</p>
<p>Pretty damn sentient.</p>
<p>It figured out that it’s missing <code>labels</code> a feature of the language and that it needs to implement them.</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>⏺ I can confirm that labels is not currently supported in the bootstrap compiler.</span></span>
<span class="giallo-l"><span>   The compiler itself uses labels heavily (because it runs on SBCL), but when</span></span>
<span class="giallo-l"><span>  compiling Habu source code, labels is not handled. I need to implement labels</span></span>
<span class="giallo-l"><span>  support.</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>  Let me first understand what forms are already supported to see what patterns I</span></span>
<span class="giallo-l"><span>   can follow. The closest analogs are LET/LET* and lambda/funcall. Let me look</span></span>
<span class="giallo-l"><span>  at how defun is handled to understand the function environment:</span></span></code></pre>
<p>It knows how to search through existing code.</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>⏺ Search(pattern: &quot;defun&quot;, path: &quot;bootstrap/compiler.lisp&quot;, output_mode: &quot;content&quot;)</span></span>
<span class="giallo-l"><span>  ⎿  Found 100 lines (ctrl+o to expand)</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>⏺ Let me look at the compile-forms function to understand how defun is handled at</span></span>
<span class="giallo-l"><span>   the top level:</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>⏺ Search(pattern: &quot;nc-compile-forms&quot;, path: &quot;bootstrap/compiler.lisp&quot;, output_mode:</span></span>
<span class="giallo-l"><span>        &quot;content&quot;)</span></span>
<span class="giallo-l"><span>  ⎿  Found 100 lines (ctrl+o to expand)</span></span></code></pre>
<p>And it knows how to put two and two together, e.g. that labels are local recursive functions.</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>⏺ Now I understand the structure. Let me look at how lambda captures free</span></span>
<span class="giallo-l"><span>  variables and how funcall works, since labels is essentially local recursive</span></span>
<span class="giallo-l"><span>  functions:</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>⏺ Read(bootstrap/compiler.lisp)</span></span>
<span class="giallo-l"><span>  ⎿  Read 100 lines</span></span></code></pre>
<p>And here’s the kicker: it knows how to simplify labels transform them into something it has already implemented!</p>
<pre class="giallo z-code"><code data-lang="plain"><span class="giallo-l"><span>⏺ I see how lambda and funcall work. For labels, I can transform it into nested</span></span>
<span class="giallo-l"><span>  lambdas with mutual recursion. The key insight is:</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>  (labels ((fn1 (args...) body1)</span></span>
<span class="giallo-l"><span>           (fn2 (args...) body2))</span></span>
<span class="giallo-l"><span>    main-body)</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>  Can be transformed into letrec-style bindings using closures. The standard</span></span>
<span class="giallo-l"><span>  approach is to use a Y-combinator-like technique or to use mutation (setq) to</span></span>
<span class="giallo-l"><span>  tie the knot.</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>  Let me implement labels by transforming it into a let/setq pattern that</span></span>
<span class="giallo-l"><span>  establishes the recursive bindings:</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>  (labels ((fact (n) (if (= n 0) 1 (* n (fact (- n 1))))))</span></span>
<span class="giallo-l"><span>    (fact 5))</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>  becomes:</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>  (let ((fact nil))</span></span>
<span class="giallo-l"><span>    (setq fact (lambda (n) (if (= n 0) 1 (* n (funcall fact (- n 1))))))</span></span>
<span class="giallo-l"><span>    (funcall fact 5))</span></span></code></pre><h3 id="where-this-is-going">Where this is going</h3>
<p>My prediction: <strong>AI will write your next compiler.</strong></p>
<p>Existing projects will struggle to accept AI contributions — not because AI is incapable, but because our development process isn’t ready for it. Reviewing large AI-generated code changes is uncomfortable, foreign, and time-consuming.</p>
<p>But a brand-new compiler? A greenfield project?<br>AI will thrive there.</p>
<p>Working with AI today is more <em>art</em> than science. You have to guide it, correct it, and force it to confront its own design decisions.</p>
<ul>
<li>AI: My tests are slow because I’m rebuilding the whole stdlib` all the time.</li>
<li>Me: Why are you rebuilding it all the time?</li>
<li>AI: Because I have no caching of MIR, etc. code and no incremental compilation. And because I’m rebuilding <code>compiler_rt </code>for every test.</li>
<li>Me: Well, then focus on caching of build artifacts and incremental completion to speed up your damn tests!</li>
</ul>
<h3 id="a-glimpse-of-what-s-coming">A glimpse of what’s coming</h3>
<p>Eventually, we’ll build the infrastructure to let AI write code without human micromanagement. You review assembly emitted by your existing compiler — you run it, test it, benchmark it, and trust the toolchain.</p>
<p>AI-written code will be no different.</p>
<p>If it:</p>
<ul>
<li>meets your requirements</li>
<li>passes your test suite</li>
<li>introduces no regressions</li>
<li>performs within expected bounds</li>
</ul>
<p>…why would you care how it made the sausage?</p>
<p><strong>P.S.</strong> If you enjoyed this, you may also like:<br>👉 <a href="https://joel.id/build-your-dreams/"><em>Build Your Dreams</em></a></p>
]]></description>
    </item>
    <item>
      <title>Build your dreams!</title>
      <link>https://joel.id/build-your-dreams/</link>
      <guid isPermaLink="true">https://joel.id/build-your-dreams/</guid>
      <pubDate>Tue, 25 Nov 2025 09:30:32 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator><description><![CDATA[<p>Discuss on <a rel="external" href="https://news.ycombinator.com/item?id=46044056">Hacker News</a>, <a rel="external" href="https://x.com/joelreymont/status/1993251055484973313?s=20">Twitter</a> or <a rel="external" href="https://www.reddit.com/r/programming/comments/1p687mx/build_your_dreams/">Reddit</a>.  Also, <a href="https://joel.id/resume/">consider hiring me</a>!</p>
<p>You might also enjoy <a href="https://joel.id/ai-will-write-your-next-compiler/">AI will write your next compiler!</a></p>
<p>I started coding over 20 years ago — writing BASIC on my lap during a 1.5-hour bus ride from <a rel="external" href="https://maps.app.goo.gl/jthBdk4onRKWoxU77">Havana</a> (where I went to school) back to <a rel="external" href="https://maps.app.goo.gl/jAfjwni843qkgzQv6">Santa Cruz del Norte</a>. Seats were usually taken, so I coded while sitting on the wheel cover. Not ideal ergonomics, but perfect motivation.</p>
<p>I got paid to program about four years later — my first $300 for breaking the copyright protection on a medical system. Different country, different time.</p>
<p>I’ve never touched Fortran or COBOL, but I’ve programmed in almost everything else at least once. And some languages I keep coming back to again and again (I’m looking at you, Forth and Lisp).</p>
<p>I’ve written trading systems and device drivers. I started a blockchain company and ran it into the ground. I’ve been around long enough to have bought the t-shirt <em>and</em> the sticker pack.</p>
<p>Have I established my bona fides yet? If not, you can browse <a href="https://joel.id/resume/">my resume</a> and <a rel="external" href="https://github.com/joelreymont/">open-source work</a>.</p>
<h2 id="the-craft">The Craft</h2>
<p>I care about the craft. I’ve spent decades honing it. Recently I wrote a <a rel="external" href="https://github.com/HexRaysSA/sleigh-compiler?ref=joel.id">transpiler from Ghidra’s Sleigh to Rust/C++</a>. I first attempted it in Smalltalk (don’t ask — aborted), then in Zig, then <a rel="external" href="https://github.com/joelreymont/bebop-caml">OCaml</a>, then Rust, just to find the most elegant expression. Rust pleasantly surprised me once I gave up on arenas and embraced <code>Rc&lt;RefCell&lt;T&gt;&gt;</code>. And OCaml still rules the compiler-development world.</p>
<p>Maybe I care too much about the process and not enough about the outcome.</p>
<h2 id="a-life-of-dreams">A Life of Dreams</h2>
<p>At 51, in the second half of my life, I want to leave a visible trace — something <em>delivered</em> into the world. I’m self-taught (skipped college), always learning, but delivery matters more than perfect process.</p>
<p>I’m an average developer living an average life — wartime Kyiv notwithstanding. I have a wife and two daughters and only so many hours each day. Contracting sometimes leaves me with gaps to experiment, but my day jobs rarely give me enough time for the strange hobby projects I dream about.</p>
<p>And my dreams are definitely off the beaten path:</p>
<ul>
<li>Why has the <a rel="external" href="https://s-graphics.neocities.org/">Nichimen Mirai</a> story never been repeated?</li>
<li>Why should I pick Rust simply because it has <a rel="external" href="https://github.com/lalrpop/lalrpop">LALRPOP</a>, while Zig doesn’t?</li>
<li>Why can’t I run lifelike drone simulations in Unreal Engine but use Julia instead of Python?</li>
<li>Why does the ML world insist on Python anyway? Why can't I use Julia?</li>
</ul>
<p>I want to build a knowledge base that lives <em>on top of</em> the codebase — without littering someone else’s source code with comments.</p>
<p>I want <a rel="external" href="https://github.com/ocaml/ocaml/pull/14350">keyboard navigation and history editing in ocamldebug</a>.<br>I want easier native OCaml debugging on macOS.<br>I want a super-fast non-LLVM Zig backend for ARM64.<br>I want tiny, deployable Julia binaries for microcontrollers and embedded Linux.</p>
<p>I love what Naughty Dog and Andy Gavin did with <a rel="external" href="https://www.reddit.com/r/programming/comments/bm9p5e/goal_is_a_compiled_lisp_dialect_created_by/">GOAL</a>.<br>I want Lisp running on drones with remote hot-patching. But no existing Lisp lets me develop on macOS and deploy to ARM64 embedded or bare-metal targets.</p>
<h2 id="why-ai-changed-everything">Why AI Changed Everything</h2>
<p>All of this felt like a pipe dream — until now.</p>
<p>Nowadays, I’m so excited that I wake up at 4–5 a.m. <a href="https://joel.id/artisanal-coding-is-dead-long-live-artisanal-coding/">just to code using AI</a>. I can go all day, non-stop. Every new model release feels like Christmas morning: <em>What can it do? What can I do with it?</em></p>
<p>I’m a college drop-out and an autodidact. AI can teach me concepts I thought were too hard to learn. AI can dig into a complex codebase on my behalf, distill it into parts, and tell me how they relate to each other. AI can tell me if my code sucks and how I can improve it. I can <a rel="external" href="https://x.com/geoffreylitt/status/1991909304085987366?s=20">combine AI with artisanal coding</a> if I have time (Hi <a rel="external" href="https://x.com/geoffreylitt">Geoffrey Litt</a>!).</p>
<p>This is the time when any motivated developer can explore languages, compilers, and entire systems for pennies. I realized I can <em>actually build</em> everything I’ve been dreaming about. No permission required. No gatekeepers. Just work.</p>
<p>The only thing standing between me and the things I want to create is my own <strong>laziness</strong>.</p>
<p>And the same goes for you.</p>
<p><strong>Build your dreams.</strong></p>
<p>And <a rel="external" href="https://x.com/joelreymont">follow me</a> on Twitter!</p>
<p><em>P.S. Pushing my ambitions onto unsuspecting open-source communities was a mistake I won’t repeat. The best playground is always your own project.</em></p>
]]></description>
    </item>
    <item>
      <title>Artisanal Coding Is Dead, Long Live Artisanal Coding!</title>
      <link>https://joel.id/artisanal-coding-is-dead-long-live-artisanal-coding/</link>
      <guid isPermaLink="true">https://joel.id/artisanal-coding-is-dead-long-live-artisanal-coding/</guid>
      <pubDate>Thu, 13 Nov 2025 13:15:25 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator><description><![CDATA[<p>Discuss on <a rel="external" href="https://news.ycombinator.com/item?id=45914635">Hacker News</a> or <a rel="external" href="https://x.com/joelreymont/status/1988959112676381030?s=20">Twitter</a>.</p>
<p><a href="https://joel.id/build-your-dreams/">Build your dreams</a> and <a href="https://joel.id/resume/">consider hiring me</a>!</p>
<p>You might also enjoy <a href="https://joel.id/ai-will-write-your-next-compiler/">AI will write your next compiler!</a></p>
<p>I recently discovered that I possess certain AI-assisted <em>superpowers</em>: I can now implement <em>working</em> features — the ones I actually need and want — far faster than if I coded them <em>artisanally</em>, and with no loss in quality. I literally feel 10× more productive!</p>
<p>Take, for example, command history browsing, editing, and tab completion in <code>ocamldebug</code>, the bytecode debugger in the <a rel="external" href="https://ocaml.org">OCaml</a> ecosystem. I’m talking about navigating through command history with arrow keys, jumping to the start of a line with <code>Ctrl-A</code> and to the end with <code>Ctrl-E</code>, and getting info breakpoints by typing <code>&lt;i&gt;&lt;TAB&gt;</code> or <code>&lt;br&gt;&lt;TAB&gt;</code>.</p>
<p>These are long-standing features in <code>lldb</code> and <code>gdb</code>, provided by <code>libreadline</code>. And no — using <code>rlwrap</code> does <em>not</em> provide the same experience.</p>
<p>Just last week, I wouldn’t have dreamed of implementing such a feature. But a few days ago, I did exactly that. <a rel="external" href="https://github.com/ocaml/ocaml/pull/14353">This PR</a> was <em>entirely</em> AI-generated over the course of 2–3 days. Can you tell?</p>
<p>The code is organized into a series of bite-sized commits. I challenge you to browse it and poke holes in the implementation!</p>
<p>It wasn’t done in one fell swoop. I worked on it carefully — from dusk till dawn — because, honestly, it was <em>fun</em>. Yes, I have enough <a rel="external" href="https://ocaml.org">OCaml</a> experience (and over 30 years of development under my belt) to ensure that the code is basically what I would have written myself.</p>
<p><strong>How I Did It</strong></p>
<p>I used <strong>Claude Sonnet 4.5</strong> to write the code and <strong>ChatGPT 5</strong> to review it, looping between them until I was satisfied. I started with the web versions of the models, then switched to CLI to refine and polish the work — also because I’m on macOS, and the web models run on Linux <code>x86_64</code>.</p>
<p>At one point, I was almost ready to roll up my sleeves and dive in manually. Claude got stuck on a <a rel="external" href="https://en.wikipedia.org/wiki/Pseudoterminal">PTY</a> issue and started making random changes that didn’t fix the problem. ChatGPT wasn’t much help either.</p>
<p>Then I asked Claude to show me the differences between two code paths — yes, you can ask models to <em>describe code!</em> — and that’s when the breakthrough happened.</p>
<p>Claude, all by itself, added debugging printouts to the problematic section of code. Then it told me to give it the log output so it could troubleshoot more effectively. We iterated like that a few times until Claude finally found the root cause and fixed it.</p>
<p>One caveat, though: even if I didn’t type the code myself, I <em>own</em> it — and it’s my responsibility now.</p>
<p><strong>The Feeling</strong></p>
<p>The process takes time — though it’s still faster than coding <em>by hand</em>. It feels a bit like being a principal engineer guiding a team of mid- to junior-level developers. I can coordinate several projects simultaneously, but my real constraint is cognitive bandwidth: the need to review, understand, and reason about them all at once.</p>
<p>Overall, I feel like a coding god. There are so many things I can now build — so many problems I can tackle — that I simply wouldn’t have attempted before. The sky is, quite literally, my coding limit.</p>
<p>I, for one, welcome our new AI overlords. I don’t care who wrote the code — human or machine — as long as it’s clean and it works.</p>
<p>Do you care who built your favorite software features, or just that they shipped fast and worked great?</p>
<p>Let me know on <a rel="external" href="https://news.ycombinator.com/item?id=45914635">Hacker News</a>, <a rel="external" href="https://x.com/joelreymont/status/1988959112676381030?s=20">Twitter</a> or the <a rel="external" href="https://discuss.ocaml.org/t/artisanal-coding-is-dead-long-live-artisanal-coding/17487">OCaml Forum</a>!</p>
<p><strong>P.S. — Use AI to Learn</strong></p>
<p>Yes, you <em>can</em> and <em>should</em> use AI for learning. I do.</p>
<p>As an autodidact, it suits me perfectly. You can ask AI to explain an unfamiliar codebase — in whole or in part — or to show you variations, optimizations, or tests.</p>
<p>Nothing stands between you and your new learning superpowers!</p>
<p><strong>P.S. — AI will write your next compiler!</strong></p>
<p>I'm working on adding <a rel="external" href="https://dwarfstd.org">DWARF</a> debugging information to the <a rel="external" href="https://ocaml.org">OCaml</a> compiler. Just for kicks and because I don't necessarily want to use <a rel="external" href="https://blog.janestreet.com/introducing-oxcaml/">OxCaml</a> the Jane St fork of OCaml that supports DWARF on the Mac.</p>
<p>DWARF is the information you need to see source code in <code>lldb</code> or <code>gdb</code>, inspect variables, stop at lines of code, etc.</p>
<p>It looks like <a rel="external" href="https://x.com/joelreymont/status/1988948903593402437?s=20">I got it working</a> but I'd like to make absolutely sure before I post about it.</p>
]]></description>
    </item>
    <item>
      <title>Making OCaml recursive modules convenient</title>
      <link>https://joel.id/making-recursive-modules-convenient/</link>
      <guid isPermaLink="true">https://joel.id/making-recursive-modules-convenient/</guid>
      <pubDate>Wed, 18 Jun 2025 05:15:54 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator>
      <category>OCaml</category><description><![CDATA[<p>Jane St posted about <a rel="external" href="https://blog.janestreet.com/a-trick-recursive-modules-from-recursive-signatures/">recursive OCaml modules from recursive signatures</a> a while ago.</p>
<pre class="giallo z-code"><code data-lang="ocaml"><span class="giallo-l"><span class="z-support z-constant z-property-value">module</span><span class="z-string z-regexp"> rec</span><span class="z-entity z-name z-function"> Even</span><span class="z-string z-regexp"> :</span><span> sig</span></span>
<span class="giallo-l"><span>  </span><span class="z-keyword">type</span><span class="z-entity z-name z-function"> t</span><span> =</span><span class="z-entity z-other z-attribute-name z-id"> Zero</span><span> |</span><span class="z-entity z-other z-attribute-name z-id"> Succ</span><span> of</span><span class="z-constant z-numeric"> Odd</span><span class="z-keyword">.</span><span>t</span></span>
<span class="giallo-l"><span>end =</span><span class="z-constant z-numeric"> Even</span></span>
<span class="giallo-l"><span class="z-string z-regexp">and</span><span class="z-entity z-name z-function"> Odd</span><span class="z-string z-regexp"> :</span><span> sig</span></span>
<span class="giallo-l"><span>  </span><span class="z-keyword">type</span><span class="z-entity z-name z-function"> t</span><span> =</span><span class="z-entity z-other z-attribute-name z-id"> Succ</span><span> of</span><span class="z-constant z-numeric"> Even</span><span class="z-keyword">.</span><span>t</span></span>
<span class="giallo-l"><span>end =</span><span class="z-constant z-numeric"> Odd</span></span></code></pre>
<p>You cannot have functions inside recursive modules defined this way which is really inconvenient. You can bring the convenience back with just a couple of wrappers, though!</p>
<pre class="giallo z-code"><code data-lang="ocaml"><span class="giallo-l"><span class="z-support z-constant z-property-value">module</span><span class="z-string z-regexp"> rec</span><span class="z-entity z-name z-function"> Even&#39;</span><span class="z-string z-regexp"> :</span><span> sig</span></span>
<span class="giallo-l"><span>  </span><span class="z-keyword">type</span><span class="z-entity z-name z-function"> t</span><span> =</span><span class="z-entity z-other z-attribute-name z-id"> Zero</span><span> |</span><span class="z-entity z-other z-attribute-name z-id"> Succ</span><span> of</span><span class="z-constant z-numeric"> Odd</span><span class="z-keyword">.</span><span>t</span></span>
<span class="giallo-l"><span>end =</span><span class="z-constant z-numeric"> Even&#39;</span></span>
<span class="giallo-l"><span class="z-string z-regexp">and</span><span class="z-entity z-name z-function"> Odd&#39;</span><span class="z-string z-regexp"> :</span><span> sig</span></span>
<span class="giallo-l"><span>  </span><span class="z-keyword">type</span><span class="z-entity z-name z-function"> t</span><span> =</span><span class="z-entity z-other z-attribute-name z-id"> Succ</span><span> of</span><span class="z-constant z-numeric"> Even</span><span class="z-keyword">.</span><span>t</span></span>
<span class="giallo-l"><span>end =</span><span class="z-constant z-numeric"> Odd&#39;</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-support z-constant z-property-value">module</span><span class="z-entity z-name z-function"> Even</span><span> = struct</span></span>
<span class="giallo-l"><span class="z-string z-regexp">  include</span><span class="z-constant z-numeric"> Even&#39;</span></span>
<span class="giallo-l"><span class="z-constant">  (* put your functions here *)</span></span>
<span class="giallo-l"><span>end</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-support z-constant z-property-value">module</span><span class="z-entity z-name z-function"> Odd</span><span> = struct</span></span>
<span class="giallo-l"><span class="z-string z-regexp">  include</span><span class="z-constant z-numeric"> Odd&#39;</span></span>
<span class="giallo-l"><span class="z-constant">  (* put your functions here *)</span></span>
<span class="giallo-l"><span>end</span></span></code></pre>
<p>Then just use the <code>Even</code> and <code>Odd</code> modules going forward.</p>
<p>Why bother with rec.modules in the first place? Compiler abstract syntax trees (ASTs) are recursive, e.g. expressions depend on themselves. Recursive modules are great to wrap an AST!</p>
]]></description>
    </item>
    <item>
      <title>Configuring Jujitsu (jj)</title>
      <link>https://joel.id/configuring-jujitsu-jj/</link>
      <guid isPermaLink="true">https://joel.id/configuring-jujitsu-jj/</guid>
      <pubDate>Mon, 26 May 2025 16:01:00 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator>
      <category>jj</category>
      <category>vcs</category><description><![CDATA[<p>This is my <a rel="external" href="https://github.com/jj-vcs/jj">Jujitsu (jj)</a> repo config template. I usually symlink it to <code>.jj/repo/config.toml</code> and check it in.<br><br>I then <code>jj sync &amp;&amp; jj evolve</code> where using git I would <code>git pull --rebase</code>.<br><br>And jj is lovely... but for having to <code>jj b s master -r @-</code> all the time before pushing, often with <code>--allow-backwards</code>. I think it's a feature, though, and not a bug!</p>
<pre class="giallo z-code"><code data-lang="toml"><span class="giallo-l"><span>[</span><span class="z-entity z-name z-section">aliases</span><span>]</span></span>
<span class="giallo-l"><span class="z-variable">ci</span><span class="z-punctuation z-separator z-key-value"> = [</span><span class="z-string">&#39;commit&#39;</span><span>]</span></span>
<span class="giallo-l"><span class="z-variable">push</span><span class="z-punctuation z-separator z-key-value"> = [</span><span class="z-string">&#39;git&#39;</span><span>,</span><span class="z-string"> &#39;push&#39;</span><span>]</span></span>
<span class="giallo-l"><span class="z-variable">sync</span><span class="z-punctuation z-separator z-key-value"> = [</span><span class="z-string">&#39;git&#39;</span><span>,</span><span class="z-string"> &#39;fetch&#39;</span><span>,</span><span class="z-string"> &#39;--all-remotes&#39;</span><span>]</span></span>
<span class="giallo-l"><span class="z-variable">evolve</span><span class="z-punctuation z-separator z-key-value"> = [</span><span class="z-string">&#39;rebase&#39;</span><span>,</span><span class="z-string"> &#39;--skip-emptied&#39;</span><span>,</span><span class="z-string"> &#39;-d&#39;</span><span>,</span><span class="z-string"> &#39;master&#39;</span><span>]</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span>[</span><span class="z-entity z-name z-section">snapshot</span><span>]</span></span>
<span class="giallo-l"><span class="z-variable">max-new-file-size</span><span class="z-punctuation z-separator z-key-value"> =</span><span class="z-constant z-numeric"> 1479557</span></span></code></pre>]]></description>
    </item>
    <item>
      <title>Configuring Helix to auto-format Haskell code</title>
      <link>https://joel.id/configuring-helix-to-auto-format-haskell-code/</link>
      <guid isPermaLink="true">https://joel.id/configuring-helix-to-auto-format-haskell-code/</guid>
      <pubDate>Mon, 26 May 2025 08:06:00 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator>
      <category>Helix</category>
      <category>Haskell</category><description><![CDATA[<p>This is how you configure Helix to auto-format Haskell code.</p>
<p>Took me a lot of googling and then reading the manual so I hope you find it useful!</p>
<pre class="giallo z-code"><code data-lang="toml"><span class="giallo-l"><span>[[</span><span class="z-entity z-name z-section">language</span><span>]]</span></span>
<span class="giallo-l"><span class="z-variable">name</span><span class="z-punctuation z-separator z-key-value"> =</span><span class="z-string"> &quot;haskell&quot;</span></span>
<span class="giallo-l"><span class="z-variable">roots</span><span class="z-punctuation z-separator z-key-value"> = [</span><span class="z-string">&quot;Setup.hs&quot;</span><span>,</span><span class="z-string"> &quot;stack.yaml&quot;</span><span>,</span><span class="z-string"> &quot;*.cabal&quot;</span><span>]</span></span>
<span class="giallo-l"><span class="z-variable">formatter</span><span class="z-punctuation z-separator z-key-value"> = {</span><span class="z-variable">command</span><span class="z-punctuation z-separator z-key-value"> =</span><span class="z-string"> &#39;fourmolu&#39;</span><span>,</span><span class="z-variable"> args</span><span class="z-punctuation z-separator z-key-value"> = [</span><span class="z-string">&quot;--stdin-input-file&quot;</span><span>,</span><span class="z-string"> &quot;%{buffer_name}&quot;</span><span>]}</span></span>
<span class="giallo-l"><span class="z-variable">auto-format</span><span class="z-punctuation z-separator z-key-value"> =</span><span class="z-constant"> true</span></span></code></pre>]]></description>
    </item>
    <item>
      <title>Zig type hackery and memory management</title>
      <link>https://joel.id/zig-type-hacker-and-memory-management/</link>
      <guid isPermaLink="true">https://joel.id/zig-type-hacker-and-memory-management/</guid>
      <pubDate>Tue, 20 May 2025 05:05:00 +0000</pubDate>
      <dc:creator>Joel Reymont</dc:creator>
      <category>Zig</category><description><![CDATA[<p>You can map and fold in Zig but lack of closures makes it unergonomic. Here's a completely silly example that shows Zig type hackery as well as memory management.</p>
<pre class="giallo z-code"><code data-lang="zig"><span class="giallo-l"><span class="z-keyword">const</span><span class="z-variable"> std</span><span class="z-keyword z-operator z-assignment z-support z-function"> = @import</span><span>(</span><span class="z-string">&quot;std&quot;</span><span>);</span></span>
<span class="giallo-l"><span class="z-keyword">const</span><span class="z-variable"> mem</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> std</span><span>.</span><span class="z-variable">mem</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword">const</span><span class="z-variable"> testing</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> std</span><span>.</span><span class="z-variable">testing</span><span>;</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">const</span><span class="z-variable"> Allocator</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> mem</span><span>.</span><span class="z-variable">Allocator</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword">pub const</span><span class="z-variable"> Error</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> Allocator</span><span>.</span><span class="z-variable">Error</span><span class="z-keyword z-operator z-bitwise"> ||</span><span class="z-keyword"> error</span><span>{};</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">pub const</span><span class="z-variable"> Expr</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> union</span><span>(</span><span class="z-keyword">enum</span><span>) {</span></span>
<span class="giallo-l"><span class="z-variable">    binary</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> Binary</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">    int</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword"> usize</span><span>,</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">    pub const</span><span class="z-variable"> Binary</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> struct</span><span> {</span></span>
<span class="giallo-l"><span class="z-variable">        op</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> BinaryOp</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">        lhs</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">        rhs</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>,</span></span>
<span class="giallo-l"><span>    };</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">    pub const</span><span class="z-variable"> BinaryOp</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> enum</span><span> {</span></span>
<span class="giallo-l"><span class="z-variable">        @&quot;+&quot;</span><span>,</span></span>
<span class="giallo-l"><span>    };</span></span>
<span class="giallo-l"><span>};</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword z-storage">pub fn</span><span class="z-entity z-name z-function"> exprWalker</span><span>(</span><span class="z-variable">T</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword"> type</span><span>,</span><span class="z-variable"> E</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword"> type</span><span>) </span><span class="z-keyword">type</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword z-control z-keyword">    return struct</span><span> {</span></span>
<span class="giallo-l"><span class="z-variable">        context</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> T</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">        walker</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> Walker</span><span>,</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">        const</span><span class="z-variable"> Self</span><span class="z-keyword z-operator z-assignment z-support z-function"> = @This</span><span>();</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">        pub const</span><span class="z-variable"> Walker</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-arithmetic"> = *</span><span class="z-keyword">const fn</span><span> (</span><span class="z-keyword z-operator z-arithmetic">*</span><span class="z-variable">Self</span><span>,</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>) </span><span class="z-variable">E</span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">bool</span><span>;</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword z-storage">        pub fn</span><span class="z-entity z-name z-function"> run</span><span>(</span><span class="z-variable">self</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Self</span><span>,</span><span class="z-variable"> node</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>) </span><span class="z-variable">E</span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">void</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword z-control">            if</span><span> (</span><span class="z-keyword z-control">try</span><span class="z-variable"> self</span><span>.</span><span class="z-entity z-name z-function">walker</span><span>(</span><span class="z-variable">self</span><span>,</span><span class="z-variable"> node</span><span>))</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                return</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword z-control">            switch</span><span> (</span><span class="z-variable">node</span><span>.</span><span class="z-keyword z-operator z-arithmetic">*</span><span>) {</span></span>
<span class="giallo-l"><span>                .</span><span class="z-variable">binary</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-comparison z-keyword z-operator z-bitwise"> =&gt; |</span><span class="z-variable">binary</span><span class="z-keyword z-operator z-bitwise">|</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                    try</span><span class="z-variable"> self</span><span>.</span><span class="z-entity z-name z-function">run</span><span>(</span><span class="z-variable">binary</span><span>.</span><span class="z-variable">lhs</span><span>);</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                    try</span><span class="z-variable"> self</span><span>.</span><span class="z-entity z-name z-function">run</span><span>(</span><span class="z-variable">binary</span><span>.</span><span class="z-variable">rhs</span><span>);</span></span>
<span class="giallo-l"><span>                },</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                else</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-comparison"> =&gt;</span><span> {},</span></span>
<span class="giallo-l"><span>            }</span></span>
<span class="giallo-l"><span>        }</span></span>
<span class="giallo-l"><span>    };</span></span>
<span class="giallo-l"><span>}</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-storage">fn</span><span class="z-entity z-name z-function"> sumup</span><span>(</span><span class="z-variable">node</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>) </span><span class="z-variable">Error</span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">usize</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> Context</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> usize</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> Walker</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-entity z-name z-function"> exprWalker</span><span>(</span><span class="z-variable">Context</span><span>,</span><span class="z-variable"> Error</span><span>);</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> walk</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> struct</span><span> {</span></span>
<span class="giallo-l"><span class="z-storage">        fn</span><span class="z-entity z-name z-function"> walk</span><span>(</span><span class="z-variable">self</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Walker</span><span>,</span><span class="z-variable"> expr</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>) </span><span class="z-variable">Error</span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">bool</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword z-control">            switch</span><span> (</span><span class="z-variable">expr</span><span>.</span><span class="z-keyword z-operator z-arithmetic">*</span><span>) {</span></span>
<span class="giallo-l"><span>                .</span><span class="z-variable">int</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-comparison z-keyword z-operator z-bitwise"> =&gt; |</span><span class="z-variable">i</span><span class="z-keyword z-operator z-bitwise">|</span><span> {</span></span>
<span class="giallo-l"><span class="z-variable">                    self</span><span>.</span><span class="z-variable">context</span><span class="z-keyword z-operator z-arithmetic"> +=</span><span class="z-variable"> i</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword z-control z-keyword">                    return true</span><span>;</span></span>
<span class="giallo-l"><span>                },</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                else</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-comparison"> =&gt;</span><span> {},</span></span>
<span class="giallo-l"><span>            }</span></span>
<span class="giallo-l"><span class="z-keyword z-control z-keyword">            return false</span><span>;</span></span>
<span class="giallo-l"><span>        }</span></span>
<span class="giallo-l"><span>    }.</span><span class="z-variable">walk</span><span>;</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">    var</span><span class="z-variable"> walker</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> Walker</span><span>{</span></span>
<span class="giallo-l"><span>        .</span><span class="z-variable">context</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-constant z-numeric"> 0</span><span>,</span></span>
<span class="giallo-l"><span>        .</span><span class="z-variable">walker</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> walk</span><span>,</span></span>
<span class="giallo-l"><span>    };</span></span>
<span class="giallo-l"><span class="z-keyword z-control">    try</span><span class="z-variable"> walker</span><span>.</span><span class="z-entity z-name z-function">run</span><span>(</span><span class="z-variable">node</span><span>);</span></span>
<span class="giallo-l"><span class="z-keyword z-control">    return</span><span class="z-variable"> walker</span><span>.</span><span class="z-variable">context</span><span>;</span></span>
<span class="giallo-l"><span>}</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword z-storage">pub fn</span><span class="z-entity z-name z-function"> clone</span><span>(</span><span class="z-variable">alloc</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> Allocator</span><span>,</span><span class="z-variable"> value</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword"> anytype</span><span>) </span><span class="z-variable">Error</span><span class="z-keyword z-operator z-bitwise z-keyword z-operator z-arithmetic z-support z-function">!*@TypeOf</span><span>(</span><span class="z-variable">value</span><span>) {</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> result</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword z-control"> try</span><span class="z-variable"> alloc</span><span>.</span><span class="z-entity z-name z-function">create</span><span>(</span><span class="z-support z-function">@TypeOf</span><span>(</span><span class="z-variable">value</span><span>));</span></span>
<span class="giallo-l"><span class="z-variable">    result</span><span>.</span><span class="z-keyword z-operator z-arithmetic z-keyword z-operator z-assignment">* =</span><span class="z-variable"> value</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword z-control">    return</span><span class="z-variable"> result</span><span>;</span></span>
<span class="giallo-l"><span>}</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword z-storage">pub fn</span><span class="z-entity z-name z-function"> main</span><span>() </span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">void</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> page_alloc</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> std</span><span>.</span><span class="z-variable">heap</span><span>.</span><span class="z-variable">page_allocator</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword">    var</span><span class="z-variable"> arena</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> std</span><span>.</span><span class="z-variable">heap</span><span>.</span><span class="z-variable">ArenaAllocator</span><span>.</span><span class="z-entity z-name z-function">init</span><span>(</span><span class="z-variable">page_alloc</span><span>);</span></span>
<span class="giallo-l"><span class="z-keyword z-control">    defer</span><span class="z-variable"> arena</span><span>.</span><span class="z-entity z-name z-function">deinit</span><span>();</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> alloc</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> arena</span><span>.</span><span class="z-entity z-name z-function">allocator</span><span>();</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> lhs</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> Expr</span><span>{ .</span><span class="z-variable">int</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-constant z-numeric"> 1</span><span> };</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> rhs</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> Expr</span><span>{ .</span><span class="z-variable">int</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-constant z-numeric"> 2</span><span> };</span></span>
<span class="giallo-l"><span class="z-keyword">    var</span><span class="z-variable"> expr</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> Expr</span><span>{</span></span>
<span class="giallo-l"><span>        .</span><span class="z-variable">binary</span><span class="z-keyword z-operator z-assignment"> =</span><span> .{</span></span>
<span class="giallo-l"><span>            .</span><span class="z-variable">op</span><span class="z-keyword z-operator z-assignment"> =</span><span> .</span><span class="z-variable">@&quot;+&quot;</span><span>,</span></span>
<span class="giallo-l"><span>            .</span><span class="z-variable">lhs</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword z-control"> try</span><span class="z-entity z-name z-function"> clone</span><span>(</span><span class="z-variable">alloc</span><span>,</span><span class="z-variable"> lhs</span><span>),</span></span>
<span class="giallo-l"><span>            .</span><span class="z-variable">rhs</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword z-control"> try</span><span class="z-entity z-name z-function"> clone</span><span>(</span><span class="z-variable">alloc</span><span>,</span><span class="z-variable"> rhs</span><span>),</span></span>
<span class="giallo-l"><span>        },</span></span>
<span class="giallo-l"><span>    };</span></span>
<span class="giallo-l"><span class="z-variable">    std</span><span>.</span><span class="z-variable">debug</span><span>.</span><span class="z-entity z-name z-function">print</span><span>(</span><span class="z-string">&quot;sum = {d}</span><span class="z-constant z-character z-escape">\n</span><span class="z-string">&quot;</span><span>, .{</span><span class="z-keyword z-control">try</span><span class="z-entity z-name z-function"> sumup</span><span>(</span><span class="z-keyword z-operator z-bitwise">&amp;</span><span class="z-variable">expr</span><span>)});</span></span>
<span class="giallo-l"><span>}</span></span></code></pre>
<p>Let's take it apart...</p>
<p>Recursive data structures require pointers so both left-hand side (<code>lhs</code>) and right-hand side (<code>rhs</code>) of the expression need to be pointers! Also, <a rel="external" href="https://zig.guide/language-basics/unions/">tagged unions</a> (see union enum) are analogous to <a rel="external" href="https://dev.realworldocaml.org/variants.html">OCaml variants</a>.</p>
<pre class="giallo z-code"><code data-lang="zig"><span class="giallo-l"><span class="z-keyword">pub const</span><span class="z-variable"> Expr</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> union</span><span>(</span><span class="z-keyword">enum</span><span>) {</span></span>
<span class="giallo-l"><span class="z-variable">    binary</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> Binary</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">    int</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword"> usize</span><span>,</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">    pub const</span><span class="z-variable"> Binary</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> struct</span><span> {</span></span>
<span class="giallo-l"><span class="z-variable">        op</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> BinaryOp</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">        lhs</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">        rhs</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>,</span></span>
<span class="giallo-l"><span>    };</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">    pub const</span><span class="z-variable"> BinaryOp</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> enum</span><span> {</span></span>
<span class="giallo-l"><span class="z-variable">        @&quot;+&quot;</span><span>,</span></span>
<span class="giallo-l"><span>    };</span></span>
<span class="giallo-l"><span>};</span></span></code></pre>
<p>Memory allocation in Zig is very much explicit! I started my compiler project manually allocating and deallocating everything but quickly went with the <a rel="external" href="https://zig.guide/standard-library/allocators">arena allocator</a>. This lets me free up all the data structures in one fell swoop once the compiler exits. <a rel="external" href="https://zig.guide/language-basics/defer">Defer</a> is awesome and will run when we exit the current scope. Think RAII in C++.</p>
<pre class="giallo z-code"><code data-lang="zig"><span class="giallo-l"><span class="z-keyword">const</span><span class="z-variable"> page_alloc</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> std</span><span>.</span><span class="z-variable">heap</span><span>.</span><span class="z-variable">page_allocator</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword">var</span><span class="z-variable"> arena</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> std</span><span>.</span><span class="z-variable">heap</span><span>.</span><span class="z-variable">ArenaAllocator</span><span>.</span><span class="z-entity z-name z-function">init</span><span>(</span><span class="z-variable">page_alloc</span><span>);</span></span>
<span class="giallo-l"><span class="z-keyword z-control">defer</span><span class="z-variable"> arena</span><span>.</span><span class="z-entity z-name z-function">deinit</span><span>();</span></span>
<span class="giallo-l"><span class="z-keyword">const</span><span class="z-variable"> alloc</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> arena</span><span>.</span><span class="z-entity z-name z-function">allocator</span><span>();</span></span></code></pre>
<p>This handy little function will make a copy of any value given to it by first allocating a chunk of memory the size of the given value and then assigning the value to the "dereferenced pointer". <br><br>Zig duck typing is great! Notice that I'm not specifying the type of value (<a rel="external" href="https://reddit.com/r/Zig/comments/11pxp5m/help_a_noob_how_does_anytype_work/">anytype</a>) and telling the Zig compiler to use the type of the value given ( <code>TypeOf(value)</code>)</p>
<pre class="giallo z-code"><code data-lang="zig"><span class="giallo-l"><span class="z-keyword z-storage">pub fn</span><span class="z-entity z-name z-function"> exprWalker</span><span>(</span><span class="z-variable">T</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword"> type</span><span>,</span><span class="z-variable"> E</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword"> type</span><span>) </span><span class="z-keyword">type</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword z-control z-keyword">    return struct</span><span> {</span></span>
<span class="giallo-l"><span class="z-variable">        context</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> T</span><span>,</span></span>
<span class="giallo-l"><span class="z-variable">        walker</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-variable"> Walker</span><span>,</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">        const</span><span class="z-variable"> Self</span><span class="z-keyword z-operator z-assignment z-support z-function"> = @This</span><span>();</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">        pub const</span><span class="z-variable"> Walker</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-arithmetic"> = *</span><span class="z-keyword">const fn</span><span> (</span><span class="z-keyword z-operator z-arithmetic">*</span><span class="z-variable">Self</span><span>,</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>) </span><span class="z-variable">E</span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">bool</span><span>;</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword z-storage">        pub fn</span><span class="z-entity z-name z-function"> run</span><span>(</span><span class="z-variable">self</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Self</span><span>,</span><span class="z-variable"> node</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>) </span><span class="z-variable">E</span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">void</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword z-control">            if</span><span> (</span><span class="z-keyword z-control">try</span><span class="z-variable"> self</span><span>.</span><span class="z-entity z-name z-function">walker</span><span>(</span><span class="z-variable">self</span><span>,</span><span class="z-variable"> node</span><span>))</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                return</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword z-control">            switch</span><span> (</span><span class="z-variable">node</span><span>.</span><span class="z-keyword z-operator z-arithmetic">*</span><span>) {</span></span>
<span class="giallo-l"><span>                .</span><span class="z-variable">binary</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-comparison z-keyword z-operator z-bitwise"> =&gt; |</span><span class="z-variable">binary</span><span class="z-keyword z-operator z-bitwise">|</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                    try</span><span class="z-variable"> self</span><span>.</span><span class="z-entity z-name z-function">run</span><span>(</span><span class="z-variable">binary</span><span>.</span><span class="z-variable">lhs</span><span>);</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                    try</span><span class="z-variable"> self</span><span>.</span><span class="z-entity z-name z-function">run</span><span>(</span><span class="z-variable">binary</span><span>.</span><span class="z-variable">rhs</span><span>);</span></span>
<span class="giallo-l"><span>                },</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                else</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-comparison"> =&gt;</span><span> {},</span></span>
<span class="giallo-l"><span>            }</span></span>
<span class="giallo-l"><span>        }</span></span>
<span class="giallo-l"><span>    };</span></span>
<span class="giallo-l"><span>}</span></span></code></pre>
<p>Zig comptime generics at work here. <br><br>The <code>exprWalker</code> function returns a new type parameterized on the context and error types supplied to it. <br><br>The <code>Walker</code> type is a function that will do the heavy lifting. The expression walking machinery will abruptly stop if the function tells it that this expression has been handled. Otherwise, It will happily recurse into nested expressions. <br><br>Zig pattern matching using <a rel="external" href="https://zig.guide/language-basics/switch/">switch</a> is not <a rel="external" href="https://dev.realworldocaml.org/lists-and-patterns.html">as advanced as in OCaml</a> but perfectly serviceable. Finally, <code>{}</code> is an empty block.</p>
<pre class="giallo z-code"><code data-lang="zig"><span class="giallo-l"><span class="z-storage">fn</span><span class="z-entity z-name z-function"> sumup</span><span>(</span><span class="z-variable">node</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>) </span><span class="z-variable">Error</span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">usize</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> Context</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> usize</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> Walker</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-entity z-name z-function"> exprWalker</span><span>(</span><span class="z-variable">Context</span><span>,</span><span class="z-variable"> Error</span><span>);</span></span>
<span class="giallo-l"><span class="z-keyword">    const</span><span class="z-variable"> walk</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-keyword"> struct</span><span> {</span></span>
<span class="giallo-l"><span class="z-storage">        fn</span><span class="z-entity z-name z-function"> walk</span><span>(</span><span class="z-variable">self</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Walker</span><span>,</span><span class="z-variable"> expr</span><span class="z-punctuation z-separator z-key-value">:</span><span class="z-keyword z-operator z-arithmetic"> *</span><span class="z-variable">Expr</span><span>) </span><span class="z-variable">Error</span><span class="z-keyword z-operator z-bitwise">!</span><span class="z-keyword">bool</span><span> {</span></span>
<span class="giallo-l"><span class="z-keyword z-control">            switch</span><span> (</span><span class="z-variable">expr</span><span>.</span><span class="z-keyword z-operator z-arithmetic">*</span><span>) {</span></span>
<span class="giallo-l"><span>                .</span><span class="z-variable">int</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-comparison z-keyword z-operator z-bitwise"> =&gt; |</span><span class="z-variable">i</span><span class="z-keyword z-operator z-bitwise">|</span><span> {</span></span>
<span class="giallo-l"><span class="z-variable">                    self</span><span>.</span><span class="z-variable">context</span><span class="z-keyword z-operator z-arithmetic"> +=</span><span class="z-variable"> i</span><span>;</span></span>
<span class="giallo-l"><span class="z-keyword z-control z-keyword">                    return true</span><span>;</span></span>
<span class="giallo-l"><span>                },</span></span>
<span class="giallo-l"><span class="z-keyword z-control">                else</span><span class="z-keyword z-operator z-assignment z-keyword z-operator z-comparison"> =&gt;</span><span> {},</span></span>
<span class="giallo-l"><span>            }</span></span>
<span class="giallo-l"><span class="z-keyword z-control z-keyword">            return false</span><span>;</span></span>
<span class="giallo-l"><span>        }</span></span>
<span class="giallo-l"><span>    }.</span><span class="z-variable">walk</span><span>;</span></span>
<span class="giallo-l"></span>
<span class="giallo-l"><span class="z-keyword">    var</span><span class="z-variable"> walker</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> Walker</span><span>{</span></span>
<span class="giallo-l"><span>        .</span><span class="z-variable">context</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-constant z-numeric"> 0</span><span>,</span></span>
<span class="giallo-l"><span>        .</span><span class="z-variable">walker</span><span class="z-keyword z-operator z-assignment"> =</span><span class="z-variable"> walk</span><span>,</span></span>
<span class="giallo-l"><span>    };</span></span>
<span class="giallo-l"><span class="z-keyword z-control">    try</span><span class="z-variable"> walker</span><span>.</span><span class="z-entity z-name z-function">run</span><span>(</span><span class="z-variable">node</span><span>);</span></span>
<span class="giallo-l"><span class="z-keyword z-control">    return</span><span class="z-variable"> walker</span><span>.</span><span class="z-variable">context</span><span>;</span></span>
<span class="giallo-l"><span>}</span></span></code></pre>
<p>Here <code>const Walker = exprWalker(Context, Error);</code> instantiates a new Walker that's parameterized over the <code>Context</code> and <code>Error</code> types. <br><br>The walk function then looks for ints and sums them up, storing the sum in the context. <br><br>There are no closures in Zig but you can sort of kind of emulate them by returning a struct with a function inside it.</p>
<p>That's it!</p>
]]></description>
    </item>
  </channel>
</rss>
