<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/default.xsl"?>
<fr:tree xmlns:fr="http://www.forester-notes.org" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:xml="http://www.w3.org/XML/1998/namespace" root="false" base-url="/">
  <fr:frontmatter>
    <fr:authors>
      <fr:author>
        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
      </fr:author>
    </fr:authors>
    <fr:date>
      <fr:year>2026</fr:year>
      <fr:month>3</fr:month>
      <fr:day>19</fr:day>
    </fr:date>
    <fr:uri>https://patrick.sirref.org/weekly-2026-w12/</fr:uri>
    <fr:display-uri>weekly-2026-w12</fr:display-uri>
    <fr:route>/weekly-2026-w12/</fr:route>
    <fr:title text="A POSIX Shell in OCaml">A POSIX Shell in OCaml</fr:title>
  </fr:frontmatter>
  <fr:mainmatter>
    <html:p>Long time, no weekly. Since the start of this year I have been building a POSIX shell in OCaml called <html:code>msh</html:code> (with the underlying library called <html:code>Merry</html:code>). <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is available online now.</html:p>
    <fr:tree show-metadata="false" numbered="false">
      <fr:frontmatter>
        <fr:authors>
          <fr:author>
            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
          </fr:author>
        </fr:authors>
        <fr:date>
          <fr:year>2026</fr:year>
          <fr:month>3</fr:month>
          <fr:day>19</fr:day>
        </fr:date>
        <fr:title text="A POSIX(ish) shell in OCaml">A POSIX(ish) shell in OCaml</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <html:p><fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is a POSIX(ish) in OCaml. It uses  <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link> alongside <fr:link href="github.com/colis-anr/morbig" type="external">Morbig</fr:link> (a static parser for POSIX shell).</html:p>
        <fr:tree show-metadata="false" numbered="false">
          <fr:frontmatter>
            <fr:authors>
              <fr:author>
                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
              </fr:author>
            </fr:authors>
            <fr:date>
              <fr:year>2026</fr:year>
              <fr:month>3</fr:month>
              <fr:day>19</fr:day>
            </fr:date>
            <fr:title text="Why another (POSIX) shell?">Why another (POSIX) shell?</fr:title>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>Shells have been around for a long time. In my research on the notion of <html:em>metashell</html:em> I wrote about Louis Pouzin originally coining the term:</html:p>
            <fr:tree show-metadata="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2025</fr:year>
                  <fr:month>5</fr:month>
                  <fr:day>19</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/pouzin-shell/</fr:uri>
                <fr:display-uri>pouzin-shell</fr:display-uri>
                <fr:route>/pouzin-shell/</fr:route>
                <fr:title text="Metashells › Louis Pouzin's &quot;SHELL&quot; "><fr:link href="/weekly-2025-05-12/" title="Metashells" uri="https://patrick.sirref.org/weekly-2025-05-12/" display-uri="weekly-2025-05-12" type="local">Metashells</fr:link> › Louis Pouzin's "SHELL" </fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>I spent some time reading <fr:link href="/pouzin-shell-2013/" title="The Origin of the Shell" uri="https://patrick.sirref.org/pouzin-shell-2013/" display-uri="pouzin-shell-2013" type="local">part of the multics design documentation</fr:link> this week. Louis Pouzin coined the term "SHELL" in this document, and I was reminded yet again just how important it is to be a good writer even as a "computer science researcher". For example, this excerpt from the requirements section of the document</html:p>
                <html:blockquote>
                  <html:p>The previous definitions imply that a command MUST be designed while keeping in mind the user, sitting at his console, wondering about what might be going on, mistyping or forgetting arguments, even if fully aware of the conventions, and possibly interfering with the command by hasty quits, carriage returns, and other temperamental reactions.</html:p>
                </html:blockquote>
                <html:p>And then later, when defining the "SHELL".</html:p>
                <html:blockquote>
                  <html:p>We may envision a common procedure called automatically by the supervisor whenever a user types in some message at his console, at a time when he has no other process in active execution under console control (presently called command level). This procedure acts as an interface between console messages and subroutine. The purpose of such a procedure is to create a medium of exchange into which one could activate any procedure, <html:em>inside of another program if it were called</html:em>. Hereafter, for simplification, we shall refer to that procedure as the "SHELL".</html:p>
                </html:blockquote>
                <html:p>It still surprises how little the undergraduate degree in computer science at <fr:link href="/ucam/" title="University of Cambridge" uri="https://patrick.sirref.org/ucam/" display-uri="ucam" type="local">Cambridge</fr:link> focuses on writing skills.</html:p>
              </fr:mainmatter>
            </fr:tree>
            <html:p>I built <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> exploring the idea of a shell-like interface that allowed users to <html:em>undo</html:em> their shell actions (amongst other cool tricks). Unfortunately <html:em>shell-like</html:em> is not enough. <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> cut many corners to masquerade as a shell (e.g. appending <html:code>env</html:code> to understand how a command may have altered the execution environment). I felt it was necessary to make <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> a "SHELL"! To do that, I needed a solid foundation to build on.</html:p>
            <html:p><html:code>msh</html:code>, the POSIX shell that comes with <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link>, is by no means POSIX-complete in terms of features. But it is my <fr:link href="https://github.com/patricoferris/nixos/blob/e0faf870f76710d4a75ace775f333c88f1321c5a/modules/default.nix#L59" type="external">daily driver at this point</fr:link>. It includes a pure OCaml rewrite of <fr:link href="github.com/antirez/linenoise" type="external">linenoise</fr:link> (a small, self-contained alternative to the venerable <html:code>readline</html:code>) called <fr:link href="https://tangled.org/patrick.sirref.org/bruit" type="external">bruit</fr:link>.</html:p>
            <html:p>If you have <html:code>docker</html:code> installed on your machine, you can take it for a spin today:</html:p>
            <html:pre><![CDATA[docker run -it --rm patrickferris/msh]]></html:pre>
            <html:p>The <html:code>patrickferris/msh</html:code> docker image is just for trying it out. It is based on the OCaml 5.3 alpine image.</html:p>
            <html:p>Alternatively, you can build <html:code>msh</html:code> from source and have it available in your opam switch.</html:p>
            <html:pre><![CDATA[opam pin git+https://tangled.org/patrick.sirref.org/merry]]></html:pre>
            <html:p>There are many small paper cuts left to patch over, but most of it is porcelain (e.g. <html:code>ctrl+left-arrow</html:code> for moving in <fr:link href="https://tangled.org/patrick.sirref.org/bruit" type="external">bruit</fr:link>). Unfortunately, these are the kinds of things that you will <html:em>immediately</html:em> stumble upon.</html:p>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="false" numbered="false">
          <fr:frontmatter>
            <fr:authors>
              <fr:author>
                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
              </fr:author>
            </fr:authors>
            <fr:date>
              <fr:year>2026</fr:year>
              <fr:month>3</fr:month>
              <fr:day>19</fr:day>
            </fr:date>
            <fr:title text="What makes Merry different?">What makes Merry different?</fr:title>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>Nothing.</html:p>
            <html:p><fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is supposed to be a solid, POSIX-ish base to build on. Unfortunaltely, as it turns out, the subset of features from the POSIX specification that people <html:em>actually use</html:em>... is pretty much all of it. Every possible redirection, variable expansion, shell built-in and compound command make some appearance. Not to mention the non-POSIX bits of shell we all take for granted (e.g. <html:code>&amp;&gt;</html:code>-redirection).</html:p>
            <html:p><fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> and <html:code>msh</html:code> are useable. You will most likely find bugs if you use them. If, when using <html:code>msh</html:code>, you find something obscure happening you can enable debug mode either my setting the variable <html:code>MSH_DEBUG</html:code> or by invoking <html:code>msh</html:code> with <html:code>-v -v</html:code>.</html:p>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="false" numbered="false">
          <fr:frontmatter>
            <fr:authors>
              <fr:author>
                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
              </fr:author>
            </fr:authors>
            <fr:date>
              <fr:year>2026</fr:year>
              <fr:month>3</fr:month>
              <fr:day>19</fr:day>
            </fr:date>
            <fr:title text="What's next?">What's next?</fr:title>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>It is soon time to combine <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> and <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> into the time-travelling, POSIX-ish shell that I have been trying to build since I first started working on <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link>.</html:p>
            <html:p>For this to be successful, I need <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> to be able to pass plenty of tests and right now that involves trying to install plenty of packages using tools like <html:code>apk</html:code> and <html:code>apt</html:code>.</html:p>
            <fr:tree show-metadata="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>3</fr:month>
                  <fr:day>19</fr:day>
                </fr:date>
                <fr:title text="Shell MRDT">Shell MRDT</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>What I am particularly interested in reasoning about, is the <fr:link href="https://tangled.org/patrick.sirref.org/merry/blob/main/src/lib/eval.ml#L22" type="external">execution context</fr:link> in <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link>. This value, alongside the file-system, constitutes a fairly deep understanding of the state that changes in each step of a shell's evaluation loop.</html:p>
                <html:p>This was, in terms of <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link>, the missing piece for truly building some kind of <fr:link href="/mrdts/" title="Mergeable Replicated Data Type Implementation" uri="https://patrick.sirref.org/mrdts/" display-uri="mrdts" type="local">MRDT</fr:link> across shell sessions.</html:p>
              </fr:mainmatter>
            </fr:tree>
          </fr:mainmatter>
        </fr:tree>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false" numbered="false">
      <fr:frontmatter>
        <fr:authors>
          <fr:author>
            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
          </fr:author>
        </fr:authors>
        <fr:date>
          <fr:year>2026</fr:year>
          <fr:month>3</fr:month>
          <fr:day>19</fr:day>
        </fr:date>
        <fr:title text="TIFF in OCaml">TIFF in OCaml</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <html:p>I picked up from the excellent <fr:link href="/outreachy/" title="Outreachy" uri="https://patrick.sirref.org/outreachy/" display-uri="outreachy" type="local">Outreachy</fr:link> work of <fr:link href="https://patrick.sirref.org/tambe salome/" type="external">Tambe Salome</fr:link> in getting write-support in <fr:link href="/ocaml-tiff/" title="ocaml-tiff" uri="https://patrick.sirref.org/ocaml-tiff/" display-uri="ocaml-tiff" type="local">ocaml-tiff</fr:link>. We are getting closer to the kind of API I envisaged in this latest round of refinement and review.</html:p>
        <html:pre class="hilite">
          <html:code>
            <html:span class="ocaml-keyword-other">let</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[(]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">/</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[)]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">=</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Path</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-source"><![CDATA[(]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">/</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[)]]></html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-keyword">let</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-entity-name-function-binding">checkerboard</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">~</html:span>
            <html:span class="ocaml-source">size</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">=</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">  </html:span>
            <html:span class="ocaml-keyword">let</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-entity-name-function-binding">v</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">=</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-source">zeros</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-source">uint8</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[[|]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">size</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">size</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[|]]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-other">in</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">  </html:span>
            <html:span class="ocaml-keyword">for</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-entity-name-function-binding">row</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">=</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-numeric-decimal-integer">0</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-other">to</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">size</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">-</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-numeric-decimal-integer">1</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-other">do</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">    </html:span>
            <html:span class="ocaml-keyword">for</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-entity-name-function-binding">col</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">=</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-numeric-decimal-integer">0</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-other">to</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">size</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">-</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-numeric-decimal-integer">1</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-other">do</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">      </html:span>
            <html:span class="ocaml-keyword-other">if</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[(]]></html:span>
            <html:span class="ocaml-source">row</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">+</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">col</html:span>
            <html:span class="ocaml-source"><![CDATA[)]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">mod</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-numeric-decimal-integer">2</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">=</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-numeric-decimal-integer">0</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-other">then</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-source">set_item</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[[]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">row</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">col</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[]]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-numeric-decimal-integer">254</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">v</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">    </html:span>
            <html:span class="ocaml-keyword-other">done</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">  </html:span>
            <html:span class="ocaml-keyword-other">done</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">  </html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-source">to_bigarray</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">v</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-keyword-other">let</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-language-unit"><![CDATA[()]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">=</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">  </html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Eio_posix</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-source">run</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">@@</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-other">fun</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">env</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">-&gt;</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">  </html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Tiff_eio</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-source">with_open_out</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[(]]></html:span>
            <html:span class="ocaml-source">env</html:span>
            <html:span class="ocaml-keyword-other">#</html:span>
            <html:span class="ocaml-source">cwd</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">/</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-string-quoted-double">"</html:span>
            <html:span class="ocaml-string-quoted-double">example.tiff</html:span>
            <html:span class="ocaml-string-quoted-double">"</html:span>
            <html:span class="ocaml-source"><![CDATA[)]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">@@</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-other">fun</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">w</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">-&gt;</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">  </html:span>
            <html:span class="ocaml-keyword">let</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-entity-name-function-binding">tif</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-operator">=</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Tiff</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-source">make</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source"><![CDATA[(]]></html:span>
            <html:span class="ocaml-source">checkerboard</html:span>
            <html:span class="ocaml-source"> ~</html:span>
            <html:span class="ocaml-source">size</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-colon punctuation">:</html:span>
            <html:span class="ocaml-constant-numeric-decimal-integer">256</html:span>
            <html:span class="ocaml-source"><![CDATA[)]]></html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-keyword-other">in</html:span>
            <html:span class="ocaml-source">
</html:span>
            <html:span class="ocaml-source">  </html:span>
            <html:span class="ocaml-constant-language-capital-identifier">Tiff</html:span>
            <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
            <html:span class="ocaml-source">to_file</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">tif</html:span>
            <html:span class="ocaml-source"> </html:span>
            <html:span class="ocaml-source">w</html:span>
            <html:span class="ocaml-source">
</html:span>
          </html:code>
        </html:pre>
        <html:img src="/bafkrmiaqqczj5lemda5ijfjtjsyldbhmveu3btfvoa7rpt5k4dvdejjuhu.png" />
        <html:p>I have also been extremely pleased to see further external collaborators appearing:</html:p>
        <html:ul>
          <html:li>
            <html:p><fr:link href="https://github.com/geocaml/ocaml-tiff/pull/63" type="external">Nicolas</fr:link> helping out with metadata maintainence.</html:p>
          </html:li>
          <html:li>
            <html:p><fr:link href="https://github.com/geocaml/ocaml-tiff/pull/62" type="external">Virgile</fr:link> adding support for reading multi-image TIFF files.</html:p>
          </html:li>
        </html:ul>
      </fr:mainmatter>
    </fr:tree>
  </fr:mainmatter>
  <fr:backmatter>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="References">References</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors>
              <fr:author>
                <fr:link href="https://patrick.sirref.org/Louis Pouzin/" type="external">Louis Pouzin</fr:link>
              </fr:author>
            </fr:authors>
            <fr:date>
              <fr:year>2013</fr:year>
            </fr:date>
            <fr:uri>https://patrick.sirref.org/pouzin-shell-2013/</fr:uri>
            <fr:display-uri>pouzin-shell-2013</fr:display-uri>
            <fr:route>/pouzin-shell-2013/</fr:route>
            <fr:title text="The Origin of the Shell">The Origin of the Shell</fr:title>
            <fr:taxon>Reference</fr:taxon>
            <fr:meta name="external">https://people.csail.mit.edu/saltzer/Multics/Multics-Documents/MDN/MDN-4.pdf</fr:meta>
            <fr:meta name="journal">Multics Documents</fr:meta>
            <fr:meta name="doi" />
          </fr:frontmatter>
          <fr:mainmatter>
            <fr:tree show-metadata="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Louis Pouzin/" type="external">Louis Pouzin</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2013</fr:year>
                </fr:date>
                <fr:title text="Abstract">Abstract</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>How RUNCOM was created for CTSS and the shell was designed for Multics by Louis Pouzin.</html:p>
              </fr:mainmatter>
            </fr:tree>
          </fr:mainmatter>
        </fr:tree>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Context">Context</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors>
              <fr:author>
                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
              </fr:author>
            </fr:authors>
            <fr:date>
              <fr:year>2026</fr:year>
              <fr:month>3</fr:month>
              <fr:day>10</fr:day>
            </fr:date>
            <fr:uri>https://patrick.sirref.org/ocaml-roundup-march-2026/</fr:uri>
            <fr:display-uri>ocaml-roundup-march-2026</fr:display-uri>
            <fr:route>/ocaml-roundup-march-2026/</fr:route>
            <fr:title text="OCaml Roundup: March 2026">OCaml Roundup: March 2026</fr:title>
          </fr:frontmatter>
          <fr:mainmatter>
            <fr:tree show-metadata="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>3</fr:month>
                  <fr:day>19</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/weekly-2026-w12/</fr:uri>
                <fr:display-uri>weekly-2026-w12</fr:display-uri>
                <fr:route>/weekly-2026-w12/</fr:route>
                <fr:title text="A POSIX Shell in OCaml">A POSIX Shell in OCaml</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>Long time, no weekly. Since the start of this year I have been building a POSIX shell in OCaml called <html:code>msh</html:code> (with the underlying library called <html:code>Merry</html:code>). <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is available online now.</html:p>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>19</fr:day>
                    </fr:date>
                    <fr:title text="A POSIX(ish) shell in OCaml">A POSIX(ish) shell in OCaml</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p><fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is a POSIX(ish) in OCaml. It uses  <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link> alongside <fr:link href="github.com/colis-anr/morbig" type="external">Morbig</fr:link> (a static parser for POSIX shell).</html:p>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>19</fr:day>
                        </fr:date>
                        <fr:title text="Why another (POSIX) shell?">Why another (POSIX) shell?</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>Shells have been around for a long time. In my research on the notion of <html:em>metashell</html:em> I wrote about Louis Pouzin originally coining the term:</html:p>
                        <fr:tree show-metadata="false" numbered="false">
                          <fr:frontmatter>
                            <fr:authors>
                              <fr:author>
                                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                              </fr:author>
                            </fr:authors>
                            <fr:date>
                              <fr:year>2025</fr:year>
                              <fr:month>5</fr:month>
                              <fr:day>19</fr:day>
                            </fr:date>
                            <fr:uri>https://patrick.sirref.org/pouzin-shell/</fr:uri>
                            <fr:display-uri>pouzin-shell</fr:display-uri>
                            <fr:route>/pouzin-shell/</fr:route>
                            <fr:title text="Metashells › Louis Pouzin's &quot;SHELL&quot; "><fr:link href="/weekly-2025-05-12/" title="Metashells" uri="https://patrick.sirref.org/weekly-2025-05-12/" display-uri="weekly-2025-05-12" type="local">Metashells</fr:link> › Louis Pouzin's "SHELL" </fr:title>
                          </fr:frontmatter>
                          <fr:mainmatter>
                            <html:p>I spent some time reading <fr:link href="/pouzin-shell-2013/" title="The Origin of the Shell" uri="https://patrick.sirref.org/pouzin-shell-2013/" display-uri="pouzin-shell-2013" type="local">part of the multics design documentation</fr:link> this week. Louis Pouzin coined the term "SHELL" in this document, and I was reminded yet again just how important it is to be a good writer even as a "computer science researcher". For example, this excerpt from the requirements section of the document</html:p>
                            <html:blockquote>
                              <html:p>The previous definitions imply that a command MUST be designed while keeping in mind the user, sitting at his console, wondering about what might be going on, mistyping or forgetting arguments, even if fully aware of the conventions, and possibly interfering with the command by hasty quits, carriage returns, and other temperamental reactions.</html:p>
                            </html:blockquote>
                            <html:p>And then later, when defining the "SHELL".</html:p>
                            <html:blockquote>
                              <html:p>We may envision a common procedure called automatically by the supervisor whenever a user types in some message at his console, at a time when he has no other process in active execution under console control (presently called command level). This procedure acts as an interface between console messages and subroutine. The purpose of such a procedure is to create a medium of exchange into which one could activate any procedure, <html:em>inside of another program if it were called</html:em>. Hereafter, for simplification, we shall refer to that procedure as the "SHELL".</html:p>
                            </html:blockquote>
                            <html:p>It still surprises how little the undergraduate degree in computer science at <fr:link href="/ucam/" title="University of Cambridge" uri="https://patrick.sirref.org/ucam/" display-uri="ucam" type="local">Cambridge</fr:link> focuses on writing skills.</html:p>
                          </fr:mainmatter>
                        </fr:tree>
                        <html:p>I built <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> exploring the idea of a shell-like interface that allowed users to <html:em>undo</html:em> their shell actions (amongst other cool tricks). Unfortunately <html:em>shell-like</html:em> is not enough. <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> cut many corners to masquerade as a shell (e.g. appending <html:code>env</html:code> to understand how a command may have altered the execution environment). I felt it was necessary to make <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> a "SHELL"! To do that, I needed a solid foundation to build on.</html:p>
                        <html:p><html:code>msh</html:code>, the POSIX shell that comes with <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link>, is by no means POSIX-complete in terms of features. But it is my <fr:link href="https://github.com/patricoferris/nixos/blob/e0faf870f76710d4a75ace775f333c88f1321c5a/modules/default.nix#L59" type="external">daily driver at this point</fr:link>. It includes a pure OCaml rewrite of <fr:link href="github.com/antirez/linenoise" type="external">linenoise</fr:link> (a small, self-contained alternative to the venerable <html:code>readline</html:code>) called <fr:link href="https://tangled.org/patrick.sirref.org/bruit" type="external">bruit</fr:link>.</html:p>
                        <html:p>If you have <html:code>docker</html:code> installed on your machine, you can take it for a spin today:</html:p>
                        <html:pre><![CDATA[docker run -it --rm patrickferris/msh]]></html:pre>
                        <html:p>The <html:code>patrickferris/msh</html:code> docker image is just for trying it out. It is based on the OCaml 5.3 alpine image.</html:p>
                        <html:p>Alternatively, you can build <html:code>msh</html:code> from source and have it available in your opam switch.</html:p>
                        <html:pre><![CDATA[opam pin git+https://tangled.org/patrick.sirref.org/merry]]></html:pre>
                        <html:p>There are many small paper cuts left to patch over, but most of it is porcelain (e.g. <html:code>ctrl+left-arrow</html:code> for moving in <fr:link href="https://tangled.org/patrick.sirref.org/bruit" type="external">bruit</fr:link>). Unfortunately, these are the kinds of things that you will <html:em>immediately</html:em> stumble upon.</html:p>
                      </fr:mainmatter>
                    </fr:tree>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>19</fr:day>
                        </fr:date>
                        <fr:title text="What makes Merry different?">What makes Merry different?</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>Nothing.</html:p>
                        <html:p><fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is supposed to be a solid, POSIX-ish base to build on. Unfortunaltely, as it turns out, the subset of features from the POSIX specification that people <html:em>actually use</html:em>... is pretty much all of it. Every possible redirection, variable expansion, shell built-in and compound command make some appearance. Not to mention the non-POSIX bits of shell we all take for granted (e.g. <html:code>&amp;&gt;</html:code>-redirection).</html:p>
                        <html:p><fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> and <html:code>msh</html:code> are useable. You will most likely find bugs if you use them. If, when using <html:code>msh</html:code>, you find something obscure happening you can enable debug mode either my setting the variable <html:code>MSH_DEBUG</html:code> or by invoking <html:code>msh</html:code> with <html:code>-v -v</html:code>.</html:p>
                      </fr:mainmatter>
                    </fr:tree>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>19</fr:day>
                        </fr:date>
                        <fr:title text="What's next?">What's next?</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>It is soon time to combine <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> and <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> into the time-travelling, POSIX-ish shell that I have been trying to build since I first started working on <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link>.</html:p>
                        <html:p>For this to be successful, I need <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> to be able to pass plenty of tests and right now that involves trying to install plenty of packages using tools like <html:code>apk</html:code> and <html:code>apt</html:code>.</html:p>
                        <fr:tree show-metadata="false" numbered="false">
                          <fr:frontmatter>
                            <fr:authors>
                              <fr:author>
                                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                              </fr:author>
                            </fr:authors>
                            <fr:date>
                              <fr:year>2026</fr:year>
                              <fr:month>3</fr:month>
                              <fr:day>19</fr:day>
                            </fr:date>
                            <fr:title text="Shell MRDT">Shell MRDT</fr:title>
                          </fr:frontmatter>
                          <fr:mainmatter>
                            <html:p>What I am particularly interested in reasoning about, is the <fr:link href="https://tangled.org/patrick.sirref.org/merry/blob/main/src/lib/eval.ml#L22" type="external">execution context</fr:link> in <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link>. This value, alongside the file-system, constitutes a fairly deep understanding of the state that changes in each step of a shell's evaluation loop.</html:p>
                            <html:p>This was, in terms of <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link>, the missing piece for truly building some kind of <fr:link href="/mrdts/" title="Mergeable Replicated Data Type Implementation" uri="https://patrick.sirref.org/mrdts/" display-uri="mrdts" type="local">MRDT</fr:link> across shell sessions.</html:p>
                          </fr:mainmatter>
                        </fr:tree>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>19</fr:day>
                    </fr:date>
                    <fr:title text="TIFF in OCaml">TIFF in OCaml</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>I picked up from the excellent <fr:link href="/outreachy/" title="Outreachy" uri="https://patrick.sirref.org/outreachy/" display-uri="outreachy" type="local">Outreachy</fr:link> work of <fr:link href="https://patrick.sirref.org/tambe salome/" type="external">Tambe Salome</fr:link> in getting write-support in <fr:link href="/ocaml-tiff/" title="ocaml-tiff" uri="https://patrick.sirref.org/ocaml-tiff/" display-uri="ocaml-tiff" type="local">ocaml-tiff</fr:link>. We are getting closer to the kind of API I envisaged in this latest round of refinement and review.</html:p>
                    <html:pre class="hilite">
                      <html:code>
                        <html:span class="ocaml-keyword-other">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Path</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">checkerboard</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">~</html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">v</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">zeros</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">uint8</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[[|]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[|]]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">in</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword">for</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">row</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">0</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">to</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">1</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">do</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">    </html:span>
                        <html:span class="ocaml-keyword">for</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">col</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">0</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">to</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">1</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">do</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">      </html:span>
                        <html:span class="ocaml-keyword-other">if</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source">row</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">+</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">col</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">mod</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">2</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">0</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">then</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">set_item</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[[]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">row</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">col</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[]]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">254</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">v</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">    </html:span>
                        <html:span class="ocaml-keyword-other">done</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword-other">done</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">to_bigarray</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">v</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-keyword-other">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-unit"><![CDATA[()]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio_posix</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">run</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">@@</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">fun</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">env</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-&gt;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Tiff_eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">with_open_out</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source">env</html:span>
                        <html:span class="ocaml-keyword-other">#</html:span>
                        <html:span class="ocaml-source">cwd</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">example.tiff</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">@@</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">fun</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">w</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-&gt;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">tif</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Tiff</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">make</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source">checkerboard</html:span>
                        <html:span class="ocaml-source"> ~</html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-colon punctuation">:</html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">256</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">in</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Tiff</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">to_file</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">tif</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">w</html:span>
                        <html:span class="ocaml-source">
</html:span>
                      </html:code>
                    </html:pre>
                    <html:img src="/bafkrmiaqqczj5lemda5ijfjtjsyldbhmveu3btfvoa7rpt5k4dvdejjuhu.png" />
                    <html:p>I have also been extremely pleased to see further external collaborators appearing:</html:p>
                    <html:ul>
                      <html:li>
                        <html:p><fr:link href="https://github.com/geocaml/ocaml-tiff/pull/63" type="external">Nicolas</fr:link> helping out with metadata maintainence.</html:p>
                      </html:li>
                      <html:li>
                        <html:p><fr:link href="https://github.com/geocaml/ocaml-tiff/pull/62" type="external">Virgile</fr:link> adding support for reading multi-image TIFF files.</html:p>
                      </html:li>
                    </html:ul>
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>3</fr:month>
                  <fr:day>30</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/weekly-2026-w13/</fr:uri>
                <fr:display-uri>weekly-2026-w13</fr:display-uri>
                <fr:route>/weekly-2026-w13/</fr:route>
                <fr:title text="Ppxlib release and Merry updates">Ppxlib release and Merry updates</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>30</fr:day>
                    </fr:date>
                    <fr:uri>https://patrick.sirref.org/ppxlib-release-0-38-0/</fr:uri>
                    <fr:display-uri>ppxlib-release-0-38-0</fr:display-uri>
                    <fr:route>/ppxlib-release-0-38-0/</fr:route>
                    <fr:title text="Ppxlib Releases › 0.38.0 "><fr:link href="/ppxlib-releases/" title="Ppxlib Releases" uri="https://patrick.sirref.org/ppxlib-releases/" display-uri="ppxlib-releases" type="local">Ppxlib Releases</fr:link> › 0.38.0 </fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p><fr:link href="/nathanreb/" title="Nathan Rebours" uri="https://patrick.sirref.org/nathanreb/" display-uri="nathanreb" type="local">Nathan</fr:link> and I <fr:link href="https://github.com/ocaml/opam-repository/pull/29563" type="external">released ppxlib.0.38.0</fr:link> last week. Its main feature is full <html:em>migration</html:em> support for the upcoming OCaml 5.5 compiler (currently in its <html:code>alpha3</html:code> release). This means supporting the handful of new features landing in OCaml 5.5: <fr:link href="/modular-explicits/" title="Modular Explicits in OCaml" uri="https://patrick.sirref.org/modular-explicits/" display-uri="modular-explicits" type="local">modular explicits</fr:link>, external type declarations and arbitrary "local" structure items.</html:p>
                    <html:p>As <fr:link href="https://patrick.sirref.org/nathenreb/" type="external">Nathan</fr:link> and I continue to find a plausible maintenance story for <fr:link href="/ppxlib/" title="Ppxlib" uri="https://patrick.sirref.org/ppxlib/" display-uri="ppxlib" type="local">ppxlib</fr:link>, <fr:link href="https://patrick.sirref.org/nathenreb/" type="external">Nathan</fr:link> has opened <fr:link href="https://github.com/ocaml/ocaml/issues/14668" type="external">an issue on the OCaml compiler to discuss the idea of adding additional extension points to the language</fr:link>.</html:p>
                    <html:p>This comes from the desire to be able to store encoded versions of new OCaml features inside older abstract syntax trees.</html:p>
                    <html:p>There are also some nice bug fixes in there too:</html:p>
                    <html:ul>
                      <html:li>
                        <html:p><fr:link href="https://github.com/ocaml-ppx/ppxlib/pull/613" type="external">A potential OOM</fr:link> loop has now been removed.</html:p>
                      </html:li>
                      <html:li>
                        <html:p>
                          <fr:link href="https://github.com/ocaml-ppx/ppxlib/pull/619" type="external">Locations have been restored to long identifiers!</fr:link>
                        </html:p>
                      </html:li>
                    </html:ul>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>30</fr:day>
                    </fr:date>
                    <fr:title text="Merry updates">Merry updates</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>It turns out writing a POSIX(ish) shell is hard; at least, there is a vast number of slightly obscure features that one needs to support. I think this is surprising because most people writing shell scripts write simple shell scripts; scripts that use a much smaller subset of features. In the same breath, those same developers are likely to use something like <html:code>apt-get install bash</html:code> which runs a plethora of more advanced (and non-POSIX) shell scripts!</html:p>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>30</fr:day>
                        </fr:date>
                        <fr:title text="Exec redirects">Exec redirects</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>In a shell script, you can use the built-in command <html:code>exec</html:code> to replace the current process with a new one (e.g. <html:code>exec vim</html:code>). However, there is a <fr:link href="https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_21" type="external"><html:em>different</html:em> mode of operation</fr:link> for <html:code>exec</html:code>:</html:p>
                        <html:blockquote>
                          <html:p>If exec is specified with no operands, any redirections associated with the exec command shall be made in the current shell execution environment.</html:p>
                        </html:blockquote>
                        <html:p>One of my litmus tests for <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is the <fr:link href="https://wiki.debian.org/Debootstrap" type="external">Debian debootstrap scripts</fr:link> (h/t <fr:link href="/anilmadhavapeddy/" title="Anil Madhavapeddy" uri="https://patrick.sirref.org/anilmadhavapeddy/" display-uri="anilmadhavapeddy" type="local">Anil</fr:link>). One thing that it does is the following:</html:p>
                        <html:pre class="hilite">
                          <html:code>
                            <html:span class="sh-entity-name-function">err</html:span>
                            <html:span class="sh-meta-function"> </html:span>
                            <html:span class="sh-punctuation-definition-arguments"><![CDATA[()]]></html:span>
                            <html:span class="sh-meta-function"> </html:span>
                            <html:span class="sh-punctuation-definition-group"><![CDATA[{]]></html:span>
                            <html:span class="sh-meta-scope-group">
</html:span>
                            <html:span class="sh-meta-scope-group">  </html:span>
                            <html:span class="sh-support-function-builtin">printf</html:span>
                            <html:span class="sh-meta-scope-group"> </html:span>
                            <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                            <html:span class="sh-string-quoted-double">err </html:span>
                            <html:span class="sh-punctuation-definition-variable">$</html:span>
                            <html:span class="sh-variable-other-positional">1</html:span>
                            <html:span class="sh-punctuation-definition-string-end">"</html:span>
                            <html:span class="sh-meta-scope-group"> </html:span>
                            <html:span class="sh-keyword-operator-redirect">&gt;&amp;4</html:span>
                            <html:span class="sh-meta-scope-group">
</html:span>
                            <html:span class="sh-punctuation-definition-group"><![CDATA[}]]></html:span>
                            <html:span class="sh-punctuation-definition-function">
</html:span>
                            <html:span class="sh-source">
</html:span>
                            <html:span class="sh-support-function-builtin">exec</html:span>
                            <html:span class="sh-source"> </html:span>
                            <html:span class="sh-keyword-operator-redirect">4&gt;&amp;1</html:span>
                            <html:span class="sh-source"> 
</html:span>
                          </html:code>
                        </html:pre>
                        <html:p>A lot of detail has been elided for clarity. <html:code>exec 4&gt;&amp;1</html:code> sets up a redirection for the shell's execution environment in which file descriptor <html:code>4</html:code> is now an alias for standard output. So, writing to <html:code>4</html:code> (by redirecting a command's standard output to <html:code>4</html:code> i.e. <html:code>&gt;&amp;4</html:code>) will output to where standard output is going (most likely the terminal).</html:p>
                        <html:p>For this to work, shell's must <fr:link href="https://www.man7.org/linux/man-pages/man2/dup.2.html" type="external"><html:code>dup2</html:code></fr:link> the relevant file descriptors which have the following condition:</html:p>
                        <html:blockquote>
                          <html:p>If the file descriptor newfd was previously open, it is closed before being reused; the close is performed silently (i.e., any errors during the close are not reported by dup2()).</html:p>
                        </html:blockquote>
                        <html:p>This is all very well, unless your program has an important file already open that happens to have file descriptor <html:code>4</html:code>. The <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link> Linux and POSIX backends suffer from this problem. Both make use of a file-based synchronisation mechanism for waking up the eventloop should another domain push a completion to the scheduler's run queue. On Linux this is <fr:link href="https://github.com/ocaml-multicore/eio/blob/c44ee5ce96c120b7ccc23a12d241dc8672e2888f/lib_eio_linux/sched.ml#L501" type="external">via an eventfd</fr:link> and in <fr:link href="https://github.com/ocaml-multicore/eio/blob/c44ee5ce96c120b7ccc23a12d241dc8672e2888f/lib_eio_posix/sched.ml#L20" type="external">POSIX, a pipe</fr:link>.</html:p>
                        <html:p>This <html:code>dup2</html:code> will close the <html:code>eventfd</html:code> and will likely grind Eio to a halt (or an <html:code>assert false</html:code>). For now, I have resorted to vendoring <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link> and moving the <html:code>eventfd</html:code> file descriptors to higher values, though I <fr:link href="https://github.com/ocaml-multicore/eio/pull/836" type="external">have opened a PR to make this more configurable</fr:link>. This bug is quite easy to write up... it was not so easy to find!</html:p>
                        <html:p>As a random example, consider the <html:code>debconf/confmodule</html:code> <fr:link href="https://sources.debian.org/src/debconf/1.5.77/confmodule/" type="external">script</fr:link> which offers very little room for a buggy implementation!</html:p>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>30</fr:day>
                    </fr:date>
                    <fr:title text="Outreachy">Outreachy</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>The presentations from the demonstrations for this round of Outreachy are now online!</html:p>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>2</fr:month>
                          <fr:day>9</fr:day>
                        </fr:date>
                        <fr:uri>https://patrick.sirref.org/outreachy-ocaml-tiff/</fr:uri>
                        <fr:display-uri>outreachy-ocaml-tiff</fr:display-uri>
                        <fr:route>/outreachy-ocaml-tiff/</fr:route>
                        <fr:title text="Write support in OCaml TIFF library">Write support in OCaml TIFF library</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>I am mentoring <fr:link href="/tambe-salome/" title="Tambe Salome" uri="https://patrick.sirref.org/tambe-salome/" display-uri="tambe-salome" type="local">Tambe Salome</fr:link> during the December 2025 Outreachy round to add support for writing TIFF files in the <fr:link href="/ocaml-tiff/" title="ocaml-tiff" uri="https://patrick.sirref.org/ocaml-tiff/" display-uri="ocaml-tiff" type="local">ocaml-tiff</fr:link> library.</html:p>
                        <html:p>You can now see the video of the demonstration day presentation:</html:p>
                        <html:div style="text-align: center">
<html:iframe title="Outreachy Demo Day December 2025 Round" width="560" height="315" src="https://watch.ocaml.org/videos/embed/8aUqMhFvhQGq4WJLH3ukjA?start=1h18m33s" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" />
</html:div>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>4</fr:month>
                  <fr:day>3</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/weekly-2026-w14/</fr:uri>
                <fr:display-uri>weekly-2026-w14</fr:display-uri>
                <fr:route>/weekly-2026-w14/</fr:route>
                <fr:title text="Forking in Shells &amp; Library Maintenance">Forking in Shells &amp; Library Maintenance</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>This week, <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> got close to executing <html:code>apt-get install ca-certificates</html:code> and running Debian's <html:code>debootstrap</html:code> scripts correctly. <fr:link href="https://patrick.sirref.org/nathan/" type="external">Nathan</fr:link> and I released <fr:link href="/ppxlib/" title="Ppxlib" uri="https://patrick.sirref.org/ppxlib/" display-uri="ppxlib" type="local">Ppxlib</fr:link> <html:code>0.38.0</html:code>, I added support to <fr:link href="https://github.com/geocaml/ocaml-proj" type="external">ocaml-proj</fr:link> for compiling to the browser and I fixed a long-standing bug in <fr:link href="https://github.com/patricoferris/hilite" type="external">hilite</fr:link>.</html:p>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:title text="Forking in Merry">Forking in Merry</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>So far, <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> has managed to support many features of the POSIX shell specification without needing to do a <html:code>fork(2)</html:code> without an <html:code>exec</html:code>. Or, to put it another way, all the forking that <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> does, will only be followed by running C code. This is actually a feature of <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link>'s <html:code>Process</html:code> API. It is required to make process execution safe in the context of multiple domains in OCaml (mostly due to ensuring consistency within the garbage collector). However, it is perfectly okay to <html:code>fork</html:code> and run OCaml code provided there is only a single domain.</html:p>
                    <html:p>In a shell, many features of the shell language require that commands (or built-ins, function applications etc.) execute within a <html:code>subshell</html:code>. The specification does not go into detail about how shells should implement this, but many choose to <html:code>fork</html:code> in order to preserve some state in the parent (for example, the file descriptor table). Some shells try to minimise the number of forks as an optimisation. The <fr:link href="/korn1996korn/" title="The New Korn Shell" uri="https://patrick.sirref.org/korn1996korn/" display-uri="korn1996korn" type="local">korn shell (ksh)</fr:link> is one such shell.</html:p>
                    <html:blockquote>
                      <html:p>Using the notation <html:code>$(command)</html:code> will cause <html:code>command</html:code> to execute in a subshell of the current ksh. In many instances, ksh will not actually fork/exec a subshell when command is a built-in or a shell function.</html:p>
                    </html:blockquote>
                    <html:p>By trying to adhere to Multicore OCaml's "<html:em>thou shalt not fork</html:em>" commandment, <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is much more similar to <html:code>ksh</html:code> in this regard. However, whenever there is an interaction with a shell built-in and semantics that need a child process, things get tricky very quickly. Consider the following:</html:p>
                    <html:pre class="hilite">
                      <html:code>
                        <html:span class="sh-keyword-control">while</html:span>
                        <html:span class="sh-meta-scope-while-loop"> </html:span>
                        <html:span class="sh-support-function-builtin">echo</html:span>
                        <html:span class="sh-meta-scope-while-loop"> </html:span>
                        <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                        <html:span class="sh-string-quoted-double">hello</html:span>
                        <html:span class="sh-punctuation-definition-string-end">"</html:span>
                        <html:span class="sh-keyword-operator-list">;</html:span>
                        <html:span class="sh-meta-scope-while-loop"> </html:span>
                        <html:span class="sh-keyword-control">do</html:span>
                        <html:span class="sh-meta-scope-while-loop">
</html:span>
                        <html:span class="sh-meta-scope-while-loop">  </html:span>
                        <html:span class="sh-support-function-builtin">:</html:span>
                        <html:span class="sh-meta-scope-while-loop">
</html:span>
                        <html:span class="sh-keyword-control">done</html:span>
                        <html:span class="sh-source"> </html:span>
                        <html:span class="sh-keyword-operator-pipe">|</html:span>
                        <html:span class="sh-source"> head -n 3
</html:span>
                      </html:code>
                    </html:pre>
                    <html:p>We have a mix of shell built-ins, normal commands, a compound command (the <html:code>while</html:code> loop) and a pipeline. Each individual command in the pipeline requires you to run them in a subshell and set them up <html:em>before</html:em> executing them. Without a <html:code>fork</html:code> for that first command, you may end up looping forever which is not the intended behaviour here. I had a good conversation with <fr:link href="/mdales/" title="Michael W. Dales" uri="https://patrick.sirref.org/mdales/" display-uri="mdales" type="local">Michael</fr:link> about the implications of this for reproducibility.</html:p>
                    <html:p>For now, I have started implementing a <html:code>fork</html:code> for some of the shell features whilst still trying to maintain the functional core of the implementation.</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:title text="An OCaml 5.5 compatible Ppxlib">An OCaml 5.5 compatible Ppxlib</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p><fr:link href="/nathanreb/" title="Nathan Rebours" uri="https://patrick.sirref.org/nathanreb/" display-uri="nathanreb" type="local">Nathan</fr:link> and I released an OCaml 5.5 compatible version of <fr:link href="/ppxlib/" title="Ppxlib" uri="https://patrick.sirref.org/ppxlib/" display-uri="ppxlib" type="local">ppxlib</fr:link>.</html:p>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>30</fr:day>
                        </fr:date>
                        <fr:uri>https://patrick.sirref.org/ppxlib-release-0-38-0/</fr:uri>
                        <fr:display-uri>ppxlib-release-0-38-0</fr:display-uri>
                        <fr:route>/ppxlib-release-0-38-0/</fr:route>
                        <fr:title text="Ppxlib Releases › 0.38.0 "><fr:link href="/ppxlib-releases/" title="Ppxlib Releases" uri="https://patrick.sirref.org/ppxlib-releases/" display-uri="ppxlib-releases" type="local">Ppxlib Releases</fr:link> › 0.38.0 </fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p><fr:link href="/nathanreb/" title="Nathan Rebours" uri="https://patrick.sirref.org/nathanreb/" display-uri="nathanreb" type="local">Nathan</fr:link> and I <fr:link href="https://github.com/ocaml/opam-repository/pull/29563" type="external">released ppxlib.0.38.0</fr:link> last week. Its main feature is full <html:em>migration</html:em> support for the upcoming OCaml 5.5 compiler (currently in its <html:code>alpha3</html:code> release). This means supporting the handful of new features landing in OCaml 5.5: <fr:link href="/modular-explicits/" title="Modular Explicits in OCaml" uri="https://patrick.sirref.org/modular-explicits/" display-uri="modular-explicits" type="local">modular explicits</fr:link>, external type declarations and arbitrary "local" structure items.</html:p>
                        <html:p>As <fr:link href="https://patrick.sirref.org/nathenreb/" type="external">Nathan</fr:link> and I continue to find a plausible maintenance story for <fr:link href="/ppxlib/" title="Ppxlib" uri="https://patrick.sirref.org/ppxlib/" display-uri="ppxlib" type="local">ppxlib</fr:link>, <fr:link href="https://patrick.sirref.org/nathenreb/" type="external">Nathan</fr:link> has opened <fr:link href="https://github.com/ocaml/ocaml/issues/14668" type="external">an issue on the OCaml compiler to discuss the idea of adding additional extension points to the language</fr:link>.</html:p>
                        <html:p>This comes from the desire to be able to store encoded versions of new OCaml features inside older abstract syntax trees.</html:p>
                        <html:p>There are also some nice bug fixes in there too:</html:p>
                        <html:ul>
                          <html:li>
                            <html:p><fr:link href="https://github.com/ocaml-ppx/ppxlib/pull/613" type="external">A potential OOM</fr:link> loop has now been removed.</html:p>
                          </html:li>
                          <html:li>
                            <html:p>
                              <fr:link href="https://github.com/ocaml-ppx/ppxlib/pull/619" type="external">Locations have been restored to long identifiers!</fr:link>
                            </html:p>
                          </html:li>
                        </html:ul>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:title text="Browser support for ocaml-proj">Browser support for ocaml-proj</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>A while ago <fr:link href="https://github.com/geocaml/ocaml-proj" type="external">I built some "modern" bindings to PROJ4 in OCaml</fr:link>. After reading <fr:link href="https://patrick.sirref.org/jonludlam/" type="external">Jon Ludlam</fr:link>'s <fr:link href="https://jon.recoil.org/blog/2026/03/weeknotes-2026-12.html" type="external">weeknotes</fr:link> (and speaking with him and <fr:link href="/anilmadhavapeddy/" title="Anil Madhavapeddy" uri="https://patrick.sirref.org/anilmadhavapeddy/" display-uri="anilmadhavapeddy" type="local">Anil</fr:link>, I thought it might be nice to add a Javascript backend to those bindings). This was relatively straight-forward using <fr:link href="https://dune.readthedocs.io/en/stable/virtual-libraries.html" type="external">Dune's virtual libraries</fr:link> and is <fr:link href="https://github.com/geocaml/ocaml-proj/blob/main/src/js/proj.ml" type="external">available on Github</fr:link>.</html:p>
                    <html:p>With virtual libraries, your own data analysis could (if you wished) depend solely on the <html:code>proj</html:code> library and later choose to either link it with <html:code>proj.c</html:code> or <html:code>proj.js</html:code> depending on where the analysis is being deployed.</html:p>
                    <html:p>I briefly looked at WASM support, but quickly realised there was not much appetite for it and trying to compile around the C FFI was going to be hard.</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:title text="Hilite updates">Hilite updates</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>Since I helped <fr:link href="https://ocaml.org/" type="external">relaunch the ocaml.org</fr:link> website a few years ago, I have maintained <fr:link href="https://github.com/patricoferris/hilite" type="external">hilite</fr:link>, a tool for doing build-time syntax highligting.</html:p>
                    <html:p>It is being used on the <fr:link href="https://ocaml.org/" type="external">ocaml.org</fr:link> website, <fr:link href="https://github.com/xhtmlboi/yocaml/blob/45858f4b25730149dae3735e7fcdb9111ac1f9eb/yocaml_markdown.opam#L18" type="external">in yocaml_markdown</fr:link> and also in <fr:link href="/mdales/" title="Michael W. Dales" uri="https://patrick.sirref.org/mdales/" display-uri="mdales" type="local">Michael's</fr:link> <fr:link href="https://github.com/mdales/webplats/blob/cf0ea95a66bae52f02a00739eb02564fc94183ee/webplats.opam#L19" type="external">webplats</fr:link> (when it works...).</html:p>
                    <html:p>And it was <fr:link href="/mdales/" title="Michael W. Dales" uri="https://patrick.sirref.org/mdales/" display-uri="mdales" type="local">Michael</fr:link> who led me to finally fixing and adding syntax highlighting support for Python and Go. Once I was on this roll, I finally took a look at fixing the long-standing bug of trying to highlight ocaml-mdx code, like the following:</html:p>
                    <html:pre class="hilite">
                      <html:code>
                        <html:span class="ocaml-mdx-hash">#</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-float">50.123</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-mdx-hash">#</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">1</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">file.ml</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                      </html:code>
                    </html:pre>
                    <html:p>In fact, this code now highlights fine, but was the source of the bug. The syntax highlighting grammer confuses the ocaml-mdx <html:code>#</html:code> as a toplevel directive and trouble ensues.</html:p>
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://patrick.sirref.org/weeklies-2026/</fr:uri>
            <fr:display-uri>weeklies-2026</fr:display-uri>
            <fr:route>/weeklies-2026/</fr:route>
            <fr:title text="Patrick Ferris' Weeklies › 2026 "><fr:link href="/weeklies/" title="Patrick Ferris' Weeklies" uri="https://patrick.sirref.org/weeklies/" display-uri="weeklies" type="local">Patrick Ferris' Weeklies</fr:link> › 2026 </fr:title>
          </fr:frontmatter>
          <fr:mainmatter>
            <fr:tree show-metadata="false" expanded="false" toc="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>4</fr:month>
                  <fr:day>28</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/weekly-2026-w17/</fr:uri>
                <fr:display-uri>weekly-2026-w17</fr:display-uri>
                <fr:route>/weekly-2026-w17/</fr:route>
                <fr:title text="Shell forks revisited">Shell forks revisited</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>Whilst I did get up to some other work last week, I decided to dedicate this weekly to <html:code>fork</html:code>-ing in the shell.</html:p>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>28</fr:day>
                    </fr:date>
                    <fr:title text="Must we fork?">Must we fork?</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>I have spent some time trying to figure out: can you implement a shell that never has to <html:code>fork</html:code> and run its own code (as opposed to <html:code>fork+exec</html:code>-ing, which is necessary)? There are, at least, two bits of POSIX shell semantics that make this hard: concurrency and redirects, particularly in the presence of shell built-ins.</html:p>
                    <html:p>Individual commands in a pipeline, according to the <fr:link href="/posixSpec/" title="IEEE/Open Group Standard for Information Technology--Portable Operating System Interface (POSIX™) Base Specifications, Issue 8" uri="https://patrick.sirref.org/posixSpec/" display-uri="posixSpec" type="local">POSIX specification</fr:link>, will have their standard input and output connected up before other redirections.</html:p>
                    <html:blockquote>
                      <html:p>The standard input, standard output, or both of a command shall be considered to be assigned by the pipeline before any redirection specified by redirection operators that are part of the command.</html:p>
                    </html:blockquote>
                    <html:p>Additionally:</html:p>
                    <html:blockquote>
                      <html:p>...each command of a multi-command pipeline is in a subshell environment; as an extension, however, any or all commands in a pipeline may be executed in the current environment.</html:p>
                    </html:blockquote>
                    <html:p>Where a subshell is defined as:</html:p>
                    <html:blockquote>
                      <html:p>Subshell: A shell execution environment, distinguished from the main or current shell execution environment.</html:p>
                    </html:blockquote>
                    <html:p>We will note that subshell <fr:tex display="inline"><![CDATA[\neq ]]></fr:tex> child process. In practice, however, the two are used fairly interchangeably given the semantics of spawning a child process. Perhaps the two most crucial aspects of a given command in a subshell are that:</html:p>
                    <html:ol>
                      <html:li>
                        <html:p>It is run <html:em>in parallel</html:em> with the other commands.</html:p>
                      </html:li>
                      <html:li>
                        <html:p>It should be <fr:link href="https://en.wikipedia.org/wiki/Reentrancy_(computing)" type="external"><html:em>reentrant</html:em></fr:link>.</html:p>
                      </html:li>
                    </html:ol>
                    <html:p>In what follows we will assume that we are discussing a multi-command pipeline <fr:tex display="inline"><![CDATA[p]]></fr:tex>.</html:p>
                    <html:p>
                      <fr:tex display="block"><![CDATA[ \texttt {p} =  \texttt {cmd}_1 | ... | \texttt {cmd}_n \text { where } n \geq  2 ]]></fr:tex>
                    </html:p>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>4</fr:month>
                          <fr:day>28</fr:day>
                        </fr:date>
                        <fr:title text="Pipeline parallelism">Pipeline parallelism</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>The <fr:link href="/posixSpec/" title="IEEE/Open Group Standard for Information Technology--Portable Operating System Interface (POSIX™) Base Specifications, Issue 8" uri="https://patrick.sirref.org/posixSpec/" display-uri="posixSpec" type="local">POSIX specification</fr:link> does not mandate parallelism explicitly for commands in a pipeline. However, in practice the semantics of a pipeline necessitate concurrency at the very least.</html:p>
                        <html:p>Suppose we have <fr:tex display="inline"><![CDATA[\texttt {cmd}_1 | \texttt {cmd}_2]]></fr:tex> where <fr:tex display="inline"><![CDATA[\texttt {cmd}_1]]></fr:tex> produces an infinite amount of output (e.g., <html:code>cat /dev/urandom</html:code>) and <fr:tex display="inline"><![CDATA[\texttt {cmd}_2]]></fr:tex> consumes a finite amount of input (e.g., <fr:link href="https://github.com/dinosaure/hxd" type="external"><html:code>hxd-xxd -l100</html:code></fr:link>). What is the result of running this pipeline with the default POSIX shell options?</html:p>
                        <html:pre class="hilite">
                          <html:code>
                            <html:span class="sh-source">$ dash -c </html:span>
                            <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                            <html:span class="sh-string-quoted-double">cat /dev/urandom | hxd.xxd -l100</html:span>
                            <html:span class="sh-punctuation-definition-string-end">"</html:span>
                            <html:span class="sh-source">
</html:span>
                            <html:span class="sh-source">00000000: 5033 7981 ae44 d475 074c 5361 0a9e 3828  P3y..D.u.LSa..8</html:span>
                            <html:span class="sh-punctuation-definition-subshell"><![CDATA[(]]></html:span>
                            <html:span class="sh-meta-scope-subshell">
</html:span>
                            <html:span class="sh-meta-scope-subshell">00000010: f88c 1f9e 3b0e bcde 528a f78c b1e4 7195  ....</html:span>
                            <html:span class="sh-keyword-operator-list">;</html:span>
                            <html:span class="sh-meta-scope-subshell">...R.....q.
</html:span>
                            <html:span class="sh-meta-scope-subshell"><![CDATA[00000020: 16f2 8325 c90b 9310 539c af04 8289 c3a0  ...%....S.......]]></html:span>
                            <html:span class="sh-meta-scope-subshell">
</html:span>
                            <html:span class="sh-meta-scope-subshell">00000030: 57c5 680a 2c81 a999 71b6 708e 95df e720  W.h.,...q.p....
</html:span>
                            <html:span class="sh-meta-scope-subshell">00000040: 004c 407a 8365 9448 65c6 b435 bed2 4084  .L@z.e.He..5..@.
</html:span>
                            <html:span class="sh-meta-scope-subshell">00000050: 62ea 96ab 2b23 0385 f9d2 fc30 d090 1719  b...+#.....0....
</html:span>
                            <html:span class="sh-meta-scope-subshell">00000060: 68d1 c514                                h...
</html:span>
                            <html:span class="sh-meta-scope-subshell">cat: write error: Broken pipe
</html:span>
                          </html:code>
                        </html:pre>
                        <html:p>There are three factors that influence the pipeline's execution behaviour (i.e. what is done about the commands that are run and the exit status we return).</html:p>
                        <html:ol>
                          <html:li>
                            <html:p><html:code>pipefail</html:code>: With <html:code>pipefail</html:code> set to <html:code>false</html:code> (the default), the exit status of the pipeline is entirely decided upon by the exit status of the <html:em>rightmost</html:em> command (above <fr:tex display="inline"><![CDATA[\texttt {cmd}_2]]></fr:tex>). With <html:code>pipefail</html:code>, the pipe will exit with 0 if all commands exit with 0; otherwise the non-zero exit status of the <html:em>rightmost</html:em> command is taken.</html:p>
                          </html:li>
                          <html:li>
                            <html:p><html:code>!</html:code>: If the pipeline begins with <html:code>!</html:code> then this inverts the exit code in the way you might expect.</html:p>
                          </html:li>
                          <html:li>
                            <html:p>All commands must terminate.</html:p>
                          </html:li>
                        </html:ol>
                        <html:p>Whilst in theory <fr:tex display="inline"><![CDATA[\texttt {cmd}_1]]></fr:tex> does not terminate, the termination of <fr:tex display="inline"><![CDATA[\texttt {cmd}_2]]></fr:tex> closes the reading end of the pipe resulting in the <html:code>Broken pipe</html:code> system error.</html:p>
                        <html:p>Let's replace <fr:tex display="inline"><![CDATA[\texttt {cmd}_1]]></fr:tex> with the following <html:em>compound command</html:em> that also produces, left to its own devices, infinite output on <html:code>stdout</html:code>.</html:p>
                        <html:pre class="hilite">
                          <html:code>
                            <html:span class="sh-keyword-control">while</html:span>
                            <html:span class="sh-meta-scope-while-loop"> </html:span>
                            <html:span class="sh-support-function-builtin">echo</html:span>
                            <html:span class="sh-meta-scope-while-loop"> </html:span>
                            <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                            <html:span class="sh-string-quoted-double">some bytes</html:span>
                            <html:span class="sh-punctuation-definition-string-end">"</html:span>
                            <html:span class="sh-keyword-operator-list">;</html:span>
                            <html:span class="sh-meta-scope-while-loop"> </html:span>
                            <html:span class="sh-keyword-control">do</html:span>
                            <html:span class="sh-meta-scope-while-loop">
</html:span>
                            <html:span class="sh-meta-scope-while-loop">  </html:span>
                            <html:span class="sh-support-function-builtin">:</html:span>
                            <html:span class="sh-meta-scope-while-loop">
</html:span>
                            <html:span class="sh-keyword-control">done</html:span>
                            <html:span class="sh-source"> </html:span>
                            <html:span class="sh-keyword-operator-pipe">|</html:span>
                            <html:span class="sh-source"> hxd.xxd -l100
</html:span>
                          </html:code>
                        </html:pre>
                        <html:p>In this example, we no longer have the luxury of being able to <html:code>fork</html:code> and <html:code>exec</html:code>. The compound command (the <html:code>while</html:code> loop) forces our hand -- we must rely on the internal execution logic of the shell to keep it going. How then do we prevent <fr:tex display="inline"><![CDATA[\texttt {cmd}_1]]></fr:tex> from blocking our shell from spawning <fr:tex display="inline"><![CDATA[\texttt {cmd}_2]]></fr:tex>?</html:p>
                        <html:p>For most shells, this is where they might <html:code>fork</html:code> themselves and allow the <html:code>while</html:code> loop to execute in the child. The parent continuing on. Alternatively, the work could continue on in a separate thread (in OCaml parlance, domain). Fibers (green threads, coroutines etc.) are only possible if there are a sufficient number of co-operative yields (which can be hard to track: any control flow or shell built-in would need to ensure they periodically yield to the other commands in the pipeline).</html:p>
                        <html:p>With that being said, it would seem we have workarounds to ensure the parallel aspects of pipelines are possible to build without forking the shell state itself.</html:p>
                      </fr:mainmatter>
                    </fr:tree>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>4</fr:month>
                          <fr:day>28</fr:day>
                        </fr:date>
                        <fr:title text="Command Reentrancy">Command Reentrancy</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>Command reentrancy deals with the fact that commands in a pipeline run in <html:em>subshells</html:em>. In practice, this means any <fr:tex display="inline"><![CDATA[\texttt {cmd}_i]]></fr:tex> should not impact the <html:em>execution environment</html:em> of any other <fr:tex display="inline"><![CDATA[\texttt {cmd}_j]]></fr:tex>. Commands may certainly interfere with one another: a rogue <html:code>rm</html:code> might remove a file that some other command was about to read or was in the middle of reading!</html:p>
                        <html:p><fr:tex display="inline"><![CDATA[\S 2.13]]></fr:tex> of the POSIX shell specification describes the execution environment. In <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> we lift this into the <html:code>ctx</html:code> type making it completely immutable; the only way to update the context is to destructively declare new fields, e.g., <html:code><![CDATA[{ ctx with cwd = "/home/bactrian" }]]></html:code>. All shell built-ins work directly from the context as opposed to any state that might <html:em>also</html:em> be kept by the process itself (e.g., we do not call <html:code>getcwd(3)</html:code> to expand <html:code>PWD</html:code>, but instead read it from the context).</html:p>
                        <html:p>Most state is handled nicely by this <html:em>functional</html:em> implementation of the shell; except open (or not) file descriptors. The <html:code>exec</html:code> built-in is particularly thorny in this regard:</html:p>
                        <html:blockquote>
                          <html:p>If exec is specified with no operands, any redirections associated with the exec command shall be made in the current shell execution environment.</html:p>
                        </html:blockquote>
                        <html:p>So, <html:code>exec 3&gt;&amp;1</html:code> says "in the current execution environment map file descriptor <html:code>3</html:code> to <html:code>stdout</html:code>" and <html:code>exec &gt; /dev/null</html:code> says "in the current execution environment map <html:code>stdout</html:code> to <html:code>/dev/null</html:code>. This is often used to silence anything writing to <html:code>stdout</html:code>.</html:p>
                        <html:pre class="hilite">
                          <html:code>
                            <html:span class="sh-entity-name-function">info</html:span>
                            <html:span class="sh-meta-function"> </html:span>
                            <html:span class="sh-punctuation-definition-arguments"><![CDATA[()]]></html:span>
                            <html:span class="sh-meta-function"> </html:span>
                            <html:span class="sh-punctuation-definition-group"><![CDATA[{]]></html:span>
                            <html:span class="sh-meta-scope-group">
</html:span>
                            <html:span class="sh-meta-scope-group">  </html:span>
                            <html:span class="sh-support-function-builtin">echo</html:span>
                            <html:span class="sh-meta-scope-group"> </html:span>
                            <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                            <html:span class="sh-string-quoted-double">INFO: </html:span>
                            <html:span class="sh-punctuation-definition-variable">$</html:span>
                            <html:span class="sh-variable-other-positional">1</html:span>
                            <html:span class="sh-punctuation-definition-string-end">"</html:span>
                            <html:span class="sh-meta-scope-group"> </html:span>
                            <html:span class="sh-keyword-operator-redirect">&gt;&amp;3</html:span>
                            <html:span class="sh-meta-scope-group">
</html:span>
                            <html:span class="sh-punctuation-definition-group"><![CDATA[}]]></html:span>
                            <html:span class="sh-punctuation-definition-function">
</html:span>
                            <html:span class="sh-source">
</html:span>
                            <html:span class="sh-support-function-builtin">exec</html:span>
                            <html:span class="sh-source"> </html:span>
                            <html:span class="sh-keyword-operator-redirect">3&gt;&amp;1</html:span>
                            <html:span class="sh-source"> </html:span>
                            <html:span class="sh-keyword-operator-redirect">&gt;</html:span>
                            <html:span class="sh-source"> /dev/null
</html:span>
                            <html:span class="sh-source">info </html:span>
                            <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                            <html:span class="sh-string-quoted-double">Starting up server...</html:span>
                            <html:span class="sh-punctuation-definition-string-end">"</html:span>
                            <html:span class="sh-source">
</html:span>
                            <html:span class="sh-support-function-builtin">echo</html:span>
                            <html:span class="sh-source"> </html:span>
                            <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                            <html:span class="sh-string-quoted-double">Some noisy program spamming STDOUT</html:span>
                            <html:span class="sh-punctuation-definition-string-end">"</html:span>
                            <html:span class="sh-source">
</html:span>
                            <html:span class="sh-source">info </html:span>
                            <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                            <html:span class="sh-string-quoted-double">All is quiet...</html:span>
                            <html:span class="sh-punctuation-definition-string-end">"</html:span>
                            <html:span class="sh-source">
</html:span>
                          </html:code>
                        </html:pre>
                        <html:p>If we are not running shell built-ins inside new child processes, then we must lift the file descriptor table into the context as well and <html:em>disallow</html:em> any two built-ins to run in parallel. Consider this example:</html:p>
                        <html:pre class="hilite">
                          <html:code>
                            <html:span class="sh-support-function-builtin">exec</html:span>
                            <html:span class="sh-source"> </html:span>
                            <html:span class="sh-keyword-operator-redirect">&lt;</html:span>
                            <html:span class="sh-source"> ./hello.txt
</html:span>
                            <html:span class="sh-source">
</html:span>
                            <html:span class="sh-keyword-control">while</html:span>
                            <html:span class="sh-meta-scope-while-loop"> </html:span>
                            <html:span class="sh-support-function-builtin">read</html:span>
                            <html:span class="sh-meta-scope-while-loop"> line</html:span>
                            <html:span class="sh-keyword-operator-list">;</html:span>
                            <html:span class="sh-meta-scope-while-loop"> </html:span>
                            <html:span class="sh-keyword-control">do</html:span>
                            <html:span class="sh-meta-scope-while-loop">
</html:span>
                            <html:span class="sh-meta-scope-while-loop">  </html:span>
                            <html:span class="sh-support-function-builtin">echo</html:span>
                            <html:span class="sh-meta-scope-while-loop"> </html:span>
                            <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                            <html:span class="sh-string-quoted-double">Got </html:span>
                            <html:span class="sh-punctuation-definition-variable">$</html:span>
                            <html:span class="sh-variable-other-normal">line</html:span>
                            <html:span class="sh-punctuation-definition-string-end">"</html:span>
                            <html:span class="sh-meta-scope-while-loop"> </html:span>
                            <html:span class="sh-keyword-operator-redirect">&gt;&amp;2</html:span>
                            <html:span class="sh-meta-scope-while-loop">
</html:span>
                            <html:span class="sh-keyword-control">done</html:span>
                            <html:span class="sh-source"> </html:span>
                            <html:span class="sh-keyword-operator-pipe">|</html:span>
                            <html:span class="sh-source"> </html:span>
                            <html:span class="sh-punctuation-definition-subshell"><![CDATA[(]]></html:span>
                            <html:span class="sh-meta-scope-subshell">exec </html:span>
                            <html:span class="sh-keyword-operator-redirect">&lt;</html:span>
                            <html:span class="sh-meta-scope-subshell"> ./world.txt</html:span>
                            <html:span class="sh-keyword-operator-list">;</html:span>
                            <html:span class="sh-meta-scope-subshell"> cat</html:span>
                            <html:span class="sh-punctuation-definition-subshell"><![CDATA[)]]></html:span>
                            <html:span class="sh-source">
</html:span>
                          </html:code>
                        </html:pre>
                        <html:p>If we allow <html:code>exec</html:code> to <html:em>change</html:em> the set of open file descriptors of the shell process directly, then individual commands in the pipeline are no longer re-entrant. The pipeline here is not being used to pipe bytes from one command to another, but instead to expose concurrency and the subshell behaviour.</html:p>
                        <html:p>The second <html:code>exec &lt; ./world.txt</html:code> has no impact on the currently opened file on <html:code>stdin</html:code>, the while loop continues to read <html:code>./hello.txt</html:code>. This is straightforward to implement with <html:code>fork</html:code> as you can <html:code>dup2</html:code> any files you need. The bookkeeping and isolation is all provided by the POSIX process semantics. Without <html:code>fork</html:code>, the file descriptors would have to be saved and restored on every suspension and resumption to provide an illusion of isolation.</html:p>
                        <html:p>We could relax our rule to "disallow any two built-ins to run in parallel" by taking a lock on the file descriptor table, but considering the lock is held from the point of resumption until the next suspension it might not achieve very much in practice.</html:p>
                        <html:p>Some of the intricacies of scheduling are also covered in <fr:link href="/greenberg2020smoosh/" title="Executable formal semantics for the POSIX shell" uri="https://patrick.sirref.org/greenberg2020smoosh/" display-uri="greenberg2020smoosh" type="local">Michael Greenberg's <html:em>An executable and formal semantics of the POSIX shell</html:em></fr:link>.</html:p>
                        <html:blockquote>
                          <html:p>To motivate the question, consider the following two pipelines:</html:p>
                          <html:p>(1) <html:code>while true; do echo 5; done | true</html:code></html:p>
                          <html:p>(2) <html:code><![CDATA[while true; do echo 5; done | { read x; echo $((x+42)); }]]></html:code></html:p>
                          <html:p>Both pipelines spawn two processes, both of which use shell builtins exclusively: neither of these pipelines needs to make an <html:code>execve</html:code> system call (though some systems may implement true or echo as executables, Smoosh and most shells build them in).</html:p>
                        </html:blockquote>
                        <html:p>Scheduling is an opaque part of the <fr:link href="/posixSpec/" title="IEEE/Open Group Standard for Information Technology--Portable Operating System Interface (POSIX™) Base Specifications, Issue 8" uri="https://patrick.sirref.org/posixSpec/" display-uri="posixSpec" type="local">POSIX specification</fr:link>. Indeed, as we have argued here, there is <html:em>no</html:em> semantics for scheduling. Here is a similar pipeline run through a script that <html:code>strace</html:code>s for <html:code>clone</html:code> and <html:code>fork</html:code> calls on various shells:</html:p>
                        <html:pre><![CDATA[% ./test/check_fork.sh "while echo 5; do : ; done | true"
bash  3
dash  3
yash  1 
zsh   1
ksh   1
ash   3]]></html:pre>
                        <html:p>It might make an interesting use case for effects in OCaml -- whilst we can certainly piggyback on <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link>'s existing suspension mechanisms, this is slightly different in that we need to ensure some preconditions are true before resuming certain computations. Fiber-local state is insufficient as programs implicitly depend on global state of the process (in particular the FD table). In this sense, we need some form of suspension-resumption hooks.</html:p>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false" toc="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>4</fr:month>
                  <fr:day>18</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/weekly-2026-w16/</fr:uri>
                <fr:display-uri>weekly-2026-w16</fr:display-uri>
                <fr:route>/weekly-2026-w16/</fr:route>
                <fr:title text="Landlocked Eio">Landlocked Eio</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>I spent the start of last week wrapping up the "local" CI I mentioned <fr:link href="/weekly-2026-w15/" title="Moving away from Github" uri="https://patrick.sirref.org/weekly-2026-w15/" display-uri="weekly-2026-w15" type="local">the previous week</fr:link>. Unfortunately it was mostly a packaging nightmare, followed by missing features in <html:code>ocaml-ci-local</html:code>. It would be great to have a richer CLI tool for running tests on a directory of git repositories, but unfortunately most of the existing tooling has prioritised the Github/Gitlab etc. APIs.</html:p>
                <html:p>After coming across <fr:link href="https://minimal.dev/" type="external">minimal</fr:link> during the week, I took it as an opportunity to learn a little more about Linux's <fr:link href="https://lwn.net/Articles/859908/" type="external">Landlock</fr:link> mechanism.</html:p>
                <fr:tree show-metadata="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>18</fr:day>
                    </fr:date>
                    <fr:uri>https://patrick.sirref.org/landlocked-eio/</fr:uri>
                    <fr:display-uri>landlocked-eio</fr:display-uri>
                    <fr:route>/landlocked-eio/</fr:route>
                    <fr:title text="Landlocked Eio ">Landlocked Eio </fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p><fr:link href="https://lwn.net/Articles/859908/" type="external">Landlock</fr:link> is a sandboxing mechanism that has existed in the Linux kernel since 5.13. It allows unprivileged processes a means to restrict their ambient rights (e.g. creating a file or opening a network connection). These rights are ambient in the <fr:link href="https://en.wikipedia.org/wiki/Ambient_authority" type="external">ambient authority</fr:link> sense, i.e., any process can attempt to perform an action without needing access to any particular capability to do so.</html:p>
                    <html:p>Ambient systems are often contrasted with capability-based ones. In terms of operating systems, this is most commonly illustrated with <fr:link href="/miller2003capability/" title="Capability myths demolished" uri="https://patrick.sirref.org/miller2003capability/" display-uri="miller2003capability" type="local">access-control-lists-as-columns and capabilities-as-rows in an access matrix</fr:link>.</html:p>
                    <html:p><fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link> is a capability-based library in OCaml -- resources are provided at the start of the application and can be passed around wherever they are needed. The design is covered in <fr:link href="/talex5/" title="Thomas Leonard" uri="https://patrick.sirref.org/talex5/" display-uri="talex5" type="local">Thomas Leonard</fr:link>'s <fr:link href="https://roscidus.com/blog/blog/2023/04/26/lambda-capabilities/" type="external">lambda capabilities blog post</fr:link>. Unfortunately, any application (willingly or via a third-party library) may ambiently access OS resources. In OCaml, this may be by using the standard library functions (e.g. <html:code>Sys.readdir</html:code>), things from the <html:code>Unix</html:code> module, a C FFI function or by spawning a subprocess.</html:p>
                    <html:p>I had a go at "landlocking" Eio applications last week after stumbling across <fr:link href="https://minimal.dev/" type="external">minimal's</fr:link> <fr:link href="github.com/gominimal/hakoniwa" type="external">hakoniwa</fr:link> library for process isolation (which is very reminiscent of <fr:link href="/void-processes/" title="Vpnkit, Void Processes, LSP Servers › Void Processes " uri="https://patrick.sirref.org/void-processes/" display-uri="void-processes" type="local">void processes</fr:link>).</html:p>
                    <html:pre class="hilite">
                      <html:code>
                        <html:span class="ocaml-keyword-other">module</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Landlock</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio_linux</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Landlock</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-keyword-other">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Path</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">run_eio</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">file</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">traceln</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">Writing to file</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Path</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">save</html:span>
                        <html:span class="ocaml-source"> ~</html:span>
                        <html:span class="ocaml-source">create</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-colon punctuation">:</html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-constant-language-polymorphic-variant">`Exclusive</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-octal-integer">0o644</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">file</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">hello, world!</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">traceln</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">Reading file: </html:span>
                        <html:span class="ocaml-constant-character-printf"><![CDATA[%s]]></html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Path</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">load</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">file</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Path</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">unlink</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">file</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">bad_program</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-unit"><![CDATA[()]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">In_channel</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">with_open_bin</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">/etc/passwd</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">In_channel</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">input_all</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword-operator">|&gt;</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">traceln</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">/etc/passwd: </html:span>
                        <html:span class="ocaml-constant-character-printf"><![CDATA[%s]]></html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-keyword-other">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-unit"><![CDATA[()]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio_linux</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">run</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">@@</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">fun</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">env</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-&gt;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Path</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">with_open_dir</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Stdenv</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">cwd</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">env</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">.</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">@@</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">fun</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">dir</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-&gt;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">parent_fd</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> 
</html:span>
                        <html:span class="ocaml-source">    </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio_unix</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Resource</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">fd_opt</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source">fst</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">dir</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">|&gt;</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Option</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">get</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword-other">in</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">file</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">dir</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">hello.txt</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">in</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">rules</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">    </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Landlock</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Rule</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">      </html:span>
                        <html:span class="ocaml-source"><![CDATA[[]]></html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">        </html:span>
                        <html:span class="ocaml-source">path_beneath</html:span>
                        <html:span class="ocaml-source"> ~</html:span>
                        <html:span class="ocaml-source">parent_fd</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">          ~</html:span>
                        <html:span class="ocaml-source">flags</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-colon punctuation">:</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Flags</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Fs</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">            </html:span>
                        <html:span class="ocaml-source">write_file</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">+</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">read_file</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">+</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">make_reg</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">+</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">remove_file</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">           </html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">      </html:span>
                        <html:span class="ocaml-source"><![CDATA[]]]></html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword-other">in</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword-other">match</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Landlock</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">enter</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">rules</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">with</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword-other">|</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Ok</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-unit"><![CDATA[()]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-&gt;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">      </html:span>
                        <html:span class="ocaml-source">run_eio</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">file</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">      </html:span>
                        <html:span class="ocaml-source">bad_program</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-unit"><![CDATA[()]]></html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword-other">|</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Error</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-polymorphic-variant">`Not_supported</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-&gt;</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">traceln</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">Landlock not supported!</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-source">
</html:span>
                      </html:code>
                    </html:pre>
                    <html:p>Running this program (with the necessary privileges) prints the following.</html:p>
                    <html:pre><![CDATA[+Writing to file
+Reading file: hello, world!
Fatal error: exception Sys_error("/etc/passwd: Permission denied")]]></html:pre>
                    <html:p>Eio already has a similar function for the BSDs using <fr:link href="https://github.com/ocaml-multicore/eio#filesystem-access" type="external">capsicum</fr:link>. In capsicum, <fr:link href="https://man.freebsd.org/cgi/man.cgi?query=cap_enter" type="external">processes enter a capability mode</fr:link>:</html:p>
                    <html:blockquote>
                      <html:p><html:code>cap_enter()</html:code> places the current process into capability mode, a mode of execution in which processes may	only issue system calls	operating on file descriptors or reading limited global system state.</html:p>
                    </html:blockquote>
                    <html:p>This plays nicely with <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link>'s capabilities. Within the scope of <html:code>Eio.Path.with_open_dir</html:code>, the directory will remain usable after entering "cap" mode. This is not the case with landlock unless you add a specific <html:code>path_beneath</html:code> rule to the ruleset (like in the example above). However, you could imagine plumbing the necessary information throughout the <html:code>Eio_linux</html:code> backend for it to build a set of rules that can be applied at any given moment in order to mimic the capsicum semantics.</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>18</fr:day>
                    </fr:date>
                    <fr:uri>https://patrick.sirref.org/self-host-music/</fr:uri>
                    <fr:display-uri>self-host-music</fr:display-uri>
                    <fr:route>/self-host-music/</fr:route>
                    <fr:title text="Self-hosting Music ">Self-hosting Music </fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>After my friend, <fr:link href="https://gretathompson.com/" type="external">Greta</fr:link>, sent me some articles about music streaming services and their exploitation of artists, I decided enough was enough and setup <fr:link href="https://www.navidrome.org/" type="external">navidrome</fr:link>. It is compatible with <fr:link href="https://www.subsonic.org/pages/api.jsp" type="external">Subsonic</fr:link> and so far I'm very pleased to be streaming my own music to my devices. I am using <fr:link href="https://substreamer.org/" type="external">substreamer</fr:link> on iOS. On my laptop, I tried out <fr:link href="https://github.com/rossberg/camp" type="external">Andreas Rossberg's Camp</fr:link></html:p>
                    <html:blockquote>
                      <html:p>What you got here is an old-school music player heavily inspired by good old <![CDATA[Winamp [1], with a focus on decent music library and playlist handling.]]></html:p>
                    </html:blockquote>
                    <html:p>I took <fr:link href="/anilmadhavapeddy/" title="Anil Madhavapeddy" uri="https://patrick.sirref.org/anilmadhavapeddy/" display-uri="anilmadhavapeddy" type="local">Anil's</fr:link> <fr:link href="https://github.com/avsm/oi" type="external">OCaml Installer</fr:link> tool for a spin with great success... after a few mishaps mostly related to system dependencies.</html:p>
                    <html:pre><![CDATA[$ nix-shell -p pkg-config gcc pulseaudio miniaudio libxi libxrandr libxinerama libffi mesa libxcursor libGL 
$ oi-linux-x86_64 run --with=https://github.com/patricoferris/camp#nix -- camp]]></html:pre>
                    <html:p>The fixes in my fork of Camp are just to install the assets using <html:code>dune</html:code> and also statically provide the <html:code>-lpulse</html:code> flag (which should probably be dynamically picked up). Otherwise, raylib or miniaudio selects the <html:code>Null</html:code> playback device. Roll-on <fr:link href="https://ryan.freumh.org/papers/2026-package-calculus.html" type="external">cross-ecosystem package management</fr:link>.</html:p>
                    <html:img src="/bafkrmifuphhk6ys7hpaqm2jaad6qr7253nevf6ddu4bh7d5tjyasp35kum.png" />
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false" toc="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>4</fr:month>
                  <fr:day>13</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/weekly-2026-w15/</fr:uri>
                <fr:display-uri>weekly-2026-w15</fr:display-uri>
                <fr:route>/weekly-2026-w15/</fr:route>
                <fr:title text="Moving away from Github">Moving away from Github</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>A short week for me as I was hiking with family in and around <fr:link href="https://en.wikipedia.org/wiki/Porthmadog" type="external">Porthmadog, Wales</fr:link>.</html:p>
                <html:img width="600" src="/bafkrmig6yukypbk4lk2xxq56dm3yfc7i7wm4vrwihpxs37ywu6s63wq3ri.jpg" />
                <fr:tree show-metadata="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>13</fr:day>
                    </fr:date>
                    <fr:title text="Reliance on Github">Reliance on Github</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>I joined Github in 2016, but only began using it in earnest (according to my Github activity) in July 2018: right after Microsoft announced its intent to acquire Github; and just before <fr:link href="https://www.computerworld.com/article/3535690/microsoft-acquisitions-a-timeline-of-growth-and-a-few-missteps.html" type="external">they closed the $7.5bn deal</fr:link>. I originally joined to boost my résumé and I hoped to receive the odd open-source contribution to projects I was working on (like <fr:link href="https://github.com/patricoferris/ppx_deriving_yaml" type="external">ppx_deriving_yaml</fr:link>). Since then, I have used Github extensively both at work (<fr:link href="https://tarides.com/" type="external">Tarides</fr:link>) and for more personal projects.</html:p>
                    <html:p>There have been many reasons to jump ship from Github ever since the Microsoft acquisition (e.g. <fr:link href="https://github.com/resources/insights/2026-pricing-changes-for-github-actions" type="external">their planned (and quickly unplanned) announcement to charge self-hosted Github runners</fr:link>). Ultimately, I was convinced when I noticed the new AI tool on the homepage with options to select your "versatile and highly intelligent" model. There is no need for me to stay on the platform for my personal projects. AI integration into Github has nullified those earlier motivations for hosting my projects on the site. The number of contributions has increased, but many of them fix superficial elements of an underlying bug. In my (maybe too uncharitable) imagination, it is all too easy for contributors to prompt their <html:em>built-in</html:em> LLM to "fix this broken unit test" and for the fix to layer a single-use workaround on top, instead of identifying and correcting the relevant code. This is, at least, what it looks like to me when I receive a new review request on my open-source projects.</html:p>
                    <html:p>At the end of last week I set up my own Git infrastructure following <fr:link href="/ryangibb/" title="Ryan Gibb" uri="https://patrick.sirref.org/ryangibb/" display-uri="ryangibb" type="local">Ryan's</fr:link> <fr:link href="https://github.com/ryangibb/nixos" type="external">nixos</fr:link> setup. In the future, the source of truth for my projects will be <fr:link href="https://git.sirref.org/" type="external">git.sirref.org</fr:link> (I will likely continue to mirror to <fr:link href="https://tangled.org/patrick.sirref.org/" type="external">tangled.org</fr:link> or Github).</html:p>
                    <fr:tree show-metadata="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>4</fr:month>
                          <fr:day>13</fr:day>
                        </fr:date>
                        <fr:title text="Reduce, Reuse, Recycle">Reduce, Reuse, Recycle</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>Part of self-hosting your own source code is managing some form of automatic testing. I hesitate to describe it as "continuous integration" as the term is confusing and overloaded. Luckily, parts of the OCaml community have been <fr:link href="https://github.com/ocurrent" type="external">building test infrastructure for a while</fr:link>.</html:p>
                        <html:p>I have a proclivity for rebuilding things from scratch be it to learn or to build something the way I want. It is a proclivity I am trying to reign in as I find less and less time to hack on projects. I think this is a good thing. Reusing and recycling existing technologies, in earnest, feels like a pushback on the <fr:link href="https://en.wikipedia.org/wiki/Not_invented_here" type="external">not-invented-here syndrome</fr:link> that seems to be snowballing thanks to LLMs and code generation tools.</html:p>
                        <html:p>To that end, I dusted off my <fr:link href="https://www.ocurrent.org/" type="external">OCurrent</fr:link> knowledge, and repurposed the "local" mode of <fr:link href="https://ocaml.ci.dev/" type="external">OCaml-CI</fr:link> to work with my <fr:link href="https://git.sirref.org/" type="external">git repositories</fr:link>. After battling through nested layers of git submodules, I managed to <html:code>Nix</html:code>-ify this and get it running at <fr:link href="https://tests.sirref.org/" type="external">tests.sirref.org</fr:link>. I needed to add multi-repository support as well as <fr:link href="https://ocaml.org/p/current/0.7.4/doc/current/Current/Monitor/index.html" type="external">Current.Monitor</fr:link>s for the local git refs. By no means perfect, but it is nice to own the testing infrastructure (and the cost of downloading OCaml images and the compute to run the tests).</html:p>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false" toc="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>4</fr:month>
                  <fr:day>3</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/weekly-2026-w14/</fr:uri>
                <fr:display-uri>weekly-2026-w14</fr:display-uri>
                <fr:route>/weekly-2026-w14/</fr:route>
                <fr:title text="Forking in Shells &amp; Library Maintenance">Forking in Shells &amp; Library Maintenance</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>This week, <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> got close to executing <html:code>apt-get install ca-certificates</html:code> and running Debian's <html:code>debootstrap</html:code> scripts correctly. <fr:link href="https://patrick.sirref.org/nathan/" type="external">Nathan</fr:link> and I released <fr:link href="/ppxlib/" title="Ppxlib" uri="https://patrick.sirref.org/ppxlib/" display-uri="ppxlib" type="local">Ppxlib</fr:link> <html:code>0.38.0</html:code>, I added support to <fr:link href="https://github.com/geocaml/ocaml-proj" type="external">ocaml-proj</fr:link> for compiling to the browser and I fixed a long-standing bug in <fr:link href="https://github.com/patricoferris/hilite" type="external">hilite</fr:link>.</html:p>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:title text="Forking in Merry">Forking in Merry</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>So far, <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> has managed to support many features of the POSIX shell specification without needing to do a <html:code>fork(2)</html:code> without an <html:code>exec</html:code>. Or, to put it another way, all the forking that <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> does, will only be followed by running C code. This is actually a feature of <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link>'s <html:code>Process</html:code> API. It is required to make process execution safe in the context of multiple domains in OCaml (mostly due to ensuring consistency within the garbage collector). However, it is perfectly okay to <html:code>fork</html:code> and run OCaml code provided there is only a single domain.</html:p>
                    <html:p>In a shell, many features of the shell language require that commands (or built-ins, function applications etc.) execute within a <html:code>subshell</html:code>. The specification does not go into detail about how shells should implement this, but many choose to <html:code>fork</html:code> in order to preserve some state in the parent (for example, the file descriptor table). Some shells try to minimise the number of forks as an optimisation. The <fr:link href="/korn1996korn/" title="The New Korn Shell" uri="https://patrick.sirref.org/korn1996korn/" display-uri="korn1996korn" type="local">korn shell (ksh)</fr:link> is one such shell.</html:p>
                    <html:blockquote>
                      <html:p>Using the notation <html:code>$(command)</html:code> will cause <html:code>command</html:code> to execute in a subshell of the current ksh. In many instances, ksh will not actually fork/exec a subshell when command is a built-in or a shell function.</html:p>
                    </html:blockquote>
                    <html:p>By trying to adhere to Multicore OCaml's "<html:em>thou shalt not fork</html:em>" commandment, <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is much more similar to <html:code>ksh</html:code> in this regard. However, whenever there is an interaction with a shell built-in and semantics that need a child process, things get tricky very quickly. Consider the following:</html:p>
                    <html:pre class="hilite">
                      <html:code>
                        <html:span class="sh-keyword-control">while</html:span>
                        <html:span class="sh-meta-scope-while-loop"> </html:span>
                        <html:span class="sh-support-function-builtin">echo</html:span>
                        <html:span class="sh-meta-scope-while-loop"> </html:span>
                        <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                        <html:span class="sh-string-quoted-double">hello</html:span>
                        <html:span class="sh-punctuation-definition-string-end">"</html:span>
                        <html:span class="sh-keyword-operator-list">;</html:span>
                        <html:span class="sh-meta-scope-while-loop"> </html:span>
                        <html:span class="sh-keyword-control">do</html:span>
                        <html:span class="sh-meta-scope-while-loop">
</html:span>
                        <html:span class="sh-meta-scope-while-loop">  </html:span>
                        <html:span class="sh-support-function-builtin">:</html:span>
                        <html:span class="sh-meta-scope-while-loop">
</html:span>
                        <html:span class="sh-keyword-control">done</html:span>
                        <html:span class="sh-source"> </html:span>
                        <html:span class="sh-keyword-operator-pipe">|</html:span>
                        <html:span class="sh-source"> head -n 3
</html:span>
                      </html:code>
                    </html:pre>
                    <html:p>We have a mix of shell built-ins, normal commands, a compound command (the <html:code>while</html:code> loop) and a pipeline. Each individual command in the pipeline requires you to run them in a subshell and set them up <html:em>before</html:em> executing them. Without a <html:code>fork</html:code> for that first command, you may end up looping forever which is not the intended behaviour here. I had a good conversation with <fr:link href="/mdales/" title="Michael W. Dales" uri="https://patrick.sirref.org/mdales/" display-uri="mdales" type="local">Michael</fr:link> about the implications of this for reproducibility.</html:p>
                    <html:p>For now, I have started implementing a <html:code>fork</html:code> for some of the shell features whilst still trying to maintain the functional core of the implementation.</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:title text="An OCaml 5.5 compatible Ppxlib">An OCaml 5.5 compatible Ppxlib</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p><fr:link href="/nathanreb/" title="Nathan Rebours" uri="https://patrick.sirref.org/nathanreb/" display-uri="nathanreb" type="local">Nathan</fr:link> and I released an OCaml 5.5 compatible version of <fr:link href="/ppxlib/" title="Ppxlib" uri="https://patrick.sirref.org/ppxlib/" display-uri="ppxlib" type="local">ppxlib</fr:link>.</html:p>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>30</fr:day>
                        </fr:date>
                        <fr:uri>https://patrick.sirref.org/ppxlib-release-0-38-0/</fr:uri>
                        <fr:display-uri>ppxlib-release-0-38-0</fr:display-uri>
                        <fr:route>/ppxlib-release-0-38-0/</fr:route>
                        <fr:title text="Ppxlib Releases › 0.38.0 "><fr:link href="/ppxlib-releases/" title="Ppxlib Releases" uri="https://patrick.sirref.org/ppxlib-releases/" display-uri="ppxlib-releases" type="local">Ppxlib Releases</fr:link> › 0.38.0 </fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p><fr:link href="/nathanreb/" title="Nathan Rebours" uri="https://patrick.sirref.org/nathanreb/" display-uri="nathanreb" type="local">Nathan</fr:link> and I <fr:link href="https://github.com/ocaml/opam-repository/pull/29563" type="external">released ppxlib.0.38.0</fr:link> last week. Its main feature is full <html:em>migration</html:em> support for the upcoming OCaml 5.5 compiler (currently in its <html:code>alpha3</html:code> release). This means supporting the handful of new features landing in OCaml 5.5: <fr:link href="/modular-explicits/" title="Modular Explicits in OCaml" uri="https://patrick.sirref.org/modular-explicits/" display-uri="modular-explicits" type="local">modular explicits</fr:link>, external type declarations and arbitrary "local" structure items.</html:p>
                        <html:p>As <fr:link href="https://patrick.sirref.org/nathenreb/" type="external">Nathan</fr:link> and I continue to find a plausible maintenance story for <fr:link href="/ppxlib/" title="Ppxlib" uri="https://patrick.sirref.org/ppxlib/" display-uri="ppxlib" type="local">ppxlib</fr:link>, <fr:link href="https://patrick.sirref.org/nathenreb/" type="external">Nathan</fr:link> has opened <fr:link href="https://github.com/ocaml/ocaml/issues/14668" type="external">an issue on the OCaml compiler to discuss the idea of adding additional extension points to the language</fr:link>.</html:p>
                        <html:p>This comes from the desire to be able to store encoded versions of new OCaml features inside older abstract syntax trees.</html:p>
                        <html:p>There are also some nice bug fixes in there too:</html:p>
                        <html:ul>
                          <html:li>
                            <html:p><fr:link href="https://github.com/ocaml-ppx/ppxlib/pull/613" type="external">A potential OOM</fr:link> loop has now been removed.</html:p>
                          </html:li>
                          <html:li>
                            <html:p>
                              <fr:link href="https://github.com/ocaml-ppx/ppxlib/pull/619" type="external">Locations have been restored to long identifiers!</fr:link>
                            </html:p>
                          </html:li>
                        </html:ul>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:title text="Browser support for ocaml-proj">Browser support for ocaml-proj</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>A while ago <fr:link href="https://github.com/geocaml/ocaml-proj" type="external">I built some "modern" bindings to PROJ4 in OCaml</fr:link>. After reading <fr:link href="https://patrick.sirref.org/jonludlam/" type="external">Jon Ludlam</fr:link>'s <fr:link href="https://jon.recoil.org/blog/2026/03/weeknotes-2026-12.html" type="external">weeknotes</fr:link> (and speaking with him and <fr:link href="/anilmadhavapeddy/" title="Anil Madhavapeddy" uri="https://patrick.sirref.org/anilmadhavapeddy/" display-uri="anilmadhavapeddy" type="local">Anil</fr:link>, I thought it might be nice to add a Javascript backend to those bindings). This was relatively straight-forward using <fr:link href="https://dune.readthedocs.io/en/stable/virtual-libraries.html" type="external">Dune's virtual libraries</fr:link> and is <fr:link href="https://github.com/geocaml/ocaml-proj/blob/main/src/js/proj.ml" type="external">available on Github</fr:link>.</html:p>
                    <html:p>With virtual libraries, your own data analysis could (if you wished) depend solely on the <html:code>proj</html:code> library and later choose to either link it with <html:code>proj.c</html:code> or <html:code>proj.js</html:code> depending on where the analysis is being deployed.</html:p>
                    <html:p>I briefly looked at WASM support, but quickly realised there was not much appetite for it and trying to compile around the C FFI was going to be hard.</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>4</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:title text="Hilite updates">Hilite updates</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>Since I helped <fr:link href="https://ocaml.org/" type="external">relaunch the ocaml.org</fr:link> website a few years ago, I have maintained <fr:link href="https://github.com/patricoferris/hilite" type="external">hilite</fr:link>, a tool for doing build-time syntax highligting.</html:p>
                    <html:p>It is being used on the <fr:link href="https://ocaml.org/" type="external">ocaml.org</fr:link> website, <fr:link href="https://github.com/xhtmlboi/yocaml/blob/45858f4b25730149dae3735e7fcdb9111ac1f9eb/yocaml_markdown.opam#L18" type="external">in yocaml_markdown</fr:link> and also in <fr:link href="/mdales/" title="Michael W. Dales" uri="https://patrick.sirref.org/mdales/" display-uri="mdales" type="local">Michael's</fr:link> <fr:link href="https://github.com/mdales/webplats/blob/cf0ea95a66bae52f02a00739eb02564fc94183ee/webplats.opam#L19" type="external">webplats</fr:link> (when it works...).</html:p>
                    <html:p>And it was <fr:link href="/mdales/" title="Michael W. Dales" uri="https://patrick.sirref.org/mdales/" display-uri="mdales" type="local">Michael</fr:link> who led me to finally fixing and adding syntax highlighting support for Python and Go. Once I was on this roll, I finally took a look at fixing the long-standing bug of trying to highlight ocaml-mdx code, like the following:</html:p>
                    <html:pre class="hilite">
                      <html:code>
                        <html:span class="ocaml-mdx-hash">#</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-float">50.123</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-mdx-hash">#</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">1</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">file.ml</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                      </html:code>
                    </html:pre>
                    <html:p>In fact, this code now highlights fine, but was the source of the bug. The syntax highlighting grammer confuses the ocaml-mdx <html:code>#</html:code> as a toplevel directive and trouble ensues.</html:p>
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false" toc="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>3</fr:month>
                  <fr:day>30</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/weekly-2026-w13/</fr:uri>
                <fr:display-uri>weekly-2026-w13</fr:display-uri>
                <fr:route>/weekly-2026-w13/</fr:route>
                <fr:title text="Ppxlib release and Merry updates">Ppxlib release and Merry updates</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>30</fr:day>
                    </fr:date>
                    <fr:uri>https://patrick.sirref.org/ppxlib-release-0-38-0/</fr:uri>
                    <fr:display-uri>ppxlib-release-0-38-0</fr:display-uri>
                    <fr:route>/ppxlib-release-0-38-0/</fr:route>
                    <fr:title text="Ppxlib Releases › 0.38.0 "><fr:link href="/ppxlib-releases/" title="Ppxlib Releases" uri="https://patrick.sirref.org/ppxlib-releases/" display-uri="ppxlib-releases" type="local">Ppxlib Releases</fr:link> › 0.38.0 </fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p><fr:link href="/nathanreb/" title="Nathan Rebours" uri="https://patrick.sirref.org/nathanreb/" display-uri="nathanreb" type="local">Nathan</fr:link> and I <fr:link href="https://github.com/ocaml/opam-repository/pull/29563" type="external">released ppxlib.0.38.0</fr:link> last week. Its main feature is full <html:em>migration</html:em> support for the upcoming OCaml 5.5 compiler (currently in its <html:code>alpha3</html:code> release). This means supporting the handful of new features landing in OCaml 5.5: <fr:link href="/modular-explicits/" title="Modular Explicits in OCaml" uri="https://patrick.sirref.org/modular-explicits/" display-uri="modular-explicits" type="local">modular explicits</fr:link>, external type declarations and arbitrary "local" structure items.</html:p>
                    <html:p>As <fr:link href="https://patrick.sirref.org/nathenreb/" type="external">Nathan</fr:link> and I continue to find a plausible maintenance story for <fr:link href="/ppxlib/" title="Ppxlib" uri="https://patrick.sirref.org/ppxlib/" display-uri="ppxlib" type="local">ppxlib</fr:link>, <fr:link href="https://patrick.sirref.org/nathenreb/" type="external">Nathan</fr:link> has opened <fr:link href="https://github.com/ocaml/ocaml/issues/14668" type="external">an issue on the OCaml compiler to discuss the idea of adding additional extension points to the language</fr:link>.</html:p>
                    <html:p>This comes from the desire to be able to store encoded versions of new OCaml features inside older abstract syntax trees.</html:p>
                    <html:p>There are also some nice bug fixes in there too:</html:p>
                    <html:ul>
                      <html:li>
                        <html:p><fr:link href="https://github.com/ocaml-ppx/ppxlib/pull/613" type="external">A potential OOM</fr:link> loop has now been removed.</html:p>
                      </html:li>
                      <html:li>
                        <html:p>
                          <fr:link href="https://github.com/ocaml-ppx/ppxlib/pull/619" type="external">Locations have been restored to long identifiers!</fr:link>
                        </html:p>
                      </html:li>
                    </html:ul>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>30</fr:day>
                    </fr:date>
                    <fr:title text="Merry updates">Merry updates</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>It turns out writing a POSIX(ish) shell is hard; at least, there is a vast number of slightly obscure features that one needs to support. I think this is surprising because most people writing shell scripts write simple shell scripts; scripts that use a much smaller subset of features. In the same breath, those same developers are likely to use something like <html:code>apt-get install bash</html:code> which runs a plethora of more advanced (and non-POSIX) shell scripts!</html:p>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>30</fr:day>
                        </fr:date>
                        <fr:title text="Exec redirects">Exec redirects</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>In a shell script, you can use the built-in command <html:code>exec</html:code> to replace the current process with a new one (e.g. <html:code>exec vim</html:code>). However, there is a <fr:link href="https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_21" type="external"><html:em>different</html:em> mode of operation</fr:link> for <html:code>exec</html:code>:</html:p>
                        <html:blockquote>
                          <html:p>If exec is specified with no operands, any redirections associated with the exec command shall be made in the current shell execution environment.</html:p>
                        </html:blockquote>
                        <html:p>One of my litmus tests for <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is the <fr:link href="https://wiki.debian.org/Debootstrap" type="external">Debian debootstrap scripts</fr:link> (h/t <fr:link href="/anilmadhavapeddy/" title="Anil Madhavapeddy" uri="https://patrick.sirref.org/anilmadhavapeddy/" display-uri="anilmadhavapeddy" type="local">Anil</fr:link>). One thing that it does is the following:</html:p>
                        <html:pre class="hilite">
                          <html:code>
                            <html:span class="sh-entity-name-function">err</html:span>
                            <html:span class="sh-meta-function"> </html:span>
                            <html:span class="sh-punctuation-definition-arguments"><![CDATA[()]]></html:span>
                            <html:span class="sh-meta-function"> </html:span>
                            <html:span class="sh-punctuation-definition-group"><![CDATA[{]]></html:span>
                            <html:span class="sh-meta-scope-group">
</html:span>
                            <html:span class="sh-meta-scope-group">  </html:span>
                            <html:span class="sh-support-function-builtin">printf</html:span>
                            <html:span class="sh-meta-scope-group"> </html:span>
                            <html:span class="sh-punctuation-definition-string-begin">"</html:span>
                            <html:span class="sh-string-quoted-double">err </html:span>
                            <html:span class="sh-punctuation-definition-variable">$</html:span>
                            <html:span class="sh-variable-other-positional">1</html:span>
                            <html:span class="sh-punctuation-definition-string-end">"</html:span>
                            <html:span class="sh-meta-scope-group"> </html:span>
                            <html:span class="sh-keyword-operator-redirect">&gt;&amp;4</html:span>
                            <html:span class="sh-meta-scope-group">
</html:span>
                            <html:span class="sh-punctuation-definition-group"><![CDATA[}]]></html:span>
                            <html:span class="sh-punctuation-definition-function">
</html:span>
                            <html:span class="sh-source">
</html:span>
                            <html:span class="sh-support-function-builtin">exec</html:span>
                            <html:span class="sh-source"> </html:span>
                            <html:span class="sh-keyword-operator-redirect">4&gt;&amp;1</html:span>
                            <html:span class="sh-source"> 
</html:span>
                          </html:code>
                        </html:pre>
                        <html:p>A lot of detail has been elided for clarity. <html:code>exec 4&gt;&amp;1</html:code> sets up a redirection for the shell's execution environment in which file descriptor <html:code>4</html:code> is now an alias for standard output. So, writing to <html:code>4</html:code> (by redirecting a command's standard output to <html:code>4</html:code> i.e. <html:code>&gt;&amp;4</html:code>) will output to where standard output is going (most likely the terminal).</html:p>
                        <html:p>For this to work, shell's must <fr:link href="https://www.man7.org/linux/man-pages/man2/dup.2.html" type="external"><html:code>dup2</html:code></fr:link> the relevant file descriptors which have the following condition:</html:p>
                        <html:blockquote>
                          <html:p>If the file descriptor newfd was previously open, it is closed before being reused; the close is performed silently (i.e., any errors during the close are not reported by dup2()).</html:p>
                        </html:blockquote>
                        <html:p>This is all very well, unless your program has an important file already open that happens to have file descriptor <html:code>4</html:code>. The <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link> Linux and POSIX backends suffer from this problem. Both make use of a file-based synchronisation mechanism for waking up the eventloop should another domain push a completion to the scheduler's run queue. On Linux this is <fr:link href="https://github.com/ocaml-multicore/eio/blob/c44ee5ce96c120b7ccc23a12d241dc8672e2888f/lib_eio_linux/sched.ml#L501" type="external">via an eventfd</fr:link> and in <fr:link href="https://github.com/ocaml-multicore/eio/blob/c44ee5ce96c120b7ccc23a12d241dc8672e2888f/lib_eio_posix/sched.ml#L20" type="external">POSIX, a pipe</fr:link>.</html:p>
                        <html:p>This <html:code>dup2</html:code> will close the <html:code>eventfd</html:code> and will likely grind Eio to a halt (or an <html:code>assert false</html:code>). For now, I have resorted to vendoring <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link> and moving the <html:code>eventfd</html:code> file descriptors to higher values, though I <fr:link href="https://github.com/ocaml-multicore/eio/pull/836" type="external">have opened a PR to make this more configurable</fr:link>. This bug is quite easy to write up... it was not so easy to find!</html:p>
                        <html:p>As a random example, consider the <html:code>debconf/confmodule</html:code> <fr:link href="https://sources.debian.org/src/debconf/1.5.77/confmodule/" type="external">script</fr:link> which offers very little room for a buggy implementation!</html:p>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>30</fr:day>
                    </fr:date>
                    <fr:title text="Outreachy">Outreachy</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>The presentations from the demonstrations for this round of Outreachy are now online!</html:p>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>2</fr:month>
                          <fr:day>9</fr:day>
                        </fr:date>
                        <fr:uri>https://patrick.sirref.org/outreachy-ocaml-tiff/</fr:uri>
                        <fr:display-uri>outreachy-ocaml-tiff</fr:display-uri>
                        <fr:route>/outreachy-ocaml-tiff/</fr:route>
                        <fr:title text="Write support in OCaml TIFF library">Write support in OCaml TIFF library</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>I am mentoring <fr:link href="/tambe-salome/" title="Tambe Salome" uri="https://patrick.sirref.org/tambe-salome/" display-uri="tambe-salome" type="local">Tambe Salome</fr:link> during the December 2025 Outreachy round to add support for writing TIFF files in the <fr:link href="/ocaml-tiff/" title="ocaml-tiff" uri="https://patrick.sirref.org/ocaml-tiff/" display-uri="ocaml-tiff" type="local">ocaml-tiff</fr:link> library.</html:p>
                        <html:p>You can now see the video of the demonstration day presentation:</html:p>
                        <html:div style="text-align: center">
<html:iframe title="Outreachy Demo Day December 2025 Round" width="560" height="315" src="https://watch.ocaml.org/videos/embed/8aUqMhFvhQGq4WJLH3ukjA?start=1h18m33s" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" />
</html:div>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false" toc="false" numbered="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>3</fr:month>
                  <fr:day>19</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/weekly-2026-w12/</fr:uri>
                <fr:display-uri>weekly-2026-w12</fr:display-uri>
                <fr:route>/weekly-2026-w12/</fr:route>
                <fr:title text="A POSIX Shell in OCaml">A POSIX Shell in OCaml</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>Long time, no weekly. Since the start of this year I have been building a POSIX shell in OCaml called <html:code>msh</html:code> (with the underlying library called <html:code>Merry</html:code>). <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is available online now.</html:p>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>19</fr:day>
                    </fr:date>
                    <fr:title text="A POSIX(ish) shell in OCaml">A POSIX(ish) shell in OCaml</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p><fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is a POSIX(ish) in OCaml. It uses  <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link> alongside <fr:link href="github.com/colis-anr/morbig" type="external">Morbig</fr:link> (a static parser for POSIX shell).</html:p>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>19</fr:day>
                        </fr:date>
                        <fr:title text="Why another (POSIX) shell?">Why another (POSIX) shell?</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>Shells have been around for a long time. In my research on the notion of <html:em>metashell</html:em> I wrote about Louis Pouzin originally coining the term:</html:p>
                        <fr:tree show-metadata="false" numbered="false">
                          <fr:frontmatter>
                            <fr:authors>
                              <fr:author>
                                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                              </fr:author>
                            </fr:authors>
                            <fr:date>
                              <fr:year>2025</fr:year>
                              <fr:month>5</fr:month>
                              <fr:day>19</fr:day>
                            </fr:date>
                            <fr:uri>https://patrick.sirref.org/pouzin-shell/</fr:uri>
                            <fr:display-uri>pouzin-shell</fr:display-uri>
                            <fr:route>/pouzin-shell/</fr:route>
                            <fr:title text="Metashells › Louis Pouzin's &quot;SHELL&quot; "><fr:link href="/weekly-2025-05-12/" title="Metashells" uri="https://patrick.sirref.org/weekly-2025-05-12/" display-uri="weekly-2025-05-12" type="local">Metashells</fr:link> › Louis Pouzin's "SHELL" </fr:title>
                          </fr:frontmatter>
                          <fr:mainmatter>
                            <html:p>I spent some time reading <fr:link href="/pouzin-shell-2013/" title="The Origin of the Shell" uri="https://patrick.sirref.org/pouzin-shell-2013/" display-uri="pouzin-shell-2013" type="local">part of the multics design documentation</fr:link> this week. Louis Pouzin coined the term "SHELL" in this document, and I was reminded yet again just how important it is to be a good writer even as a "computer science researcher". For example, this excerpt from the requirements section of the document</html:p>
                            <html:blockquote>
                              <html:p>The previous definitions imply that a command MUST be designed while keeping in mind the user, sitting at his console, wondering about what might be going on, mistyping or forgetting arguments, even if fully aware of the conventions, and possibly interfering with the command by hasty quits, carriage returns, and other temperamental reactions.</html:p>
                            </html:blockquote>
                            <html:p>And then later, when defining the "SHELL".</html:p>
                            <html:blockquote>
                              <html:p>We may envision a common procedure called automatically by the supervisor whenever a user types in some message at his console, at a time when he has no other process in active execution under console control (presently called command level). This procedure acts as an interface between console messages and subroutine. The purpose of such a procedure is to create a medium of exchange into which one could activate any procedure, <html:em>inside of another program if it were called</html:em>. Hereafter, for simplification, we shall refer to that procedure as the "SHELL".</html:p>
                            </html:blockquote>
                            <html:p>It still surprises how little the undergraduate degree in computer science at <fr:link href="/ucam/" title="University of Cambridge" uri="https://patrick.sirref.org/ucam/" display-uri="ucam" type="local">Cambridge</fr:link> focuses on writing skills.</html:p>
                          </fr:mainmatter>
                        </fr:tree>
                        <html:p>I built <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> exploring the idea of a shell-like interface that allowed users to <html:em>undo</html:em> their shell actions (amongst other cool tricks). Unfortunately <html:em>shell-like</html:em> is not enough. <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> cut many corners to masquerade as a shell (e.g. appending <html:code>env</html:code> to understand how a command may have altered the execution environment). I felt it was necessary to make <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> a "SHELL"! To do that, I needed a solid foundation to build on.</html:p>
                        <html:p><html:code>msh</html:code>, the POSIX shell that comes with <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link>, is by no means POSIX-complete in terms of features. But it is my <fr:link href="https://github.com/patricoferris/nixos/blob/e0faf870f76710d4a75ace775f333c88f1321c5a/modules/default.nix#L59" type="external">daily driver at this point</fr:link>. It includes a pure OCaml rewrite of <fr:link href="github.com/antirez/linenoise" type="external">linenoise</fr:link> (a small, self-contained alternative to the venerable <html:code>readline</html:code>) called <fr:link href="https://tangled.org/patrick.sirref.org/bruit" type="external">bruit</fr:link>.</html:p>
                        <html:p>If you have <html:code>docker</html:code> installed on your machine, you can take it for a spin today:</html:p>
                        <html:pre><![CDATA[docker run -it --rm patrickferris/msh]]></html:pre>
                        <html:p>The <html:code>patrickferris/msh</html:code> docker image is just for trying it out. It is based on the OCaml 5.3 alpine image.</html:p>
                        <html:p>Alternatively, you can build <html:code>msh</html:code> from source and have it available in your opam switch.</html:p>
                        <html:pre><![CDATA[opam pin git+https://tangled.org/patrick.sirref.org/merry]]></html:pre>
                        <html:p>There are many small paper cuts left to patch over, but most of it is porcelain (e.g. <html:code>ctrl+left-arrow</html:code> for moving in <fr:link href="https://tangled.org/patrick.sirref.org/bruit" type="external">bruit</fr:link>). Unfortunately, these are the kinds of things that you will <html:em>immediately</html:em> stumble upon.</html:p>
                      </fr:mainmatter>
                    </fr:tree>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>19</fr:day>
                        </fr:date>
                        <fr:title text="What makes Merry different?">What makes Merry different?</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>Nothing.</html:p>
                        <html:p><fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> is supposed to be a solid, POSIX-ish base to build on. Unfortunaltely, as it turns out, the subset of features from the POSIX specification that people <html:em>actually use</html:em>... is pretty much all of it. Every possible redirection, variable expansion, shell built-in and compound command make some appearance. Not to mention the non-POSIX bits of shell we all take for granted (e.g. <html:code>&amp;&gt;</html:code>-redirection).</html:p>
                        <html:p><fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> and <html:code>msh</html:code> are useable. You will most likely find bugs if you use them. If, when using <html:code>msh</html:code>, you find something obscure happening you can enable debug mode either my setting the variable <html:code>MSH_DEBUG</html:code> or by invoking <html:code>msh</html:code> with <html:code>-v -v</html:code>.</html:p>
                      </fr:mainmatter>
                    </fr:tree>
                    <fr:tree show-metadata="false" numbered="false">
                      <fr:frontmatter>
                        <fr:authors>
                          <fr:author>
                            <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                          </fr:author>
                        </fr:authors>
                        <fr:date>
                          <fr:year>2026</fr:year>
                          <fr:month>3</fr:month>
                          <fr:day>19</fr:day>
                        </fr:date>
                        <fr:title text="What's next?">What's next?</fr:title>
                      </fr:frontmatter>
                      <fr:mainmatter>
                        <html:p>It is soon time to combine <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link> and <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> into the time-travelling, POSIX-ish shell that I have been trying to build since I first started working on <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link>.</html:p>
                        <html:p>For this to be successful, I need <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link> to be able to pass plenty of tests and right now that involves trying to install plenty of packages using tools like <html:code>apk</html:code> and <html:code>apt</html:code>.</html:p>
                        <fr:tree show-metadata="false" numbered="false">
                          <fr:frontmatter>
                            <fr:authors>
                              <fr:author>
                                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                              </fr:author>
                            </fr:authors>
                            <fr:date>
                              <fr:year>2026</fr:year>
                              <fr:month>3</fr:month>
                              <fr:day>19</fr:day>
                            </fr:date>
                            <fr:title text="Shell MRDT">Shell MRDT</fr:title>
                          </fr:frontmatter>
                          <fr:mainmatter>
                            <html:p>What I am particularly interested in reasoning about, is the <fr:link href="https://tangled.org/patrick.sirref.org/merry/blob/main/src/lib/eval.ml#L22" type="external">execution context</fr:link> in <fr:link href="/merry/" title="Merry" uri="https://patrick.sirref.org/merry/" display-uri="merry" type="local">Merry</fr:link>. This value, alongside the file-system, constitutes a fairly deep understanding of the state that changes in each step of a shell's evaluation loop.</html:p>
                            <html:p>This was, in terms of <fr:link href="/shelter/" title="Shelter" uri="https://patrick.sirref.org/shelter/" display-uri="shelter" type="local">Shelter</fr:link>, the missing piece for truly building some kind of <fr:link href="/mrdts/" title="Mergeable Replicated Data Type Implementation" uri="https://patrick.sirref.org/mrdts/" display-uri="mrdts" type="local">MRDT</fr:link> across shell sessions.</html:p>
                          </fr:mainmatter>
                        </fr:tree>
                      </fr:mainmatter>
                    </fr:tree>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" numbered="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2026</fr:year>
                      <fr:month>3</fr:month>
                      <fr:day>19</fr:day>
                    </fr:date>
                    <fr:title text="TIFF in OCaml">TIFF in OCaml</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>I picked up from the excellent <fr:link href="/outreachy/" title="Outreachy" uri="https://patrick.sirref.org/outreachy/" display-uri="outreachy" type="local">Outreachy</fr:link> work of <fr:link href="https://patrick.sirref.org/tambe salome/" type="external">Tambe Salome</fr:link> in getting write-support in <fr:link href="/ocaml-tiff/" title="ocaml-tiff" uri="https://patrick.sirref.org/ocaml-tiff/" display-uri="ocaml-tiff" type="local">ocaml-tiff</fr:link>. We are getting closer to the kind of API I envisaged in this latest round of refinement and review.</html:p>
                    <html:pre class="hilite">
                      <html:code>
                        <html:span class="ocaml-keyword-other">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Path</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">checkerboard</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">~</html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">v</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">zeros</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">uint8</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[[|]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[|]]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">in</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword">for</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">row</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">0</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">to</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">1</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">do</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">    </html:span>
                        <html:span class="ocaml-keyword">for</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">col</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">0</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">to</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">1</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">do</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">      </html:span>
                        <html:span class="ocaml-keyword-other">if</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source">row</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">+</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">col</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">mod</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">2</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">0</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">then</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">set_item</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[[]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">row</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">col</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[]]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">254</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">v</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">    </html:span>
                        <html:span class="ocaml-keyword-other">done</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword-other">done</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-separator-terminator punctuation-separator">;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Nx</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">to_bigarray</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">v</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-keyword-other">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-unit"><![CDATA[()]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Eio_posix</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">run</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">@@</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">fun</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">env</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-&gt;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Tiff_eio</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">with_open_out</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source">env</html:span>
                        <html:span class="ocaml-keyword-other">#</html:span>
                        <html:span class="ocaml-source">cwd</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">/</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-string-quoted-double">example.tiff</html:span>
                        <html:span class="ocaml-string-quoted-double">"</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">@@</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">fun</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">w</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">-&gt;</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-keyword">let</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-entity-name-function-binding">tif</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-operator">=</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Tiff</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">make</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source"><![CDATA[(]]></html:span>
                        <html:span class="ocaml-source">checkerboard</html:span>
                        <html:span class="ocaml-source"> ~</html:span>
                        <html:span class="ocaml-source">size</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-colon punctuation">:</html:span>
                        <html:span class="ocaml-constant-numeric-decimal-integer">256</html:span>
                        <html:span class="ocaml-source"><![CDATA[)]]></html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-keyword-other">in</html:span>
                        <html:span class="ocaml-source">
</html:span>
                        <html:span class="ocaml-source">  </html:span>
                        <html:span class="ocaml-constant-language-capital-identifier">Tiff</html:span>
                        <html:span class="ocaml-keyword-other-ocaml punctuation-other-period punctuation-separator">.</html:span>
                        <html:span class="ocaml-source">to_file</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">tif</html:span>
                        <html:span class="ocaml-source"> </html:span>
                        <html:span class="ocaml-source">w</html:span>
                        <html:span class="ocaml-source">
</html:span>
                      </html:code>
                    </html:pre>
                    <html:img src="/bafkrmiaqqczj5lemda5ijfjtjsyldbhmveu3btfvoa7rpt5k4dvdejjuhu.png" />
                    <html:p>I have also been extremely pleased to see further external collaborators appearing:</html:p>
                    <html:ul>
                      <html:li>
                        <html:p><fr:link href="https://github.com/geocaml/ocaml-tiff/pull/63" type="external">Nicolas</fr:link> helping out with metadata maintainence.</html:p>
                      </html:li>
                      <html:li>
                        <html:p><fr:link href="https://github.com/geocaml/ocaml-tiff/pull/62" type="external">Virgile</fr:link> adding support for reading multi-image TIFF files.</html:p>
                      </html:li>
                    </html:ul>
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
          </fr:mainmatter>
        </fr:tree>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Backlinks">Backlinks</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Related">Related</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors>
              <fr:author>
                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
              </fr:author>
            </fr:authors>
            <fr:date>
              <fr:year>2026</fr:year>
              <fr:month>2</fr:month>
              <fr:day>9</fr:day>
            </fr:date>
            <fr:uri>https://patrick.sirref.org/merry/</fr:uri>
            <fr:display-uri>merry</fr:display-uri>
            <fr:route>/merry/</fr:route>
            <fr:title text="Merry">Merry</fr:title>
            <fr:meta name="external">https://tangled.org/patrick.sirref.org/merry</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>Merry is a library for building POSIX-ish shells in OCaml. It comes with a (work-in-progress) POSIX shell written using <fr:link href="/eio/" title="Eio" uri="https://patrick.sirref.org/eio/" display-uri="eio" type="local">Eio</fr:link> called <html:code>msh</html:code>.</html:p>
            <html:p>Supporting the full <fr:link href="https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/" type="external">POSIX specification</fr:link> is no easy feat, however, <html:code>msh</html:code> already <fr:link href="https://tangled.org/patrick.sirref.org/merry/tree/main/test" type="external">supports plenty of the more useful bits of the spec</fr:link>.</html:p>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://patrick.sirref.org/eio/</fr:uri>
            <fr:display-uri>eio</fr:display-uri>
            <fr:route>/eio/</fr:route>
            <fr:title text="Eio">Eio</fr:title>
            <fr:meta name="external">https://github.com/ocaml-multicore/eio</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:blockquote>
              <html:p>Eio provides an effects-based direct-style IO stack for OCaml 5</html:p>
            </html:blockquote>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors>
              <fr:author>
                <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
              </fr:author>
            </fr:authors>
            <fr:uri>https://patrick.sirref.org/mrdts/</fr:uri>
            <fr:display-uri>mrdts</fr:display-uri>
            <fr:route>/mrdts/</fr:route>
            <fr:title text="Mergeable Replicated Data Type Implementation">Mergeable Replicated Data Type Implementation</fr:title>
            <fr:taxon>Definition</fr:taxon>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>A <html:strong>mergeable replicated data type (MRDT) implementation</html:strong> for a data type <fr:tex display="inline"><![CDATA[\tau ]]></fr:tex> is a tuple <fr:tex display="inline"><![CDATA[D_{\tau } = (\Sigma , \sigma _{0}, do, merge)]]></fr:tex> where:</html:p>
            <html:ul>
              <html:li>
                <html:p><fr:tex display="inline"><![CDATA[\Sigma ]]></fr:tex> is the set of all possible states at a branch,</html:p>
              </html:li>
              <html:li>
                <html:p><fr:tex display="inline"><![CDATA[\sigma _{0} \in  \Sigma ]]></fr:tex> is the initial state,</html:p>
              </html:li>
              <html:li>
                <html:p><fr:tex display="inline"><![CDATA[do : Op_{\tau } \times  \Sigma  \times  Timestamp \rightarrow  \Sigma  \times  Val_{\tau }]]></fr:tex> implements every data type operation,</html:p>
              </html:li>
              <html:li>
                <html:p><fr:tex display="inline"><![CDATA[merge : \Sigma  \times  \Sigma  \times  \Sigma  \rightarrow  \Sigma ]]></fr:tex> implements the <html:em>three-way merge strategy</html:em>.</html:p>
              </html:li>
            </html:ul>
            <html:p><fr:link href="/kcrsk-mrdts-2022/" title="Certified mergeable replicated data types" uri="https://patrick.sirref.org/kcrsk-mrdts-2022/" display-uri="kcrsk-mrdts-2022" type="local">Definition 2.1 from "Certified Mergeable Replicated Data Types"</fr:link>.</html:p>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://patrick.sirref.org/outreachy/</fr:uri>
            <fr:display-uri>outreachy</fr:display-uri>
            <fr:route>/outreachy/</fr:route>
            <fr:title text="Outreachy">Outreachy</fr:title>
            <fr:taxon>Project</fr:taxon>
            <fr:meta name="external">https://www.outreachy.org/</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:blockquote>
              <html:p>Outreachy provides internships in open source and open science. Outreachy provides internships to people subject to systemic bias and impacted by underrepresentation in the technical industry where they are living.</html:p>
            </html:blockquote>
            <html:p>I am one of the coordinators for the <html:em>OCaml community</html:em> for Outreachy. I have also mentored multiple three-month internships for the community.</html:p>
            <html:hr />
            <fr:tree show-metadata="true" expanded="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="https://patrick.sirref.org/Patrick Ferris/" type="external">Patrick Ferris</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>2</fr:month>
                  <fr:day>9</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/outreachy-ocaml-tiff/</fr:uri>
                <fr:display-uri>outreachy-ocaml-tiff</fr:display-uri>
                <fr:route>/outreachy-ocaml-tiff/</fr:route>
                <fr:title text="Write support in OCaml TIFF library">Write support in OCaml TIFF library</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>I am mentoring <fr:link href="/tambe-salome/" title="Tambe Salome" uri="https://patrick.sirref.org/tambe-salome/" display-uri="tambe-salome" type="local">Tambe Salome</fr:link> during the December 2025 Outreachy round to add support for writing TIFF files in the <fr:link href="/ocaml-tiff/" title="ocaml-tiff" uri="https://patrick.sirref.org/ocaml-tiff/" display-uri="ocaml-tiff" type="local">ocaml-tiff</fr:link> library.</html:p>
                <html:p>You can now see the video of the demonstration day presentation:</html:p>
                <html:div style="text-align: center">
<html:iframe title="Outreachy Demo Day December 2025 Round" width="560" height="315" src="https://watch.ocaml.org/videos/embed/8aUqMhFvhQGq4WJLH3ukjA?start=1h18m33s" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" />
</html:div>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="true" expanded="false">
              <fr:frontmatter>
                <fr:authors />
                <fr:date>
                  <fr:year>2022</fr:year>
                  <fr:month>12</fr:month>
                  <fr:day>1</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/topojson-0001/</fr:uri>
                <fr:display-uri>topojson-0001</fr:display-uri>
                <fr:route>/topojson-0001/</fr:route>
                <fr:title text="Implement a non-blocking, streaming codec for TopoJSON">Implement a non-blocking, streaming codec for TopoJSON</fr:title>
                <fr:meta name="external">https://github.com/geocaml/ocaml-geojson</fr:meta>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>GeoJSON is a JSON standardised format for handling simple geographical data. One common use of GeoJSON is to add map overlays such as with the popular JavaScript library leaflet. One issue with GeoJSON is redundant data, for example when two polygons share boundaries ideally only one would contain the full data about the boundary, however, with GeoJSON the data is duplicated.</html:p>
                <html:p>TopoJSON is an extension to GeoJSON to encode topology. This allows for redundant data to be removed and file sizes to be greatly reduced. This is often very desirable especially when working with data in the browser. In a previous Outreachy internship a new OCaml library was implemented to provide an OCaml library for TopoJSON, this project will build on this adding more functionality to the library and providing a non-blocking, streaming codec version similar to the geojsone library.</html:p>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="true" expanded="false">
              <fr:frontmatter>
                <fr:authors />
                <fr:date>
                  <fr:year>2022</fr:year>
                  <fr:month>6</fr:month>
                  <fr:day>1</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/topojson-0002/</fr:uri>
                <fr:display-uri>topojson-0002</fr:display-uri>
                <fr:route>/topojson-0002/</fr:route>
                <fr:title text="Extend OCaml's GeoJSON library to support TopoJSON">Extend OCaml's GeoJSON library to support TopoJSON</fr:title>
                <fr:meta name="external">https://github.com/geocaml/ocaml-geojson</fr:meta>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>GeoJSON is a JSON standardised format for handling simple geographical data. One common use of GeoJSON is to add map overlays such as with the popular JavaScript library leaflet. One issue with GeoJSON is redundant data, for example when two polygons share boundaries ideally only one would contain the full data about the boundary, however, with GeoJSON the data is duplicated.</html:p>
                <html:p>TopoJSON is an extension to GeoJSON to encode topology. This allows for redundant data to be removed and file sizes to be greatly reduced. This is often very desirable especially when working with data in the browser. In a previous Outreachy internship a new OCaml library was implemented to provide an OCaml library for TopoJSON, this project will build on this adding more functionality to the library and providing a non-blocking, streaming codec version similar to the geojsone library.</html:p>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="true" expanded="false">
              <fr:frontmatter>
                <fr:authors />
                <fr:date>
                  <fr:year>2021</fr:year>
                  <fr:month>6</fr:month>
                  <fr:day>1</fr:day>
                </fr:date>
                <fr:uri>https://patrick.sirref.org/ocamlorg-0001/</fr:uri>
                <fr:display-uri>ocamlorg-0001</fr:display-uri>
                <fr:route>/ocamlorg-0001/</fr:route>
                <fr:title text="Improve the ocaml.org Website">Improve the ocaml.org Website</fr:title>
                <fr:meta name="external">https://ocaml.org</fr:meta>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>OCaml.org is the main website for OCaml, a functional, typed, high-level programming language. This project revolves around improving the website on multiple different fronts including: layout, accessibility and content.</html:p>
                <html:p>Because a lot of the content of the website is about OCaml and the website itself is written in OCaml, this would be a great opportunity for interns to learn the language and functional programming concepts.</html:p>
              </fr:mainmatter>
            </fr:tree>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://patrick.sirref.org/shelter/</fr:uri>
            <fr:display-uri>shelter</fr:display-uri>
            <fr:route>/shelter/</fr:route>
            <fr:title text="Shelter">Shelter</fr:title>
            <fr:meta name="external">https://tangled.sh/@patrick.sirref.org/shelter</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>A shim between a user and the shell that provides greater reproducibility and insight into what your code is doing!</html:p>
            <fr:tree show-metadata="false" numbered="false">
              <fr:frontmatter>
                <fr:authors />
                <fr:title text="Give me Shelter!">Give me Shelter!</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>Shelter combines the caching and reproducibility of Dockerfiles with the ease of interaction of the shell. I'd be interested to use <fr:link href="/ryangibb/" title="Ryan Gibb" uri="https://patrick.sirref.org/ryangibb/" display-uri="ryangibb" type="local">Ryan</fr:link>'s <fr:link href="https://ryan.freumh.org/research.html" type="external">package management work</fr:link> as a means to specify the base environment users want.</html:p>
                <html:p>Here is an example of running Shelter and making use of the branchable sessions.</html:p>
                <html:pre class="ansi2html-content"><html:span class="ansi33">shelter&gt; </html:span>echo hello &gt; hello.txt
<html:span class="ansi33">shelter</html:span><![CDATA[[]]><html:span class="ansi32">main</html:span>#<html:span class="ansi35">bbd691a</html:span><![CDATA[] : { mode:]]><html:span class="ansi31">rw</html:span><![CDATA[ }> @ session exp-1]]><html:span class="ansi33">shelter</html:span><![CDATA[[]]><html:span class="ansi32">exp-1</html:span>#<html:span class="ansi35">bbd691a</html:span><![CDATA[] : { mode:]]><html:span class="ansi31">rw</html:span><![CDATA[ }> echo world >> hello.txt]]><html:span class="ansi33">shelter</html:span><![CDATA[[]]><html:span class="ansi32">exp-1</html:span>#<html:span class="ansi35">dd5bab8</html:span><![CDATA[] : { mode:]]><html:span class="ansi31">rw</html:span><![CDATA[ }> @ session main]]><html:span class="ansi33">shelter</html:span><![CDATA[[]]><html:span class="ansi32">main</html:span>#<html:span class="ansi35">bbd691a</html:span><![CDATA[] : { mode:]]><html:span class="ansi31">rw</html:span><![CDATA[ }> echo "to the" >> hello.txt]]><html:span class="ansi33">shelter</html:span><![CDATA[[]]><html:span class="ansi32">main</html:span>#<html:span class="ansi35">b9abef4</html:span><![CDATA[] : { mode:]]><html:span class="ansi31">rw</html:span><![CDATA[ }> cat hello.txt
hello
to the]]><html:span class="ansi33">shelter</html:span><![CDATA[[]]><html:span class="ansi32">main</html:span>#<html:span class="ansi35">d1c1728</html:span><![CDATA[] : { mode:]]><html:span class="ansi31">rw</html:span><![CDATA[ }> @ session exp-1]]><html:span class="ansi33">shelter</html:span><![CDATA[[]]><html:span class="ansi32">exp-1</html:span>#<html:span class="ansi35">dd5bab8</html:span><![CDATA[] : { mode:]]><html:span class="ansi31">rw</html:span><![CDATA[ }> @ replay main]]><html:span class="ansi33">shelter</html:span><![CDATA[[]]><html:span class="ansi32">exp-1</html:span>#<html:span class="ansi35">df1b4e1</html:span><![CDATA[] : { mode:]]><html:span class="ansi31">rw</html:span><![CDATA[ }> cat hello.txt
hello
to the
world]]><html:span class="ansi33">shelter</html:span><![CDATA[[]]><html:span class="ansi32">exp-1</html:span>#<html:span class="ansi35">67162b7</html:span><![CDATA[] : { mode:]]><html:span class="ansi31">rw</html:span><![CDATA[ }>]]></html:pre>
                <html:p>The <html:code>@</html:code> character allows the user to interact directly with Shelter's build-in operators. <html:code>@ session exp-1</html:code> creates a new session as there is no <html:code>exp-1</html:code> session. It points to the head commit of the branch it came from (<html:code>git checkout -b exp-1</html:code>).</html:p>
                <html:p>From there we make changes and switch between branches until we perform a replay. A replay is a rebase without any merge conflict checks, it simply finds the <html:em>least common ancestor</html:em> of the two branches and re-applies the commits from your current branch (here <html:code>exp-1</html:code>) onto the head of the target branch (here <html:code>main</html:code>).</html:p>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" numbered="false">
              <fr:frontmatter>
                <fr:authors />
                <fr:title text="Under the hood">Under the hood</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>Shelter is a light-weight, reimplementation of <fr:link href="https://github.com/ocurrent/obuilder" type="external">obuilder</fr:link>. It uses the similar ideas, like snapshotting filesystems (e.g. <fr:link href="https://github.com/patricoferris/ocaml-zfs" type="external">ZFS</fr:link>) and Linux namespaces (e.g. <fr:link href="https://github.com/quantifyearth/void" type="external">Void</fr:link>). Between the low-level store and the interactive shell parts, there is an <fr:link href="https://github.com/mirage/irmin" type="external">Irmin</fr:link> store.</html:p>
              </fr:mainmatter>
            </fr:tree>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://patrick.sirref.org/ocaml-tiff/</fr:uri>
            <fr:display-uri>ocaml-tiff</fr:display-uri>
            <fr:route>/ocaml-tiff/</fr:route>
            <fr:title text="ocaml-tiff">ocaml-tiff</fr:title>
            <fr:meta name="external">https://github.com/geocaml/ocaml-tiff</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>Part of the <fr:link href="/geocaml/" title="Geocaml" uri="https://patrick.sirref.org/geocaml/" display-uri="geocaml" type="local">geocaml</fr:link> suite of geospatial libraries for OCaml.</html:p>
          </fr:mainmatter>
        </fr:tree>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Contributions">Contributions</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
  </fr:backmatter>
</fr:tree>
