<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Lysxia's blog</title>
    <link href="https://blog.poisson.chat/atom.xml" rel="self" />
    <link href="https://blog.poisson.chat" />
    <icon>https://blog.poisson.chat/favicon.png</icon>
    <id>https://blog.poisson.chat/atom.xml</id>
    <author>
        <name>Lysxia</name>
        <email>lysxia@gmail.com</email>
    </author>
    <updated>2025-09-01T00:00:00Z</updated>
    <entry>
    <title>Alpha-beta pruning is just minimax in a lattice of clamping functions</title>
    <link href="https://blog.poisson.chat/posts/2025-09-01-alpha-beta.html" />
    <id>https://blog.poisson.chat/posts/2025-09-01-alpha-beta.html</id>
    <published>2025-09-01T00:00:00Z</published>
    <updated>2025-09-01T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>A lazy take on a classic game theory algorithm.</p>
<figure>
<figcaption>
Sip a caffè latte while thinking about lattices
</figcaption>
<img src="/images/cafe-latte.jpg" alt="A cup of latte"></img>
</figure>
<pre class="toc"><code></code></pre>
<details>
<summary>
Haskell extensions and imports used in this post
</summary>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ot">  DataKinds,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="ot">  DeriveGeneric,</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="ot">  DeriveTraversable,</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="ot">  DerivingStrategies,</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="ot">  GeneralizedNewtypeDeriving,</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="ot">  RankNTypes,</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="ot">  ScopedTypeVariables,</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="ot">  StandaloneDeriving,</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="ot">  TypeFamilies #-}</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Ord</span> (<span class="dt">Down</span>(<span class="dt">Down</span>, <span class="fu">getDown</span>))</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.List.NonEmpty</span> (<span class="dt">NonEmpty</span>(..))</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">GHC.Generics</span> <span class="kw">as</span> <span class="dt">GHC</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Generics.SOP</span> (<span class="dt">Generic</span>, <span class="dt">HasDatatypeInfo</span>, <span class="dt">NP</span>(..), <span class="dt">K</span>(..))</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Test.QuickCheck</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Test.StrictCheck</span></span></code></pre></div>
</details>
<h2 id="minimax">Minimax</h2>
<p>Minimax is a general algorithm for finding optimal strategies.
It’s not meant to be efficient or practical. It is more of a
basic concept of game theory, and a reference against which
to compare other game-solving algorithms.</p>
<p>We consider a simple model of two-player games.
They take turns playing moves until reaching an end
state with a final score. One player’s goal is to maximize the
score, whereas the other player’s goal is to minimize it.
Let us call these players Max and Min respectively, short for Maximizer
and Minimizer.</p>
<p>We represent such a game by its <strong>game tree</strong>, which is made up of
three constructors:
a <code>Max</code> (resp. <code>Min</code>) node represents a game state where Max (resp. Min)
chooses the next move, each move resulting in a new game state,
and an <code>End</code> leaf represents an end state as its score.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Game</span> <span class="fu">score</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Max</span> (<span class="dt">NonEmpty</span> (<span class="dt">Game</span> <span class="fu">score</span>))</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Min</span> (<span class="dt">NonEmpty</span> (<span class="dt">Game</span> <span class="fu">score</span>))</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">End</span> <span class="fu">score</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> <span class="fu">stock</span> (<span class="dt">Show</span>, <span class="dt">Functor</span>, <span class="dt">Foldable</span>)</span></code></pre></div>
<p>Note that <code>Max</code> and <code>Min</code> nodes must have at least one possible move.
You may be wondering about games that end when one player can no longer play:
instead of an empty <code>Min</code> or <code>Max</code> node, such game states simply correspond
to an <code>End</code> leaf, making the final <code>score</code> explicit.</p>
<p>Most real games just have a win/tie/lose end condition.
They naturally correspond to applying <code>Game</code> to a type with three possible scores:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">WinLose</span> <span class="ot">=</span> <span class="dt">MinWins</span> <span class="op">|</span> <span class="dt">Tie</span> <span class="op">|</span> <span class="dt">MaxWins</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Ord</span>, <span class="dt">Show</span>)</span></code></pre></div>
<p>In practice, chess engines don’t work with the whole game tree
since it is too massive. Instead, they build approximations by
pruning certain branches of the tree and replacing them with leaves.
The score on each leaf is a number which estimates how favorable
the game state is to either player. So we end up with
<code>Game ℝ</code>, or <code>Game Double</code>.</p>
<p>In general, the type <code>Game</code> represents two-player games
with complete information and zero-sum objectives.</p>
<p>We shall assume that <code>score</code> is a totally ordered set. This requirement
corresponds to a constraint <code>Ord score</code> in Haskell. In that case,
there exists an “optimal strategy” for each player which guarantees
them an “optimal score” <code>m</code> in the sense that as long as one player
sticks to their “optimal strategy”, the other player cannot
score better than <code>m</code>.
This situation is what we call a <a href="https://en.wikipedia.org/wiki/Nash_equilibrium">Nash equilibrium</a> in game theory.
For win/tie/lose games, the existence of a Nash equilibrium
means that either there is a winning strategy for one of the players,
or they must tie by playing optimally.</p>
<p>The “optimal score” <code>m</code> is unique, and can be computed by a fold of the game tree,
replacing <code>Max</code> and <code>Min</code> constructors with the functions <code>maximum</code> and <code>minimum</code>.
This is the <a href="https://en.wikipedia.org/wiki/Minimax#Minimax_algorithm_with_alternate_moves">minimax algorithm</a>:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="fu">minimax</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="dt">Game</span> <span class="fu">score</span> <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="fu">minimax</span> (<span class="dt">Max</span> <span class="fu">gs</span>) <span class="ot">=</span> <span class="fu">maximum</span> (<span class="fu">minimax</span> <span class="op">&lt;$&gt;</span> <span class="fu">gs</span>)</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="fu">minimax</span> (<span class="dt">Min</span> <span class="fu">gs</span>) <span class="ot">=</span> <span class="fu">minimum</span> (<span class="fu">minimax</span> <span class="op">&lt;$&gt;</span> <span class="fu">gs</span>)</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="fu">minimax</span> (<span class="dt">End</span> <span class="fu">s</span>) <span class="ot">=</span> <span class="fu">s</span></span></code></pre></div>
<p><code>minimax</code> is quite an inefficient algorithm:
it must traverse the whole game tree. Indeed, <code>maximum</code>
and <code>minimum</code> must traverse the whole list to find
the maximum or minimum element.</p>
<p>Often, we can do much better. For instance, consider the following tree:</p>
<pre><code>Max [ End 0,
      Min [ End (-1),
            t ] ]</code></pre>
<p>The minimax of that tree does not depend on the subtree <code>t</code>.
Indeed, <code>minimum [-1, minimax t]</code> is guaranteed to be at most <code>-1</code>,
so the maximum between that value and <code>0</code> is guaranteed to be <code>0</code>.
Thus we can compute the minimax without inspecting the subtree <code>t</code>,
which may be arbitrarily large.
That idea leads to a more efficient algorithm to compute the minimax.</p>
<h2 id="alpha-beta">Alpha-beta</h2>
<p>The alpha-beta pruning algorithm<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> is a modification of
minimax with an extra pair of arguments:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="fu">alphabeta</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="dt">Game</span> <span class="fu">score</span> <span class="ot">-&gt;</span> (<span class="fu">score</span>, <span class="fu">score</span>) <span class="ot">-&gt;</span> <span class="fu">score</span></span></code></pre></div>
<p>The pair <code>(alpha, beta)</code> represents a “relevance interval” which
relaxes the possible outputs of <code>alphabeta</code>.
Either <code>alphabeta t (alpha, beta)</code> produces a score within that interval,
in which case it is guaranteed to be equal to <code>minimax</code>. Otherwise,
<code>alphabeta t (alpha, beta)</code> produces a value outside of the interval,
in which case its exact value does not matter; it only has to be on
the same side of the interval as <code>minimax t</code>. More rigorously:</p>
<ul>
<li>if <code>alpha &lt; minimax t &lt; beta</code>, then <code>alphabeta t (alpha, beta) = minimax t</code>;</li>
<li>if <code>minimax t &lt;= alpha</code>, then <code>alphabeta t (alpha, beta) &lt;= alpha</code>;</li>
<li>if <code>beta &lt;= minimax t</code>, then <code>beta &lt;= alphabeta t (alpha, beta)</code>.</li>
</ul>
<p>Leaving the value of <code>alphabeta</code> underspecified when outside of the
interval allows the implementation to short-circuit:
we can stop searching through <code>Max</code> nodes as soon as we can guarantee a score greater than <code>beta</code>,
and we can stop searching through <code>Min</code> nodes as soon as we can guarantee a score smaller than <code>alpha</code>.</p>
<p>We can then use <code>alphabeta</code> to redefine <code>minimax</code>:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Minimax using alpha-beta pruning</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxAB</span> <span class="ot">::</span> (<span class="dt">Ord</span> <span class="fu">score</span>, <span class="dt">Bounded</span> <span class="fu">score</span>) <span class="ot">=&gt;</span> <span class="dt">Game</span> <span class="fu">score</span> <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxAB</span> <span class="fu">t</span> <span class="ot">=</span> <span class="fu">alphabeta</span> <span class="fu">t</span> (<span class="fu">minBound</span>, <span class="fu">maxBound</span>)</span></code></pre></div>
<p>assuming that <code>score</code> is <code>Bounded</code> with extreme values
<code>minBound :: score</code> and <code>maxBound :: score</code>.
It’s possible to avoid the <code>Bounded</code> constraint by changing
the interval type <code>(score, score)</code> to <code>(Maybe score, Maybe score)</code>,
which amounts to adding distinguished top and bottom elements.
We’ll stick with <code>Bounded</code> to keep things a bit simpler.</p>
<p>Implementing <code>alphabeta</code> is a standard exercise.
It is even easier when you have a formal specification like the above
to guide the implementation.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">alphabeta</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="dt">Game</span> <span class="fu">score</span> <span class="ot">-&gt;</span> (<span class="fu">score</span>, <span class="fu">score</span>) <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="fu">alphabeta</span> (<span class="dt">Max</span> (<span class="fu">g0</span> <span class="op">:|</span> [])) <span class="fu">i</span> <span class="ot">=</span> <span class="fu">alphabeta</span> <span class="fu">g0</span> <span class="fu">i</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="fu">alphabeta</span> (<span class="dt">Max</span> (<span class="fu">g0</span> <span class="op">:|</span> <span class="fu">g1</span> <span class="op">:</span> <span class="fu">gs</span>)) (<span class="fu">alpha</span>, <span class="fu">beta</span>) <span class="ot">=</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> <span class="fu">m0</span> <span class="ot">=</span> <span class="fu">alphabeta</span> <span class="fu">g0</span> (<span class="fu">alpha</span>, <span class="fu">beta</span>) <span class="kw">in</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">if</span> <span class="fu">beta</span> <span class="op">&lt;</span><span class="ot">=</span> <span class="fu">m0</span> <span class="kw">then</span> <span class="fu">m0</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="fu">m0</span> <span class="ot">`max`</span> <span class="fu">alphabeta</span> (<span class="dt">Max</span> (<span class="fu">g1</span> <span class="op">:|</span> <span class="fu">gs</span>)) (<span class="fu">max</span> <span class="fu">alpha</span> <span class="fu">m0</span>, <span class="fu">beta</span>)</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="fu">alphabeta</span> (<span class="dt">Min</span> (<span class="fu">g0</span> <span class="op">:|</span> [])) <span class="fu">i</span> <span class="ot">=</span> <span class="fu">alphabeta</span> <span class="fu">g0</span> <span class="fu">i</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a><span class="fu">alphabeta</span> (<span class="dt">Min</span> (<span class="fu">g0</span> <span class="op">:|</span> <span class="fu">g1</span> <span class="op">:</span> <span class="fu">gs</span>)) (<span class="fu">alpha</span>, <span class="fu">beta</span>) <span class="ot">=</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> <span class="fu">m0</span> <span class="ot">=</span> <span class="fu">alphabeta</span> <span class="fu">g0</span> (<span class="fu">alpha</span>, <span class="fu">beta</span>) <span class="kw">in</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>  <span class="kw">if</span> <span class="fu">m0</span> <span class="op">&lt;</span><span class="ot">=</span> <span class="fu">alpha</span> <span class="kw">then</span> <span class="fu">m0</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="fu">m0</span> <span class="ot">`min`</span> <span class="fu">alphabeta</span> (<span class="dt">Min</span> (<span class="fu">g1</span> <span class="op">:|</span> <span class="fu">gs</span>)) (<span class="fu">alpha</span>, <span class="fu">min</span> <span class="fu">beta</span> <span class="fu">m0</span>)</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="fu">alphabeta</span> (<span class="dt">End</span> <span class="fu">s</span>) <span class="fu">_</span> <span class="ot">=</span> <span class="fu">s</span></span></code></pre></div>
<p>But still, it is at least a little finicky and tedious to make sure that
you haven’t mixed your alphas and betas.</p>
<p>As we will see in this post,
we can streamline the implementation of alpha-beta pruning
by factoring the short-circuiting logic out of the “minimax” logic.</p>
<h2 id="generalized-minimax">Generalized minimax</h2>
<p>Remark that <code>minimax</code> only uses <code>min</code> and <code>max</code>
(via <code>minimum</code> and <code>maximum</code>), rather than the comparison
functions of <code>Ord</code> (<code>compare</code>, <code>(&lt;=)</code>, etc.).</p>
<p>We can reduce the dependency footprint of <code>minimax</code> by
defining a new class with only the necessary operations,
the class of lattices:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">Lattice</span> <span class="fu">a</span> <span class="kw">where</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- Join, least upper bound, max</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>  (<span class="op">\/</span>) <span class="ot">::</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a</span> </span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- Meet, greatest lower bound, min</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>  (<span class="op">/\</span>) <span class="ot">::</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a</span></span></code></pre></div>
<p>In mathematics, lattices are algebraic structures with two operations
<code>(\/)</code> (“join”) and <code>(/\)</code> (“meet”)
satisfying commutativity, associativity, as well as the absorption laws:</p>
<pre><code>x \/ (x /\ y) = x
x /\ (x \/ y) = x</code></pre>
<p>In this post, we will only be looking at lattices that arise
out of total orders,
so this class is rather just a way of saying that we only
depend on <code>min</code> and <code>max</code>.</p>
<p>Binary operations can be iterated to combine lists of arguments,
similarly to the <code>maximum</code> and <code>minimum</code> functions:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- maximum</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="fu">joins</span> <span class="ot">::</span> <span class="dt">Lattice</span> <span class="fu">a</span> <span class="ot">=&gt;</span> <span class="dt">NonEmpty</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="fu">joins</span> <span class="ot">=</span> <span class="fu">foldr1</span> (<span class="op">\/</span>)</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- minimum</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="fu">meets</span> <span class="ot">::</span> <span class="dt">Lattice</span> <span class="fu">a</span> <span class="ot">=&gt;</span> <span class="dt">NonEmpty</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="fu">meets</span> <span class="ot">=</span> <span class="fu">foldr1</span> (<span class="op">/\</span>)</span></code></pre></div>
<p>Minimax in lattices is defined by replacing <code>Max</code> and <code>Min</code> nodes with
the <code>joins</code> and <code>meets</code> operations.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Minimax in lattices</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxL</span> <span class="ot">::</span> <span class="dt">Lattice</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="dt">Game</span> <span class="fu">score</span> <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxL</span> (<span class="dt">Max</span> <span class="fu">gs</span>) <span class="ot">=</span> <span class="fu">joins</span> (<span class="fu">minimaxL</span> <span class="op">&lt;$&gt;</span> <span class="fu">gs</span>)</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxL</span> (<span class="dt">Min</span> <span class="fu">gs</span>) <span class="ot">=</span> <span class="fu">meets</span> (<span class="fu">minimaxL</span> <span class="op">&lt;$&gt;</span> <span class="fu">gs</span>)</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxL</span> (<span class="dt">End</span> <span class="fu">x</span>) <span class="ot">=</span> <span class="fu">x</span></span></code></pre></div>
<p><code>minimaxL</code> generalizes <code>minimax</code> since every decidable total order is a lattice
(because you can use <code>(&lt;=)</code> to define <code>min</code>/<code>max</code>).
Ideally this fact would be made explicit by making <code>Lattice</code> into
a superclass of <code>Ord</code>. Unfortunately in Haskell this would require us
to modify <code>Ord</code> or redefine it.
Another way to express the relation between <code>Lattice</code> and <code>Ord</code> is through a <code>newtype</code>.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">OrdLattice</span> <span class="fu">a</span> <span class="ot">=</span> <span class="dt">OrdLattice</span> <span class="fu">a</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>   <span class="kw">deriving</span> <span class="kw">newtype</span> (<span class="dt">Eq</span>, <span class="dt">Ord</span>, <span class="dt">Bounded</span>)</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="fu">unOrdLattice</span> <span class="ot">::</span> <span class="dt">OrdLattice</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a><span class="fu">unOrdLattice</span> (<span class="dt">OrdLattice</span> <span class="fu">x</span>) <span class="ot">=</span> <span class="fu">x</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Ord</span> <span class="fu">a</span> <span class="ot">=&gt;</span> <span class="dt">Lattice</span> (<span class="dt">OrdLattice</span> <span class="fu">a</span>) <span class="kw">where</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>  <span class="dt">OrdLattice</span> <span class="fu">x</span> <span class="op">\/</span> <span class="dt">OrdLattice</span> <span class="fu">y</span> <span class="ot">=</span> <span class="dt">OrdLattice</span> (<span class="fu">max</span> <span class="fu">x</span> <span class="fu">y</span>)</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>  <span class="dt">OrdLattice</span> <span class="fu">x</span> <span class="op">/\</span> <span class="dt">OrdLattice</span> <span class="fu">y</span> <span class="ot">=</span> <span class="dt">OrdLattice</span> (<span class="fu">min</span> <span class="fu">x</span> <span class="fu">y</span>)</span></code></pre></div>
<p>With that, we recover the starting <code>minimax</code> by specializing
<code>minimaxL</code> to <code>OrdLattice s</code>, and then unwrapping <code>OrdLattice</code>:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxO</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="dt">Game</span> <span class="fu">score</span> <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxO</span> <span class="ot">=</span> <span class="fu">unOrdLattice</span> <span class="op">.</span> <span class="fu">minimaxL</span> <span class="op">.</span> <span class="fu">fmap</span> <span class="dt">OrdLattice</span></span></code></pre></div>
<h2 id="clamping-functions">Clamping functions</h2>
<p>Focus on the type <code>(score, score) -&gt; score</code> which appears in the signature of <code>alphabeta</code>.
More specifically, we are interested in a subset of those functions that
we shall call <em>clamping functions</em>.</p>
<p>Intuitively, a clamping function <code>f</code> is a delayed representation of a constant <code>s</code>:
the goal of <code>f</code> is to compute <code>s</code>, but it may also stop early with an approximation
if it’s not necessary to know the exact value of <code>s</code>.</p>
<p>The name “clamping function” is a reference to the <code>clamp</code> function:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="fu">clamp</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="fu">score</span> <span class="ot">-&gt;</span> (<span class="fu">score</span>, <span class="fu">score</span>) <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="fu">clamp</span> <span class="fu">s</span> (<span class="fu">alpha</span>, <span class="fu">beta</span>) <span class="ot">=</span> <span class="fu">max</span> <span class="fu">alpha</span> (<span class="fu">min</span> <span class="fu">s</span> <span class="fu">beta</span>)</span></code></pre></div>
<p>We can think of the partially applied function <code>clamp s</code> as an encoding of the constant <code>s</code>,
which may or may not be output depending on the interval <code>(alpha, beta)</code>.</p>
<p>More formally, a <strong>clamping function</strong> with value <code>s</code> is a function <code>f :: (score, score) -&gt; score</code>
that satisfies the following, for all <code>(alpha, beta)</code> such that <code>alpha &lt; beta</code>:</p>
<ul>
<li>if <code>alpha &lt; s &lt; beta</code>, then <code>f (alpha, beta) = s</code>;</li>
<li>if <code>s &lt;= alpha</code>, then <code>f (alpha, beta) &lt;= alpha</code>;</li>
<li>if <code>beta &lt;= s</code>, then <code>beta &lt;= f (alpha, beta)</code>.</li>
</ul>
<p>Two clamping functions with the same value <code>s</code> are considered equal.
In particular, as clamping functions, <code>const s</code> is equal to <code>clamp s</code>.
Making the notion of equality explicit is necessary to make sense of equations
(laws for lattices, homomorphisms, and isomorphisms).</p>
<p>We enshrine the definition of clamping functions in a newtype:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Type of clamping functions, satisfying the properties above.</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Clamping</span> <span class="fu">score</span> <span class="ot">=</span> <span class="dt">Clamping</span> ((<span class="fu">score</span>, <span class="fu">score</span>) <span class="ot">-&gt;</span> <span class="fu">score</span>)</span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a><span class="fu">unClamping</span> <span class="ot">::</span> <span class="dt">Clamping</span> <span class="fu">score</span> <span class="ot">-&gt;</span> (<span class="fu">score</span>, <span class="fu">score</span>) <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a><span class="fu">unClamping</span> (<span class="dt">Clamping</span> <span class="fu">f</span>) <span class="ot">=</span> <span class="fu">f</span></span></code></pre></div>
<p>For any value <code>s</code>, we can construct the constant clamping function:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="fu">clamping</span> <span class="ot">::</span> <span class="fu">score</span> <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">score</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="fu">clamping</span> <span class="fu">s</span> <span class="ot">=</span> <span class="dt">Clamping</span> (<span class="op">\</span><span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">s</span>)</span></code></pre></div>
<p>Note that <code>\_ -&gt; s</code> and <code>clamp s</code> are both clamping functions with value <code>s</code>,
so both are valid definitions of <code>clamping s</code>.
We prefer the constant function <code>\_ -&gt; s</code> because it does less work.</p>
<p>Conversely, we can project clamping functions back into their values
by passing the whole interval <code>(minBound, maxBound)</code>:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="fu">declamp</span> <span class="ot">::</span> <span class="dt">Bounded</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="dt">Clamping</span> <span class="fu">score</span> <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="fu">declamp</span> (<span class="dt">Clamping</span> <span class="fu">f</span>) <span class="ot">=</span> <span class="fu">f</span> (<span class="fu">minBound</span>, <span class="fu">maxBound</span>)</span></code></pre></div>
<p>Those two functions form an isomorphism between <code>score</code> and <code>Clamping score</code>,
meaning that they satisfy the following equations:</p>
<pre><code>declamp . clamping = id
clamping . declamp = id</code></pre>
<p>We now get to the secret sauce of this post:
the maximum of two clamping functions (as well as the minimum).
This operation can be defined in two ways.
First is the naive definition, for reference:</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- &quot;max&quot; for clamping functions, naive variant</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="fu">maxC</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">s</span> <span class="ot">=&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><span class="fu">maxC</span> (<span class="dt">Clamping</span> <span class="fu">f</span>) (<span class="dt">Clamping</span> <span class="fu">g</span>) <span class="ot">=</span> <span class="dt">Clamping</span> (<span class="op">\</span><span class="fu">i</span> <span class="ot">-&gt;</span> <span class="fu">max</span> (<span class="fu">f</span> <span class="fu">i</span>) (<span class="fu">g</span> <span class="fu">i</span>))</span></code></pre></div>
<p>Second is the <em>lazy definition</em>: if <code>f (alpha, beta)</code> is greater
than the given upper bound <code>beta</code>, then the max of <code>f</code> and <code>g</code> will
be even greater:</p>
<pre><code>beta &lt;= f (alpha, beta) &lt;= max (f (alpha, beta)) (g (alpha, beta)) </code></pre>
<p>In that case, the maximum of <code>f</code> and <code>g</code> is allowed to output
<code>f (alpha, beta)</code> without looking at <code>g</code>.
Otherwise we must evaluate <code>g</code>, but we can tighten the interval by
updating the lower bound to <code>max alpha (f (alpha, beta))</code>.</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- &quot;max&quot; for clamping functions, lazy variant</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="fu">lazyMaxC</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">s</span> <span class="ot">=&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a><span class="fu">lazyMaxC</span> (<span class="dt">Clamping</span> <span class="fu">f</span>) (<span class="dt">Clamping</span> <span class="fu">g</span>) <span class="ot">=</span> <span class="dt">Clamping</span> (<span class="op">\</span>(<span class="fu">alpha</span>, <span class="fu">beta</span>) <span class="ot">-&gt;</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> <span class="fu">fi</span> <span class="ot">=</span> <span class="fu">f</span> (<span class="fu">alpha</span>, <span class="fu">beta</span>) <span class="kw">in</span></span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">if</span> <span class="fu">beta</span> <span class="op">&lt;</span><span class="ot">=</span> <span class="fu">fi</span> <span class="kw">then</span> <span class="fu">fi</span> <span class="kw">else</span> <span class="fu">fi</span> <span class="ot">`max`</span> <span class="fu">g</span> (<span class="fu">max</span> <span class="fu">alpha</span> <span class="fu">fi</span>, <span class="fu">beta</span>))</span></code></pre></div>
<p>Dually, we also have a <code>lazyMinC</code>.</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="fu">lazyMinC</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">s</span> <span class="ot">=&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a><span class="fu">lazyMinC</span> (<span class="dt">Clamping</span> <span class="fu">f</span>) (<span class="dt">Clamping</span> <span class="fu">g</span>) <span class="ot">=</span> <span class="dt">Clamping</span> (<span class="op">\</span>(<span class="fu">alpha</span>, <span class="fu">beta</span>) <span class="ot">-&gt;</span></span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> <span class="fu">fi</span> <span class="ot">=</span> <span class="fu">f</span> (<span class="fu">alpha</span>, <span class="fu">beta</span>) <span class="kw">in</span></span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">if</span> <span class="fu">fi</span> <span class="op">&lt;</span><span class="ot">=</span> <span class="fu">alpha</span> <span class="kw">then</span> <span class="fu">fi</span> <span class="kw">else</span> <span class="fu">fi</span> <span class="ot">`min`</span> <span class="fu">g</span> (<span class="fu">alpha</span>, <span class="fu">min</span> <span class="fu">beta</span> <span class="fu">fi</span>))</span></code></pre></div>
<p>To <a href="https://en.wikipedia.org/wiki/Don&#39;t_repeat_yourself">avoid repeating ourselves</a>,
we can also reuse <code>lazyMaxC</code> to implement <code>lazyMinC</code>.
Use <code>Down</code> to invert the ordering of an <code>Ord</code>:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="fu">lazyMinC</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">s</span> <span class="ot">=&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">s</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a><span class="fu">lazyMinC</span> <span class="fu">f</span> <span class="fu">g</span> <span class="ot">=</span> <span class="fu">undualize</span> (<span class="fu">lazyMaxC</span> (<span class="fu">dualize</span> <span class="fu">f</span>) (<span class="fu">dualize</span> <span class="fu">g</span>))</span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">dualizeWith</span> <span class="fu">from</span> <span class="fu">to</span> (<span class="dt">Clamping</span> <span class="fu">h</span>) <span class="ot">=</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Clamping</span> (<span class="op">\</span>(<span class="fu">beta</span>, <span class="fu">alpha</span>) <span class="ot">-&gt;</span> <span class="fu">from</span> (<span class="fu">h</span> (<span class="fu">to</span> <span class="fu">alpha</span>, <span class="fu">to</span> <span class="fu">beta</span>)))</span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a>    <span class="fu">dualize</span>   <span class="ot">=</span> <span class="fu">dualizeWith</span> <span class="dt">Down</span> <span class="fu">getDown</span> <span class="co">-- Clamping s -&gt; Clamping (Down s)</span></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">undualize</span> <span class="ot">=</span> <span class="fu">dualizeWith</span> <span class="fu">getDown</span> <span class="dt">Down</span> <span class="co">-- Clamping (Down s) -&gt; Clamping s</span></span></code></pre></div>
<p>These “naive” and “lazy” functions denote the same value
(<code>maxC = lazyMaxC</code> and <code>minC = lazyMinC</code>),
but <code>lazyMaxC</code> and <code>lazyMinC</code> may do less work,
either by ignoring their second argument or by applying it to a smaller interval than expected.</p>
<p>The point is that these “lazy” functions embody
the short-circuiting logic of alpha-beta pruning exactly.
All that’s left to do is to plug them into minimax.</p>
<h2 id="the-lattice-of-clamping-functions">The lattice of clamping functions</h2>
<p>With the lazy min and max that we just defined, we get a lattice:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Ord</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="dt">Lattice</span> (<span class="dt">Clamping</span> <span class="fu">score</span>) <span class="kw">where</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>  (<span class="op">\/</span>) <span class="ot">=</span> <span class="fu">lazyMaxC</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>  (<span class="op">/\</span>) <span class="ot">=</span> <span class="fu">lazyMinC</span></span></code></pre></div>
<p>Specialize minimax in the lattice of clamping functions:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxC</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="dt">Game</span> (<span class="dt">Clamping</span> <span class="fu">score</span>) <span class="ot">-&gt;</span> <span class="dt">Clamping</span> <span class="fu">score</span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxC</span> <span class="ot">=</span> <span class="fu">minimaxL</span></span></code></pre></div>
<p>This doesn’t look like much, but we have actually implemented the
alpha-beta pruning algorithm.
With a tiny bit of plumbing, we can redefine the function <code>alphabeta</code>
from earlier:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="fu">alphabeta&#39;</span> <span class="ot">::</span> <span class="dt">Ord</span> <span class="fu">score</span> <span class="ot">=&gt;</span> <span class="dt">Game</span> <span class="fu">score</span> <span class="ot">-&gt;</span> (<span class="fu">score</span>, <span class="fu">score</span>) <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a><span class="fu">alphabeta&#39;</span> <span class="ot">=</span> <span class="fu">unClamping</span> <span class="op">.</span> <span class="fu">minimaxC</span> <span class="op">.</span> <span class="fu">fmap</span> <span class="fu">clamping</span></span></code></pre></div>
<p>Then we want to partially apply <code>alphabeta'</code> to the interval <code>(minBound, maxBound)</code>.
This amounts to replacing <code>unClamping</code> with <code>declamp</code> in the body of <code>alphabeta'</code>.
Behold our final implementation of minimax by alpha-beta pruning:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxAB&#39;</span> <span class="ot">::</span> (<span class="dt">Ord</span> <span class="fu">score</span>, <span class="dt">Bounded</span> <span class="fu">score</span>) <span class="ot">=&gt;</span> <span class="dt">Game</span> <span class="fu">score</span> <span class="ot">-&gt;</span> <span class="fu">score</span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a><span class="fu">minimaxAB&#39;</span> <span class="ot">=</span> <span class="fu">declamp</span> <span class="op">.</span> <span class="fu">minimaxC</span> <span class="op">.</span> <span class="fu">fmap</span> <span class="fu">clamping</span></span></code></pre></div>
<p>To sum up, we implemented alpha-beta pruning as a simple combination of:</p>
<ul>
<li>minimax, generalized from orders to lattices (<code>minimaxL</code>);</li>
<li>the lattice of clamping functions (<code>Lattice (Clamping score)</code>).</li>
</ul>
<p>This alternative approach does not completely absolve you from effort:
you still have to juggle alphas and betas correctly to implement the lattice
(<code>lazyMinC</code> and <code>lazyMaxC</code>).
But unlike in the original <code>alphabeta</code>,
you don’t have to do all that juggling in the middle of a recursive function.
The logic of alpha-beta pruning is neatly decomposed into bite-sized pieces.</p>
<h2 id="correctness-for-free">Correctness for free</h2>
<p>Since we just reused the code of minimax, it’s also easier to <strong>prove</strong> that
that alpha-beta pruning yields the same result:</p>
<pre><code>minimax = minimaxAB&#39;</code></pre>
<p>As we are about to see, this is a direct consequence of
the free theorem<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> for <code>minimaxL</code>:
any function of type <code>forall s. Lattice s =&gt; Game s -&gt; s</code>,
such as <code>minimaxL</code>, commutes with any lattice homomorphism<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> <code>f</code>,
in the following sense:</p>
<pre><code>f . minimaxL = minimaxL . fmap f</code></pre>
<p>We can picture that equation as a commutative diagram:</p>
<p>\[\require{AMScd}
\begin{CD}
\small\texttt{Game s} @&gt;{\texttt{minimaxL}}&gt;&gt; \small\texttt{s} \\
@V{\texttt{fmap f}}VV @VV{\texttt{f}}V \\
\small\texttt{Game t} @&gt;{\texttt{minimaxL}}&gt;&gt; \small\texttt{t}
\end{CD}\]</p>
<p>If <code>f</code> has an inverse <code>f⁻¹</code>, we can rewrite that to</p>
<pre><code>minimaxL = f⁻¹ . minimaxL . fmap f</code></pre>
<p>By replacing <code>(f, f⁻¹)</code> with the isomorphism <code>(clamping, declamp)</code> defined earlier,
we obtain exactly the equality between minimax and alpha-beta pruning:</p>
<pre><code>minimaxL = declamp . minimaxL . fmap clamping
         = minimaxAB&#39;</code></pre>
<p>As a commutative diagram:</p>
<p>\[\require{AMScd}
\begin{CD}
\small\texttt{Game s} @&gt;{\texttt{minimaxAB’}\text{ (alpha-beta)}}&gt;&gt; \small\texttt{s} \\
@V{\texttt{fmap clamping}}VV @AA{\texttt{declamp}}A \\
\scriptsize\texttt{Game (Clamping s)} @&gt;{\texttt{minimaxL}}&gt;&gt; \scriptsize\texttt{Clamping s}
\end{CD}\]</p>
<p>QED.</p>
<p>(To be pedantic, the above proof
conflates <code>minimaxL</code> with <code>minimax</code>/<code>minimaxO</code>,
which relies on pretending that <code>Lattice</code> is a superclass of <code>Ord</code>.
Below is another proof that doesn’t take that shortcut,
by going through the <code>OrdLattice</code> newtype explicitly,
so this proof applies more directly to the Haskell definitions as written here.)</p>
<details>
<summary>
A somewhat more rigorous proof
</summary>
<p>We want to prove that the alpha-beta-pruning <code>minimaxAB'</code>
is equivalent to the naive <code>minimax</code>:</p>
<pre><code>minimax = minimaxAB&#39;</code></pre>
<p>Recall the free theorem of <code>minimaxL</code>. For any lattice isomorphism <code>(f, f⁻¹)</code>:</p>
<pre><code>minimaxL = f⁻¹ . minimaxL . fmap f</code></pre>
<p>Replace <code>(f, f⁻¹)</code> with the lattice isomorphism <code>(clamping . unOrdLattice, OrdLattice . declamp)</code>
between the lattices <code>OrdLattice score</code> and <code>Clamping score</code>.</p>
<pre><code>minimaxL = OrdLattice . declamp . minimaxL . fmap (clamping . unOrdLattice)</code></pre>
<p>Now we can prove the equality between <code>minimax</code> and <code>minimaxAB'</code>,
using the above equation as the middle step, followed by
canceling inverses:</p>
<pre><code>minimax
= minimaxO
= unOrdLattice . minimaxL . fmap OrdLattice
= unOrdLattice . OrdLattice . declamp . minimaxL . fmap (clamping . unOrdLattice) . fmap OrdLattice
= declamp . minimaxL . fmap clamping
= minimaxAB&#39;</code></pre>
</details>
<p>The above is only a proof of <em>functional correctness</em>:
<code>minimax</code> and <code>minimaxAB'</code> compute the same result.</p>
<p>To verify that <code>minimaxAB'</code> does so more efficiently
is another problem for another day. For now, we can <strong>test</strong> it.</p>
<h2 id="strictness-check">Strictness check</h2>
<p>We test that our “fancy” implementation of alpha-beta (<code>minimaxAB'</code>) has the same
strictness as the “classical” implementation (<code>minimaxAB</code>),
which we presume to be much lazier than minimax.</p>
<p>We use StrictCheck for property-testing of strictness behaviors in Haskell.
The following test checks that <code>minimaxAB</code> and <code>minimaxAB'</code> have the
same demand on random inputs.
We use the function <code>observe1</code> from StrictCheck to observe the demand
of a function <code>f</code>: <code>observe1</code> applies <code>f</code> it to an instrumented copy
of the provided input <code>g</code>, it forces the output (<code>f g</code> of type <code>Int</code>)
using the provided forcing function <code>(`seq` ())</code>,
and finally returns the demand on the input tree <code>g</code> that was observed
by forcing the instrumented copy of <code>g</code>.</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="fu">main</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a><span class="fu">main</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">quickCheck</span> <span class="op">$</span> <span class="op">\</span>(<span class="fu">g</span> <span class="ot">::</span> <span class="dt">Game</span> <span class="dt">Int</span>) <span class="ot">-&gt;</span></span>
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">label</span> (<span class="fu">bucket</span> (<span class="fu">length</span> <span class="fu">g</span>)) <span class="op">$</span></span>
<span id="cb39-5"><a href="#cb39-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="fu">demand</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">snd</span> (<span class="fu">observe1</span> (<span class="ot">`seq`</span> ()) <span class="fu">f</span> <span class="fu">g</span>) <span class="kw">in</span></span>
<span id="cb39-6"><a href="#cb39-6" aria-hidden="true" tabindex="-1"></a>    <span class="fu">demand</span> <span class="fu">minimaxAB</span> <span class="op">==</span><span class="ot">=</span> <span class="fu">demand</span> <span class="fu">minimaxAB&#39;</span></span></code></pre></div>
<p>From the <a href="https://gitlab.com/lysxia/blog.poisson.chat">source repository of this blog</a>,
the following command compiles and runs this blog post:</p>
<pre><code>cabal run alpha-beta</code></pre>
<details>
<summary>
Instances and auxiliary definitions
</summary>
<div class="sourceCode" id="cb41"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Histogram of generated value sizes</span></span>
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a><span class="fu">bucket</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">String</span></span>
<span id="cb41-3"><a href="#cb41-3" aria-hidden="true" tabindex="-1"></a><span class="fu">bucket</span> <span class="fu">n</span> <span class="op">|</span> <span class="fu">n</span> <span class="op">=</span><span class="ot">=</span> <span class="dv">1</span> <span class="ot">=</span> <span class="st">&quot;= 1&quot;</span></span>
<span id="cb41-4"><a href="#cb41-4" aria-hidden="true" tabindex="-1"></a>         <span class="op">|</span> <span class="fu">n</span> <span class="op">&lt;</span> <span class="dv">10</span> <span class="ot">=</span> <span class="st">&quot;&lt; 10&quot;</span></span>
<span id="cb41-5"><a href="#cb41-5" aria-hidden="true" tabindex="-1"></a>         <span class="op">|</span> <span class="fu">n</span> <span class="op">&lt;</span> <span class="dv">100</span> <span class="ot">=</span> <span class="st">&quot;&lt; 100&quot;</span></span>
<span id="cb41-6"><a href="#cb41-6" aria-hidden="true" tabindex="-1"></a>         <span class="op">|</span> <span class="fu">n</span> <span class="op">&lt;</span> <span class="dv">1000</span> <span class="ot">=</span> <span class="st">&quot;&lt; 1000&quot;</span></span>
<span id="cb41-7"><a href="#cb41-7" aria-hidden="true" tabindex="-1"></a>         <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> <span class="st">&quot;&gt;= 1000&quot;</span></span>
<span id="cb41-8"><a href="#cb41-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-9"><a href="#cb41-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- Instances</span></span>
<span id="cb41-10"><a href="#cb41-10" aria-hidden="true" tabindex="-1"></a><span class="kw">deriving</span> <span class="fu">stock</span> <span class="kw">instance</span> <span class="dt">GHC.Generic</span> (<span class="dt">Game</span> <span class="fu">a</span>) </span>
<span id="cb41-11"><a href="#cb41-11" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Generic</span> (<span class="dt">Game</span> <span class="fu">a</span>)</span>
<span id="cb41-12"><a href="#cb41-12" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">HasDatatypeInfo</span> (<span class="dt">Game</span> <span class="fu">a</span>)</span>
<span id="cb41-13"><a href="#cb41-13" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Shaped</span> <span class="fu">a</span> <span class="ot">=&gt;</span> <span class="dt">Shaped</span> (<span class="dt">Game</span> <span class="fu">a</span>)</span>
<span id="cb41-14"><a href="#cb41-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-15"><a href="#cb41-15" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Arbitrary</span> <span class="fu">a</span> <span class="ot">=&gt;</span> <span class="dt">Arbitrary</span> (<span class="dt">Game</span> <span class="fu">a</span>) <span class="kw">where</span></span>
<span id="cb41-16"><a href="#cb41-16" aria-hidden="true" tabindex="-1"></a>  <span class="fu">arbitrary</span> <span class="ot">=</span> <span class="fu">sized</span> <span class="op">$</span> <span class="op">\</span><span class="fu">n</span> <span class="ot">-&gt;</span> <span class="kw">if</span> <span class="fu">n</span> <span class="op">=</span><span class="ot">=</span> <span class="dv">0</span> <span class="kw">then</span> <span class="dt">End</span> <span class="op">&lt;$&gt;</span> <span class="fu">arbitrary</span> <span class="kw">else</span></span>
<span id="cb41-17"><a href="#cb41-17" aria-hidden="true" tabindex="-1"></a>    <span class="fu">resize</span> (<span class="fu">n</span> <span class="ot">`div`</span> <span class="dv">2</span>) <span class="op">$</span> <span class="fu">frequency</span></span>
<span id="cb41-18"><a href="#cb41-18" aria-hidden="true" tabindex="-1"></a>      [(<span class="dv">1</span>, <span class="dt">End</span> <span class="op">&lt;$&gt;</span> <span class="fu">arbitrary</span>), (<span class="dv">2</span>, <span class="dt">Max</span> <span class="op">&lt;$&gt;</span> <span class="fu">arbitrary</span>), (<span class="dv">2</span>, <span class="dt">Min</span> <span class="op">&lt;$&gt;</span> <span class="fu">arbitrary</span>)]</span>
<span id="cb41-19"><a href="#cb41-19" aria-hidden="true" tabindex="-1"></a>  <span class="fu">shrink</span> (<span class="dt">Max</span> (<span class="fu">g</span> <span class="op">:|</span> <span class="fu">gs</span>)) <span class="ot">=</span> <span class="fu">g</span> <span class="op">:</span> <span class="fu">gs</span> <span class="op">++</span> (<span class="dt">Max</span> <span class="op">&lt;$&gt;</span> <span class="fu">shrink</span> (<span class="fu">g</span> <span class="op">:|</span> <span class="fu">gs</span>))</span>
<span id="cb41-20"><a href="#cb41-20" aria-hidden="true" tabindex="-1"></a>  <span class="fu">shrink</span> (<span class="dt">Min</span> (<span class="fu">g</span> <span class="op">:|</span> <span class="fu">gs</span>)) <span class="ot">=</span> <span class="fu">g</span> <span class="op">:</span> <span class="fu">gs</span> <span class="op">++</span> (<span class="dt">Min</span> <span class="op">&lt;$&gt;</span> <span class="fu">shrink</span> (<span class="fu">g</span> <span class="op">:|</span> <span class="fu">gs</span>))</span>
<span id="cb41-21"><a href="#cb41-21" aria-hidden="true" tabindex="-1"></a>  <span class="fu">shrink</span> (<span class="dt">End</span> <span class="fu">s</span>) <span class="ot">=</span> <span class="dt">End</span> <span class="op">&lt;$&gt;</span> <span class="fu">shrink</span> <span class="fu">s</span></span>
<span id="cb41-22"><a href="#cb41-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-23"><a href="#cb41-23" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Arbitrary</span> <span class="fu">a</span> <span class="ot">=&gt;</span> <span class="dt">Arbitrary</span> (<span class="dt">NonEmpty</span> <span class="fu">a</span>) <span class="kw">where</span></span>
<span id="cb41-24"><a href="#cb41-24" aria-hidden="true" tabindex="-1"></a>  <span class="fu">arbitrary</span> <span class="ot">=</span> <span class="fu">liftA2</span> (<span class="op">:|</span>) <span class="fu">arbitrary</span> <span class="fu">arbitrary</span></span>
<span id="cb41-25"><a href="#cb41-25" aria-hidden="true" tabindex="-1"></a>  <span class="fu">shrink</span> (<span class="fu">x</span> <span class="op">:|</span> <span class="fu">xs</span>) <span class="ot">=</span> [<span class="fu">y</span> <span class="op">:|</span> <span class="fu">ys</span> <span class="op">|</span> <span class="fu">y</span> <span class="op">:</span> <span class="fu">ys</span> <span class="ot">&lt;-</span> <span class="fu">shrink</span> (<span class="fu">x</span> <span class="op">:</span> <span class="fu">xs</span>)]</span></code></pre></div>
</details>
<h2 id="conclusion">Conclusion</h2>
<p>I came up with this idea a while back on Stack Overflow, as an answer to
<a href="https://stackoverflow.com/questions/72517172/alpha-beta-pruning-with-recursion-schemes">Alpha-beta pruning with recursion schemes</a>.
My understanding of alpha-beta pruning changed overnight from a somewhat tricky algorithm
to a completely trivial solution.
Getting to reuse minimax is not only a satisfying achievement in refactoring,
it enables a neat proof of correctness by <em>parametricity</em> (via free theorems).</p>
<p>The role of laziness should also be underscored.
If you try to do the same thing in a call-by-value language,
the implementation of “generalized minimax” must
explicitly delay computations, obscuring the point:</p>
<blockquote>
Alpha-beta pruning is just minimax in a lattice of clamping functions.
</blockquote>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>For a clearer presentation, see the talk
<a href="https://www.youtube.com/watch?app=desktop&amp;v=I7HgWMnlFfM">Alpha-Beta Pruning Explored, Extended and Verified</a>
(2024) by Tobias Nipkow.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p><a href="https://dl.acm.org/doi/10.1145/99370.99404"><em>Theorems for free!</em></a> by Philip Wadler.
<a href="https://dl.acm.org/doi/10.1145/1631687.1596577"><em>Free theorems involving type constructor classes</em></a> by Janis Voigtländer.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>A lattice homomorphism <code>f</code> is a function that
commutes with the lattice operations:</p>
<pre><code>f (x /\ y) = f x /\ f y
f (x \/ y) = f x \/ f y</code></pre>
<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></li>
</ol>
</section>]]></summary>
</entry>
<entry>
    <title>Twentyseven 1.0.0</title>
    <link href="https://blog.poisson.chat/posts/2025-08-01-twentyseven.html" />
    <id>https://blog.poisson.chat/posts/2025-08-01-twentyseven.html</id>
    <published>2025-08-01T00:00:00Z</published>
    <updated>2025-08-01T00:00:00Z</updated>
    <summary type="html"><![CDATA[<h2 id="twelve-years-of-haskell">Twelve years of Haskell</h2>
<p><a href="https://github.com/Lysxia/twentyseven">Twentyseven</a>
is a Rubik’s cube solver and one of my earliest projects in Haskell.
The first commit dates from January 2014, and version 0.0.0 was uploaded on Hackage in March 2016.</p>
<p>I first heard of Haskell in a course on lambda calculus in 2013.
A programming language with lazy evaluation sounded
like a crazy idea, so I gave it a try.
Since then, I have kept writing in Haskell as my favorite language.
For me it is the ideal blend of programming and math.
And a Rubik’s cube solver is a great excuse for doing group theory.</p>
<p><a href="https://hackage.haskell.org/package/twentyseven-1.0.0">Twentyseven 1.0.0</a> is more of a commemorative release for myself,
with the goal of making it compile with the current version of GHC (9.12).
There was surprisingly little breakage:</p>
<ol type="1">
<li><a href="https://github.com/Lysxia/twentyseven/pull/4/files"><code>Semigroup</code> has become a superclass of <code>Monoid</code></a></li>
<li><a href="https://github.com/Lysxia/twentyseven/pull/7/files#diff-e3649649115d119680513c282d18b6773d2dfecbb63419118a5243c567863906">A breaking change in the Template Haskell AST</a></li>
</ol>
<p>Aside from that, the code is basically just as it was 9 years ago,
including design decisions that I would find questionable today.
For example, I use <code>unsafePerformIO</code> to read precomputed tables
into top-level constants, but the location of the files to read from
can be configured by command-line arguments, so I better make sure that
the tables are not forced before the location is set…</p>
<h2 id="how-twentyseven-works">How Twentyseven works</h2>
<p>The input of the program is a string enumerating the 54 facelets
of a Rubik’s cube, each character represents one color.</p>
<pre><code>DDDFUDLRB FUFDLLLRR UBLBFDFUD ULBFRULLB RRRLBBRUB UBFFDFDRU</code></pre>
<p>The facelets follow the order pictured below. They are grouped
by faces (up, left, front, right, back, top), and in each face
they are listed in top-down, left-right order.</p>
<pre><code>                  00 01 02
                  03 04 05
                  06 07 08

        10 11 12  20 21 22  30 31 32  40 41 42
        13 14 15  23 24 25  33 34 35  43 44 45
        16 17 18  26 27 28  36 37 38  46 47 48

                  50 51 52
                  53 54 55
                  56 57 58</code></pre>
<p>The output is a sequence of moves to solve that cube.</p>
<pre><code>U L B&#39; L R2 D R U2 F U2 L2 B2 U B2 D&#39; B2 U&#39; R2 U L2 R2 U</code></pre>
<p>The implementation of Twentyseven is based on Herbert Kociemba’s <a href="https://kociemba.org/cube.htm">notes
about <em>Cube Explorer</em></a>, a program written in Pascal!</p>
<p>The search algorithm is <a href="https://en.wikipedia.org/wiki/Iterative_deepening_A*">iterative deepening A*</a>, or IDA*. Like A*, IDA* finds
the shortest path between two vertices in a graph.
A conventional A* is not feasible because the state space of a Rubik’s cube is massive (43 252 003 274 489 856 000 states,
literally billions of billions).
Instead, we run a series of depth-first searches
with a maximum allowed number of moves that increases for each search.
As it is based on depth-first search,
IDA* only needs memory for the current path,
which is super cheap.</p>
<p>IDA* relies on an estimate of the number of moves remaining
to reach the solved state. We obtain such an estimate by
projecting the Rubik’s cube state into a simpler puzzle.
For example, we can consider only the permutation of corners,
ignoring their orientation.
We can pre-compute a table mapping each corner permutation
(there are 8! = 40320) to the minimum
number of moves to put the corners back to their location.
This is a lower bound on the number of moves to actually solve a Rubik’s cube.
Different projections yield different lower bounds (for example, by
looking at the permutation of edges instead, or their orientation),
and we can combine lower bounds into their maximum,
yielding a more precise lower bound, and thus a faster IDA*.</p>
<p>Putting all that together, we obtain an optimal solver for Rubik’s cubes.
But even with these heuristics, Twentyseven can take hours to solve a random cube optimally.
Kociemba’s Cube Explorer is apparently much faster
(I’ve never tried it myself).
My guess is that the difference is due to a better selection of projections,
yielding better heuristics.
But I haven’t gotten around to figure out whether I’ve misinterpreted
his notes or those improvements can only be found in the code.</p>
<p>A faster alternative is Kociemba’s two phase algorithm.
It is suboptimal, but it solves Rubik’s cubes in a fraction of a second
(1000 cubes per minute).
The first phase puts cubies into a “common orientation”
and “separates” the edges into two groups.
In other words, we reach a state where the permutation
of 12 edges can be decomposed into two disjoint
permutations of 4 and 8 edges respectively.
In the second phase, we restrict the possible moves:
quarter- and half-turns on the top and bottom faces,
half-turns only on the other faces.
These restricted moves preserve the “common orientation” of edges and corners
from phase 1,
and the edges in the middle slice stay in their slice.
Each phase thus performs an IDA* search in a much smaller space
than the full Rubik’s cube state space (2 217 093 120 and 19 508 428 800
states respectively).</p>]]></summary>
</entry>
<entry>
    <title>Unfolding trees breadth-first in Haskell</title>
    <link href="https://blog.poisson.chat/posts/2025-03-30-breadth-first-unfolds.html" />
    <id>https://blog.poisson.chat/posts/2025-03-30-breadth-first-unfolds.html</id>
    <published>2025-03-30T00:00:00Z</published>
    <updated>2025-03-30T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>To visit a tree or graph in breadth-first order, there are two main
implementation approaches: queue-based or level-based.
Our goal here is to develop a level-based approach where the levels of
the breadth-first walk are constructed <em>compositionally</em> and <em>dynamically</em>.</p>
<p>Compositionality means that for every node, its descendants—the other nodes
reachable from it—are defined by composing the descendants of its children.
Dynamism means that the children of a node are generated only when that node
is visited; we will see that this requirement corresponds to asking for a
<em>monadic unfold</em>.</p>
<p>A prior solution, using <a href="https://hackage.haskell.org/package/tree-traversals-0.1.3.0/docs/Control-Applicative-Phases.html">the <code>Phases</code> applicative functor</a>,
is compositional but not dynamic in that sense. The essence of <code>Phases</code>
is a zipping operation in free applicative functors.
What if we did zipping in free monads instead?</p>
<p>This is a Literate Haskell post.
<a href="https://gitlab.com/lysxia/blog.poisson.chat/-/blob/main/posts/2025-03-30-breadth-first-unfolds.lhs?ref_type=heads">The source code is on Gitlab.</a>
A reusable version of this code is
now available on Hackage: <a href="https://hackage.haskell.org/package/weave">the <em>weave</em> library</a>.</p>
<pre class="toc"><code></code></pre>
<details class="code-details">
<summary>
Extensions and imports for this Literate Haskell file
</summary>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE DeriveAnyClass #-}</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE DeriveTraversable #-}</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE DeriveGeneric #-}</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE GADTs #-}</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE LambdaCase #-}</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE PackageImports #-}</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE PatternSynonyms #-}</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE RankNTypes #-}</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE ScopedTypeVariables #-}</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE ViewPatterns #-}</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# OPTIONS_GHC -Wno-x-partial -Wno-unused-matches -Wno-unused-top-binds -Wno-unused-imports #-}</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> &quot;<span class="fu">deepseq</span>&quot; <span class="dt">Control.DeepSeq</span> (<span class="dt">NFData</span>)</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Foldable</span> (<span class="fu">toList</span>)</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Function</span> ((&amp;))</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Functor</span> ((&lt;&amp;&gt;))</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Functor.Identity</span> (<span class="dt">Identity</span>(..), <span class="fu">runIdentity</span>)</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">GHC.Generics</span> (<span class="dt">Generic</span>)</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> &quot;<span class="fu">tasty</span>&quot; <span class="dt">Test.Tasty</span> (<span class="dt">TestTree</span>, <span class="fu">localOption</span>)</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> &quot;<span class="fu">tasty</span>-<span class="fu">hunit</span>&quot; <span class="dt">Test.Tasty.HUnit</span> ((@?=), <span class="fu">testCase</span>)</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> &quot;<span class="fu">tasty</span>-<span class="fu">bench</span>&quot; <span class="dt">Test.Tasty.Bench</span> (<span class="fu">bgroup</span>, <span class="fu">bench</span>, <span class="fu">defaultMain</span>, <span class="fu">nf</span>, <span class="fu">bcompare</span>)</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a><span class="co">-- import &quot;tasty-bench&quot; Test.Tasty.Bench (mutatorCpuTime)</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> &quot;<span class="fu">tasty</span>-<span class="fu">expected</span>-<span class="fu">failure</span>&quot; <span class="dt">Test.Tasty.ExpectedFailure</span> (<span class="fu">expectFail</span>)</span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> &quot;<span class="fu">some</span>&quot; <span class="dt">Data.Some.Newtype</span> (<span class="dt">Some</span>(<span class="dt">Some</span>))</span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> &quot;<span class="fu">transformers</span>&quot; <span class="dt">Control.Monad.Trans.State</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> &quot;<span class="fu">containers</span>&quot; <span class="dt">Data.Set</span> <span class="kw">as</span> <span class="dt">Set</span></span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> &quot;<span class="fu">containers</span>&quot; <span class="dt">Data.Set</span> (<span class="dt">Set</span>)</span></code></pre></div>
</details>
<h2 id="background-breadth-first-folds-and-traversals">Background: breadth-first folds and traversals</h2>
<p>Our running example will be the type of binary trees:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Tree</span> <span class="fu">a</span> <span class="ot">=</span> <span class="dt">Leaf</span> <span class="op">|</span> <span class="dt">Node</span> <span class="fu">a</span> (<span class="dt">Tree</span> <span class="fu">a</span>) (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>, <span class="dt">Generic</span>, <span class="dt">NFData</span>)</span></code></pre></div>
<p>A breadth-first walk explores the tree level by level; every level contains the
nodes at the same distance from the root. The list of levels of a tree can be defined
recursively—it is a fold. For a tree <code>Node x l r</code>, the first level contains
just the root node <code>x</code>, and the subsequent levels are obtained by appending the
levels of the subtrees <code>l</code> and <code>r</code> pairwise.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">levels</span> <span class="ot">::</span> <span class="dt">Tree</span> <span class="fu">a</span> <span class="ot">-&gt;</span> [[<span class="fu">a</span>]]</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="fu">levels</span> <span class="dt">Leaf</span> <span class="ot">=</span> []</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="fu">levels</span> (<span class="dt">Node</span> <span class="fu">x</span> <span class="fu">l</span> <span class="fu">r</span>) <span class="ot">=</span> [<span class="fu">x</span>] <span class="op">:</span> <span class="fu">zipLevels</span> (<span class="fu">levels</span> <span class="fu">l</span>) (<span class="fu">levels</span> <span class="fu">r</span>)</span></code></pre></div>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="fu">zipLevels</span> <span class="ot">::</span> [[<span class="fu">a</span>]] <span class="ot">-&gt;</span> [[<span class="fu">a</span>]] <span class="ot">-&gt;</span> [[<span class="fu">a</span>]]</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="fu">zipLevels</span> [] <span class="fu">yss</span> <span class="ot">=</span> <span class="fu">yss</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="fu">zipLevels</span> <span class="fu">xss</span> [] <span class="ot">=</span> <span class="fu">xss</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="fu">zipLevels</span> (<span class="fu">xs</span> <span class="op">:</span> <span class="fu">xss</span>) (<span class="fu">ys</span> <span class="op">:</span> <span class="fu">yss</span>) <span class="ot">=</span> (<span class="fu">xs</span> <span class="op">++</span> <span class="fu">ys</span>) <span class="op">:</span> <span class="fu">zipLevels</span> <span class="fu">xss</span> <span class="fu">yss</span></span></code></pre></div>
<p>(We can’t just use <code>zipWith</code> because it throws away the end of a list when the
other list is empty.)</p>
<p>Finally, we concatenate the levels together to obtain the list of nodes in
breadth-first order.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="fu">toListBF</span> <span class="ot">::</span> <span class="dt">Tree</span> <span class="fu">a</span> <span class="ot">-&gt;</span> [<span class="fu">a</span>]</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="fu">toListBF</span> <span class="ot">=</span> <span class="fu">concat</span> <span class="op">.</span> <span class="fu">levels</span></span></code></pre></div>
<p>Thanks to laziness, the list will indeed be produced by walking the tree in
breadth-first order.
So far so good.</p>
<p>The above function lets us <em>fold</em> a tree in breadth-first order.
The next level of difficulty is to <em>traverse</em> a tree, producing a tree
with the same shape as the original tree, only with modified labels.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="fu">traverseBF</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> <span class="fu">b</span>) <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">b</span>)</span></code></pre></div>
<p>This has the exact same type as <code>traverse</code>, which you might obtain with
<code>deriving (Foldable, Traversable)</code>. The stock-derived <code>Traversable</code>—enabled
by the <code>DeriveTraversable</code> extension—is a depth-first traversal, but the laws
of <code>traverse</code> don’t specify the order in which nodes should be visited,
so you could make it a breadth-first traversal if you wanted.</p>
<p>To define a breadth-first traversal is a surprisingly non-trivial exercise,
as pointed out by Chris Okasaki in
<a href="https://dl.acm.org/doi/10.1145/351240.351253"><em>Breadth-first numbering: lessons from a small exercise in algorithm design</em> (ICFP 2000)</a>.</p>
<p>“Breadth-first numbering” is a special case of “breadth-first traversal”
where the arrow <code>(a -&gt; m b)</code> is specialized to a counter.
Okasaki presents a “numbering” solution based on queues and another solution
based on levels.
Both are easily adaptable to the more general “traversal” problem as we will
soon see.</p>
<p>There is <a href="https://discourse.haskell.org/t/breadth-first-tree-relabelling-or-the-power-of-lazyness/9961">a wonderful Discourse thread from 2024</a> on the topic of
breadth-first traversals.
The first post gives an elegant breadth-first numbering algorithm
which also appears in the appendix of Okasaki’s paper,
but sadly it does not generalize from “numbering” to
“traversal” beyond the special case <code>m = State s</code>.</p>
<p>Last but not least, another level-based solution to the breadth-first traversal
problem can be found in the
<a href="https://hackage.haskell.org/package/tree-traversals"><em>tree-traversals</em> library</a> by Noah Easterly.
It is built around <a href="https://hackage.haskell.org/package/tree-traversals-0.1.3.0/docs/Control-Applicative-Phases.html">an applicative transformer named <code>Phases</code></a>,
which is a list of actions—imagine the type “<code>[m _]</code>”—where each
element <code>m _</code> represents one level of the tree.
The <code>Phases</code> applicative enables a <em>compositional</em> definition of a
breadth-first traversal, similarly to the <code>levels</code> function above:
the set of nodes reachable from the root is defined by combining the sets of
nodes reachable from its children. This concern of compositionality
is one of the main motivations behind this post.</p>
<h3 id="non-standard-terminology">Non-standard terminology</h3>
<p>The broad family of algorithms being discussed is typically called
“breadth-first search” (BFS) or “breadth-first traversal”,
but in general these algorithms are not “searching” for anything,
and in Haskell, “traversal” is reserved for “things like <code>traverse</code>”.
Instead, this post will use “walks” as a term encompassing folds, traversals,
unfolds, or any concept that can be qualified with “breadth-first”.</p>
<h2 id="problem-statement-breadth-first-unfolds">Problem statement: Breadth-first unfolds</h2>
<p>Both the fold <code>toListBF</code> and the traversal <code>traverseBF</code> had in common that they
receive a tree as an input. This explicit tree makes the notion of levels
“static”. With unfolds, we will have to deal with levels that exist only
“dynamically” as the result of <em>unfolding</em> the tree progressively.</p>
<p>To introduce the unfolding of a tree, it is convenient to introduce its “base
functor”. We modify the tree type by replacing the recursive tree fields with
an extra type parameter:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">t</span> <span class="ot">=</span> <span class="dt">LeafF</span> <span class="op">|</span> <span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">t</span> <span class="fu">t</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> (<span class="dt">Functor</span>, <span class="dt">Foldable</span>, <span class="dt">Traversable</span>)</span></code></pre></div>
<p>An unfold generates a tree from a seed and a
function which expands the seed into a leaf or a node containing more seeds.
A <em>pure unfold</em>—or <em>anamorphism</em>—can be defined readily:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfold</span> <span class="ot">::</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="fu">a</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfold</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="kw">case</span> <span class="fu">f</span> <span class="fu">s</span> <span class="kw">of</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">LeafF</span> <span class="ot">-&gt;</span> <span class="dt">Leaf</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">l</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Node</span> <span class="fu">a</span> (<span class="fu">unfold</span> <span class="fu">f</span> <span class="fu">l</span>) (<span class="fu">unfold</span> <span class="fu">f</span> <span class="fu">r</span>)</span></code></pre></div>
<p>The order in which nodes are evaluated depends on
how the resulting tree is consumed. Hence <code>unfold</code>
is neither inherently “depth-first” nor “breadth-first”.</p>
<p>The situation changes if we make the unfold monadic.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span></code></pre></div>
<p>An implementation of <code>unfoldM</code> must decide upon an ordering between actions.
To see why adding an <code>M</code> to <code>unfold</code> imposes an ordering,
contemplate the fact that these expressions have the same meaning:</p>
<pre><code>Node a (unfold f l) (unfold f r)
= ( let tl = unfold f l in
    let tr = unfold f r in
    Node a tl tr )
= ( let tr = unfold f r in
    let tl = unfold f l in
    Node a tl tr )</code></pre>
<p>whereas these monadic expressions do not have the same meaning in general:</p>
<pre><code>( unfoldM f l &gt;&gt;= \tl -&gt;
  unfoldM f r &gt;&gt;= \tr -&gt;
  pure (Node a tl tr) )
/=
( unfoldM f r &gt;&gt;= \tr -&gt;
  unfoldM f l &gt;&gt;= \tl -&gt;
  pure (Node a tl tr) )</code></pre>
<p>Without further requirements, there is an “obvious” definition of <code>unfoldM</code>,
which is a <em>depth-first unfold</em>:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_DF</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_DF</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="op">\</span><span class="kw">case</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">LeafF</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="dt">Leaf</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">l</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="fu">liftA2</span> (<span class="dt">Node</span> <span class="fu">a</span>) (<span class="fu">unfoldM_DF</span> <span class="fu">f</span> <span class="fu">l</span>) (<span class="fu">unfoldM_DF</span> <span class="fu">f</span> <span class="fu">r</span>)</span></code></pre></div>
<p>We unfold the left subtree <code>l</code> fully before unfolding the right one <code>r</code>.</p>
<p><strong>The problem is to define a <em>breadth-first</em> <code>unfoldM</code>.</strong></p>
<p>If you want to think about this problem on your own, you can stop reading here.
The rest of this post presents solutions.</p>
<h2 id="queue-based-unfold">Queue-based unfold</h2>
<p>The two breadth-first numbering algorithms in <a href="https://dl.acm.org/doi/10.1145/351240.351253">Okasaki’s paper</a> can
actually be generalized to breadth-first unfolds.
Here is the first one that uses queues (using the function <code>(&lt;+)</code> for “push” and
pattern-matching on <code>(:&gt;)</code> for “pop”):</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_Q</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_Q</span> <span class="fu">f</span> <span class="fu">b0</span> <span class="ot">=</span> <span class="fu">go</span> (<span class="fu">b0</span> <span class="op">&lt;+</span> <span class="dt">Empty</span>) <span class="op">&lt;&amp;&gt;</span> <span class="op">\</span><span class="kw">case</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>    <span class="fu">_</span> <span class="op">:&gt;</span> <span class="fu">t</span> <span class="ot">-&gt;</span> <span class="fu">t</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="st">&quot;impossible&quot;</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>    <span class="fu">go</span> <span class="dt">Empty</span> <span class="ot">=</span> <span class="fu">pure</span> <span class="dt">Empty</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">go</span> (<span class="fu">q</span> <span class="op">:&gt;</span> <span class="fu">b</span>) <span class="ot">=</span> <span class="fu">f</span> <span class="fu">b</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="op">\</span><span class="kw">case</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>      <span class="dt">LeafF</span> <span class="ot">-&gt;</span> <span class="fu">go</span> <span class="fu">q</span> <span class="op">&lt;&amp;&gt;</span> <span class="op">\</span><span class="fu">p</span> <span class="ot">-&gt;</span> <span class="dt">Leaf</span> <span class="op">&lt;+</span> <span class="fu">p</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>      <span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">b1</span> <span class="fu">b2</span> <span class="ot">-&gt;</span> <span class="fu">go</span> (<span class="fu">b2</span> <span class="op">&lt;+</span> <span class="fu">b1</span> <span class="op">&lt;+</span> <span class="fu">q</span>) <span class="op">&lt;&amp;&gt;</span> <span class="op">\</span><span class="kw">case</span></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a>        <span class="fu">p</span> <span class="op">:&gt;</span> <span class="fu">t1</span> <span class="op">:&gt;</span> <span class="fu">t2</span> <span class="ot">-&gt;</span> <span class="dt">Node</span> <span class="fu">a</span> <span class="fu">t1</span> <span class="fu">t2</span> <span class="op">&lt;+</span> <span class="fu">p</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a>        <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="st">&quot;impossible&quot;</span></span></code></pre></div>
<p>(The operator <code>(&lt;&amp;&gt;)</code> is <code>flip (&lt;$&gt;)</code>. I use it to avoid parentheses around
lambdas.)</p>
<details class="code-details">
<summary>
Queue implementation for <code>unfoldM_BF_Q</code>
</summary>
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Q</span> <span class="fu">a</span> <span class="ot">=</span> <span class="dt">Q</span> [<span class="fu">a</span>] [<span class="fu">a</span>]</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="fu">pattern</span> <span class="dt">Empty</span> <span class="ot">::</span> <span class="dt">Q</span> <span class="fu">a</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a><span class="fu">pattern</span> <span class="dt">Empty</span> <span class="ot">=</span> <span class="dt">Q</span> [] []</span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a><span class="kw">infixr</span> <span class="dv">1</span> <span class="op">&lt;+</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>(<span class="op">&lt;+</span>) <span class="ot">::</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Q</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Q</span> <span class="fu">a</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a><span class="fu">x</span> <span class="op">&lt;+</span> <span class="dt">Q</span> <span class="fu">xs</span> <span class="fu">ys</span> <span class="ot">=</span> <span class="dt">Q</span> (<span class="fu">x</span> <span class="op">:</span> <span class="fu">xs</span>) <span class="fu">ys</span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a><span class="fu">pop</span> <span class="ot">::</span> <span class="dt">Q</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> (<span class="dt">Q</span> <span class="fu">a</span>, <span class="fu">a</span>)</span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a><span class="fu">pop</span> (<span class="dt">Q</span> <span class="fu">xs</span> (<span class="fu">y</span> <span class="op">:</span> <span class="fu">ys</span>)) <span class="ot">=</span> <span class="dt">Just</span> (<span class="dt">Q</span> <span class="fu">xs</span> <span class="fu">ys</span>, <span class="fu">y</span>)</span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a><span class="fu">pop</span> (<span class="dt">Q</span> <span class="fu">xs</span> []) <span class="ot">=</span> <span class="kw">case</span> <span class="fu">reverse</span> <span class="fu">xs</span> <span class="kw">of</span></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a>  [] <span class="ot">-&gt;</span> <span class="dt">Nothing</span></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a>  <span class="fu">y</span> <span class="op">:</span> <span class="fu">ys</span> <span class="ot">-&gt;</span> <span class="dt">Just</span> (<span class="dt">Q</span> [] <span class="fu">ys</span>, <span class="fu">y</span>)</span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a><span class="kw">infixl</span> <span class="dv">1</span> <span class="op">:&gt;</span></span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true" tabindex="-1"></a><span class="fu">pattern</span> (<span class="op">:&gt;</span>) <span class="ot">::</span> <span class="dt">Q</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Q</span> <span class="fu">a</span></span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true" tabindex="-1"></a><span class="fu">pattern</span> <span class="fu">q</span> <span class="op">:&gt;</span> <span class="fu">y</span> <span class="ot">&lt;-</span> (<span class="fu">pop</span> <span class="ot">-&gt;</span> <span class="dt">Just</span> (<span class="fu">q</span>, <span class="fu">y</span>))</span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-20"><a href="#cb15-20" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# COMPLETE Empty, (:&gt;) #-}</span></span></code></pre></div>
</details>
<p>As it happens, <em>containers</em> uses that queue-based technique to implement
<a href="https://hackage.haskell.org/package/containers-0.7/docs/Data-Tree.html#v:unfoldTreeM_BF">breadth-first unfold for rose trees (<code>Data.Tree.unfoldTreeM_BF</code>)</a>.
There is a pending question of <a href="https://github.com/haskell/containers/pull/198">whether we can improve upon it</a>.
This post might provide a theoretical alternative,
but it seems too slow to be worth serious consideration
(<a href="#benchmarks">see the benchmark section</a>).</p>
<p>If you’re frowning upon the use of <code>error</code>—as you should be—you can replace
<code>error</code> with dummy values here (<code>Empty</code>, <code>Leaf</code>), but
(1) that won’t be possible with tree structures that must be non-empty
(<em>e.g.</em>, if <code>Leaf</code> contained a value) and (2) this is dead code, which
is harmless but no more elegant than making it obvious with <code>error</code>.</p>
<p>The correctness of this solution is also not quite obvious.
There are subtle ways to get this implementation wrong:
should the recursive call be <code>b2 &lt;+ b1 &lt;+ q</code> or <code>b1 &lt;+ b2 &lt;+ q</code>?
Should the pattern be <code>p :&gt; t1 :&gt; t2</code> or <code>p :&gt; t2 :&gt; t1</code>?
For another version of this challenge, try implementing the unfold for another
tree type, such as finger trees or rose trees, without getting lost in the
order of pushes and pops (by the way, this is <code>Data.Tree.unfoldTreeM_BF</code> in
<em>containers</em>). The invariant is not complex but there is room for mistakes.
I believe that the compositional approach that will be presented later is more
robust on that front, although it is admittedly a subjective quality for which
is difficult to make a strong case.</p>
<h2 id="some-uses-of-unfolds">Some uses of unfolds</h2>
<h3 id="traversals-from-unfolds">Traversals from unfolds</h3>
<p>One sense in which <code>unfoldM</code> is a more difficult problem than <code>traverse</code> is
that we can use <code>unfoldM</code> to implement <code>traverse</code>.
We do have to make light of the technicality that there is a <code>Monad</code> constraint
instead of <code>Applicative</code>, which makes <code>unfoldM</code> not suited to implement the
<code>Traversable</code> class.</p>
<p>A depth-first unfold gives a depth-first traversal:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="fu">traverse_DF</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> <span class="fu">b</span>) <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">b</span>)</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="fu">traverse_DF</span> <span class="ot">=</span> <span class="fu">unfoldM_DF</span> <span class="op">.</span> <span class="fu">traverseRoot</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- auxiliary function</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="fu">traverseRoot</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> <span class="fu">b</span>) <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">b</span> (<span class="dt">Tree</span> <span class="fu">a</span>))</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a><span class="fu">traverseRoot</span> <span class="fu">_</span> <span class="dt">Leaf</span> <span class="ot">=</span> <span class="fu">pure</span> <span class="dt">LeafF</span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="fu">traverseRoot</span> <span class="fu">f</span> (<span class="dt">Node</span> <span class="fu">a</span> <span class="fu">l</span> <span class="fu">r</span>) <span class="ot">=</span> <span class="fu">f</span> <span class="fu">a</span> <span class="op">&lt;&amp;&gt;</span> <span class="op">\</span><span class="fu">b</span> <span class="ot">-&gt;</span> <span class="dt">NodeF</span> <span class="fu">b</span> <span class="fu">l</span> <span class="fu">r</span></span></code></pre></div>
<p>A breadth-first unfold gives a breadth-first traversal:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="fu">traverse_BF_Q</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> <span class="fu">b</span>) <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">b</span>)</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="fu">traverse_BF_Q</span> <span class="ot">=</span> <span class="fu">unfoldM_BF_Q</span> <span class="op">.</span> <span class="fu">traverseRoot</span></span></code></pre></div>
<h3 id="unfolds-in-graphs">Unfolds in graphs</h3>
<p>We can use a tree unfold to explore a graph.
This usage distinguishes unfolds from folds and traversals,
which only let you explore trees.</p>
<p>Given a type of vertices <code>V</code>, a directed graph is represented by a function
<code>V -&gt; F V</code>, where <code>F</code> is a functor which describes the arity of each node.
The obvious choice for <code>F</code> is lists, but we will stick to <code>TreeF</code> here
so we can just reuse this post’s <code>unfoldM</code> implementations.
The <code>TreeF</code> functor restricts us graphs where each node has zero or two
outgoing edges; it is a weird restriction, but we will make do for the sake of
example.</p>
<figure>
<figcaption>
An ASCII drawing of a graph
</figcaption>
<pre><code>        +-------+
        v       |
+-&gt;1---&gt;2---&gt;3  |
|  |    |    ^  |
|  v    v    |  |
|  4---&gt;5---&gt;6--+
|  |    |    ^
|  +----|----+
|       |
+-------+</code></pre>
</figure>
<p>The graph drawn above turns into the following function, where every vertex
is mapped either to <code>NodeF</code> with the same vertex as the first argument followed
by its two adjacent vertices, or to <code>LeafF</code> if it has no outgoing edges or does
not belong to the graph.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="fu">graph</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">TreeF</span> <span class="dt">Int</span> <span class="dt">Int</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="fu">graph</span> <span class="dv">1</span> <span class="ot">=</span> <span class="dt">NodeF</span> <span class="dv">1</span> <span class="dv">2</span> <span class="dv">4</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="fu">graph</span> <span class="dv">2</span> <span class="ot">=</span> <span class="dt">NodeF</span> <span class="dv">2</span> <span class="dv">3</span> <span class="dv">5</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a><span class="fu">graph</span> <span class="dv">3</span> <span class="ot">=</span> <span class="dt">LeafF</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="fu">graph</span> <span class="dv">4</span> <span class="ot">=</span> <span class="dt">NodeF</span> <span class="dv">4</span> <span class="dv">5</span> <span class="dv">6</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="fu">graph</span> <span class="dv">5</span> <span class="ot">=</span> <span class="dt">NodeF</span> <span class="dv">5</span> <span class="dv">1</span> <span class="dv">6</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a><span class="fu">graph</span> <span class="dv">6</span> <span class="ot">=</span> <span class="dt">NodeF</span> <span class="dv">6</span> <span class="dv">2</span> <span class="dv">3</span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a><span class="fu">graph</span> <span class="fu">_</span> <span class="ot">=</span> <span class="dt">LeafF</span></span></code></pre></div>
<p>If we simply feed that function to <code>unfold</code>, we will get the infinite tree
of all possible paths from a chosen starting vertex.</p>
<p>To obtain a finite tree, we want to keep track of vertices that we have
already visited, using a stateful memory. The following function wraps <code>graph</code>,
returning <code>LeafF</code> also if a vertex has already been visited.</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="fu">visitGraph</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">State</span> (<span class="dt">Set</span> <span class="dt">Int</span>) (<span class="dt">TreeF</span> <span class="dt">Int</span> <span class="dt">Int</span>)</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="fu">visitGraph</span> <span class="fu">vertex</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">visited</span> <span class="ot">&lt;-</span> <span class="fu">get</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">if</span> <span class="fu">vertex</span> <span class="ot">`elem`</span> <span class="fu">visited</span> <span class="kw">then</span> <span class="fu">pure</span> <span class="dt">LeafF</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">do</span></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>    <span class="fu">put</span> (<span class="fu">Set.insert</span> <span class="fu">vertex</span> <span class="fu">visited</span>)</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">pure</span> (<span class="fu">graph</span> <span class="fu">vertex</span>)</span></code></pre></div>
<p>Applying <code>unfoldM_BF</code> to that function produces a “breadth-first tree”
of the graph, an encoding of the trajectory of a breadth-first walk through the
graph. “Breadth-first trees” are a concept from graph theory with well-studied
properties.</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Visit `graph` in breadth-first order</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraph_Q</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="dt">Int</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraph_Q</span> <span class="ot">=</span> (<span class="ot">`evalState`</span> <span class="fu">Set.empty</span>) <span class="op">.</span> <span class="fu">unfoldM_BF_Q</span> <span class="fu">visitGraph</span></span></code></pre></div>
<div class="sourceCode" id="cb22"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphQ</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphQ</span> <span class="ot">=</span> <span class="fu">testCase</span> <span class="st">&quot;Q-graph&quot;</span> <span class="op">$</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">bfGraph_Q</span> <span class="dv">1</span> <span class="op">@?</span><span class="ot">=</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="dv">1</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">2</span> <span class="dt">Leaf</span></span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a>              (<span class="dt">Node</span> <span class="dv">5</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">4</span> <span class="dt">Leaf</span> (<span class="dt">Node</span> <span class="dv">6</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span></code></pre></div>
<h3 id="compile-and-run">Compile and run</h3>
<p>This post is a compilable Literate Haskell file. You can run all of the tests
and benchmarks in here. The source repository provides the necessary
configuration to build it with <code>cabal</code>.</p>
<pre><code>$ cabal build breadth-first-unfolds</code></pre>
<p>Test cases can then be selected with the <code>-p</code> option and a pattern
(see <a href="https://hackage.haskell.org/package/tasty#readme">the <em>tasty</em> documentation</a> for details).
Run all tests and benchmarks by passing no option.</p>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;/Q-graph/||/S-graph/&quot;
All
  Q-graph: OK
  S-graph: OK</code></pre>
<h2 id="global-level-based-unfold">“Global” level-based unfold</h2>
<p>The other solution from Okasaki’s paper can also be adapted into a monadic unfold.</p>
<p>The starting point is to unfold a list of seeds <code>[s]</code> instead of a single seed:
we can traverse the list with the expansion function <code>s -&gt; m (TreeF a s)</code> to
obtain another list of seeds, the next level of the breadth-first unfold,
and keep going.</p>
<p>Iterating this process naively yields a variant of monadic unfold without a
result. This no-result variant can be generalized from <code>TreeF</code> to
any foldable structure:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Inner loop: multi-seed unfold</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldsM_BF_G_</span> <span class="ot">::</span> (<span class="dt">Monad</span> <span class="fu">m</span>, <span class="dt">Foldable</span> <span class="fu">f</span>) <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="fu">f</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> [<span class="fu">s</span>] <span class="ot">-&gt;</span> <span class="fu">m</span> ()</span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldsM_BF_G_</span> <span class="fu">f</span> [] <span class="ot">=</span> <span class="fu">pure</span> ()</span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- Read from right to left: traverse, flatten, recurse.</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldsM_BF_G_</span> <span class="fu">f</span> <span class="fu">xs</span> <span class="ot">=</span> <span class="fu">unfoldsM_BF_G_</span> <span class="fu">f</span> <span class="op">.</span> <span class="fu">concatMap</span> <span class="fu">toList</span> <span class="op">=&lt;&lt;</span> <span class="fu">traverse</span> <span class="fu">f</span> <span class="fu">xs</span></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a><span class="co">-- Top-level function: single-seed unfold</span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_G_</span> <span class="ot">::</span> (<span class="dt">Monad</span> <span class="fu">m</span>, <span class="dt">Foldable</span> <span class="fu">f</span>) <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="fu">f</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> ()</span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_G_</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">unfoldsM_BF_G_</span> <span class="fu">f</span> <span class="op">.</span> (<span class="op">:</span> [])</span></code></pre></div>
<p>Modifying this solution to create the output tree requires a little more thought.
We must keep hold of the intermediate list of <code>ts :: [TreeF a s]</code> to
reconstruct trees after the recursive call returns.</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldsM_BF_G</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> [<span class="fu">s</span>] <span class="ot">-&gt;</span> <span class="fu">m</span> [<span class="dt">Tree</span> <span class="fu">a</span>]</span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldsM_BF_G</span> <span class="fu">f</span> [] <span class="ot">=</span> <span class="fu">pure</span> []</span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- traverse, flatten, recurse, reconstruct</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldsM_BF_G</span> <span class="fu">f</span> <span class="fu">xs</span> <span class="ot">=</span> <span class="fu">traverse</span> <span class="fu">f</span> <span class="fu">xs</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="op">\</span><span class="fu">ts</span> <span class="ot">-&gt;</span></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">reconstruct</span> <span class="fu">ts</span> <span class="op">&lt;$&gt;</span> <span class="fu">unfoldsM_BF_G</span> <span class="fu">f</span> (<span class="fu">concatMap</span> <span class="fu">toList</span> <span class="fu">ts</span>)</span></code></pre></div>
<p>The reconstruction function picks a root in the first list and
completes it with subtrees from the second list:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="fu">reconstruct</span> <span class="ot">::</span> [<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>] <span class="ot">-&gt;</span> [<span class="dt">Tree</span> <span class="fu">a</span>] <span class="ot">-&gt;</span> [<span class="dt">Tree</span> <span class="fu">a</span>]</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a><span class="fu">reconstruct</span> (<span class="dt">LeafF</span> <span class="op">:</span> <span class="fu">ts</span>) <span class="fu">us</span> <span class="ot">=</span> <span class="dt">Leaf</span> <span class="op">:</span> <span class="fu">reconstruct</span> <span class="fu">ts</span> <span class="fu">us</span></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a><span class="fu">reconstruct</span> (<span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">_</span> <span class="fu">_</span> <span class="op">:</span> <span class="fu">ts</span>) (<span class="fu">l</span> <span class="op">:</span> <span class="fu">r</span> <span class="op">:</span> <span class="fu">us</span>) <span class="ot">=</span> <span class="dt">Node</span> <span class="fu">a</span> <span class="fu">l</span> <span class="fu">r</span> <span class="op">:</span> <span class="fu">reconstruct</span> <span class="fu">ts</span> <span class="fu">us</span></span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a><span class="fu">reconstruct</span> <span class="fu">_</span> <span class="fu">_</span> <span class="ot">=</span> <span class="fu">error</span> <span class="st">&quot;impossible&quot;</span></span></code></pre></div>
<p>You could modify the final branch to produce <code>[]</code>, but <code>error</code> makes it
explicit that this branch should never be reached by the unfold where it is
used.</p>
<p>The top-level unfold function wraps the seed in a singleton input list and
extracts the root from a singleton output list.</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_G</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_G</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">fmap</span> <span class="fu">head</span> <span class="op">.</span> <span class="fu">unfoldsM_BF_G</span> <span class="fu">f</span> <span class="op">.</span> (<span class="op">:</span> [])</span></code></pre></div>
<details class="code-details">
<summary>
Unit test <code>testGraphG</code>
</summary>
<div class="sourceCode" id="cb29"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraph_G</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="dt">Int</span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraph_G</span> <span class="ot">=</span> (<span class="ot">`evalState`</span> <span class="fu">Set.empty</span>) <span class="op">.</span> <span class="fu">unfoldM_BF_G</span> <span class="fu">visitGraph</span></span></code></pre></div>
<div class="sourceCode" id="cb30"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphG</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphG</span> <span class="ot">=</span> <span class="fu">testCase</span> <span class="st">&quot;Q-graph&quot;</span> <span class="op">$</span></span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">bfGraph_G</span> <span class="dv">1</span> <span class="op">@?</span><span class="ot">=</span></span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="dv">1</span></span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">2</span> <span class="dt">Leaf</span></span>
<span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a>              (<span class="dt">Node</span> <span class="dv">5</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span>
<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">4</span> <span class="dt">Leaf</span> (<span class="dt">Node</span> <span class="dv">6</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span></code></pre></div>
</details>
<p>This solution is less brittle than the queue-based solution because
we always traverse lists left-to-right.
To avoid the uses of <code>error</code> in <code>reconstruct</code>,
you can probably create a specialized data structure in place of <code>[TreeF a s]</code>,
but that is finicky in its own way.</p>
<h2 id="in-search-of-compositionality">In search of compositionality</h2>
<p>Both of the solutions above (the queue-based and the “monolithic” level-based unfolds)
stem from a <em>global</em> view of breadth-first walks: we are iterating on a list or a
queue which holds all the seeds from one or two levels at a time.
That structure represents a “front line” between visited and unvisited
vertices, and every iteration advances the front line a little: with a queue we
advance it one vertex at a time, with a list we advance the whole front line
in an inner loop—one call to <code>traverse</code>—before recursing.</p>
<p>The opposite <em>local</em> view of breadth-first order is exemplified by the earlier
<code>levels</code> function: it only produces a list of lists of the vertices
reachable from the current root. It does so recursively, by <em>composing</em>
together the vertices reachable from its children. Our goal here is to find a
similarly local, compositional implementation of breadth-first unfolds.</p>
<p>Rather than defining <code>unfoldM</code> directly, which sequences the computations on
all levels into a single computation, we will introduce an intermediate
function <code>weave</code> that keeps levels separate—just as <code>toListBF</code> is defined
using <code>levels</code>.
The result of <code>weave</code> will be in an as yet unknown applicative functor <code>F m</code>
depending on <code>m</code>.
And because levels are kept separate, <code>weave</code> only needs
a constraint <code>Applicative m</code> to compose computations on the same level.
The goal is to implement this signature, where the result type <code>F</code> is also an
unknown:</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weave</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">F</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span></code></pre></div>
<p>The name <code>weave</code> comes from visualizing a breadth-first walk
as a path zigzagging across a tree like this:</p>
<figure>
<img src="/images/breadth-first-weave.png" alt="Breadth-first path"></img>
</figure>
<p>which is reminiscent of weaving as in the making of textile:</p>
<figure>
<figcaption>
Illustration from <a href="https://en.wikipedia.org/wiki/Weaving">the Wikipedia article on Weaving</a>:
warp and weft.
</figcaption>
<img src="/images/warp-and-weft.jpeg" alt="Warp and weft illustration"></img>
</figure>
<p>With only what we know so far, a bit of type-directed programming leads to the
following incomplete definition. We have constructed something of type
<code>m (F m (Tree a))</code>, while we expect <code>F m (Tree a)</code>:</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weave</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">F</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a><span class="fu">weave</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">_</span> (<span class="fu">step</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">s</span>) <span class="kw">where</span></span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">step</span> <span class="ot">::</span> <span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">F</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">step</span> <span class="dt">LeafF</span> <span class="ot">=</span> <span class="fu">pure</span> <span class="dt">Leaf</span></span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">step</span> (<span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">l</span> <span class="fu">r</span>) <span class="ot">=</span> <span class="fu">liftA2</span> <span class="dt">NodeF</span> (<span class="fu">weave</span> <span class="fu">f</span> <span class="fu">l</span>) (<span class="fu">weave</span> <span class="fu">f</span> <span class="fu">r</span>)</span></code></pre></div>
<p>To fill the hole <code>_</code>, we postulate the following primitive, <code>weft</code>,
as part of the unknown definition of <code>F</code>:</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weft</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="fu">m</span> (<span class="dt">F</span> <span class="fu">m</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">F</span> <span class="fu">m</span> <span class="fu">a</span></span></code></pre></div>
<p>Intuitively, <code>F m</code> represents “multi-level computations”.
The <code>weft</code> function constructs a multi-level <code>(F m)</code>-computation from
one level of <code>m</code>-computation which returns the subsequent levels
as an <code>(F m)</code>-computation.</p>
<p>We fill the hole with <code>weft</code>, completing the definition of <code>weave</code>:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weave</span> <span class="ot">::</span> <span class="kw">forall</span> <span class="fu">m</span> <span class="fu">s</span> <span class="fu">a</span><span class="op">.</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">F</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a><span class="fu">weave</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">weft</span> (<span class="fu">weaveF</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">s</span>) <span class="kw">where</span></span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">weaveF</span> <span class="ot">::</span> <span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">F</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">weaveF</span> <span class="dt">LeafF</span> <span class="ot">=</span> <span class="fu">pure</span> <span class="dt">Leaf</span></span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">weaveF</span> (<span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">l</span> <span class="fu">r</span>) <span class="ot">=</span> <span class="fu">liftA2</span> (<span class="dt">Node</span> <span class="fu">a</span>) (<span class="fu">weave</span> <span class="fu">f</span> <span class="fu">l</span>) (<span class="fu">weave</span> <span class="fu">f</span> <span class="fu">r</span>)</span></code></pre></div>
<p>The function <code>weave</code> defines a multi-level computation which represents
a breadth-first walk from a seed <code>s</code>:</p>
<ul>
<li>the first level of the walk is <code>f s</code>, expanding the initial seed;</li>
<li>the auxiliary function <code>weaveF</code> constructs the remaining levels from
the initial seed’s expansion:
<ul>
<li>if the seed expands to <code>LeafF</code>, there are no more seeds,
and we terminate with an empty computation (<code>pure</code>);</li>
<li>if the seed expands to <code>NodeF</code>, we obtain two sub-seeds <code>l</code> and <code>r</code>,
they generate their own weaves recursively (<code>weave f l</code> and <code>weave f r</code>),
and we compose them (<code>liftA2</code>).</li>
</ul></li>
</ul>
<p>One way to think about <code>weft</code> is as a generalization of the following primitives:
we can “embed” <code>m</code>-computations into <code>F m</code>,
and we can “delay” multi-level <code>(F m)</code>-computations, shifting the
<code>m</code>-computation on each level to the next level.</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="fu">embed</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="fu">m</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">F</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a><span class="fu">embed</span> <span class="fu">u</span> <span class="ot">=</span> <span class="fu">weft</span> (<span class="fu">pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">u</span>)</span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a><span class="fu">delay</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">F</span> <span class="fu">m</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">F</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a><span class="fu">delay</span> <span class="fu">u</span> <span class="ot">=</span> <span class="fu">weft</span> (<span class="fu">pure</span> <span class="fu">u</span>)</span></code></pre></div>
<p>The key law relating these two operations is that embedded computations
and delayed computations commute with each other:</p>
<pre><code>embed u *&gt; delay v = delay v &lt;* embed u</code></pre>
<p>The <code>embed</code> and <code>delay</code> operations are provided by the <code>Phases</code> applicative
functor that I mentioned earlier, which enables breadth-first traversals,
but not breadth-first unfolds. Thus, <code>weft</code> is a strictly more expressive
primitive than <code>embed</code> and <code>delay</code>.</p>
<p>Eventually, we will run a multi-level computation as a single <code>m</code>-computation
so that we can use <code>weave</code> to define <code>unfoldM</code>. The runner function will be
called <code>mesh</code>:</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="fu">mesh</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">F</span> <span class="fu">m</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> <span class="fu">a</span></span></code></pre></div>
<p>It is characterized by this law which says that <code>mesh</code> executes the first
level of the computation <code>u :: m (F m a)</code>, then executes the remaining levels
recursively:</p>
<pre><code>mesh (weft u) = u &gt;&gt;= mesh</code></pre>
<p>Putting everything together, <code>weave</code> and <code>mesh</code> combine into a breadth-first unfold:</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">mesh</span> (<span class="fu">weave</span> <span class="fu">f</span> <span class="fu">s</span>)</span></code></pre></div>
<p>It remains to find an applicative functor <code>F</code> equipped with <code>weft</code> and <code>mesh</code>.</p>
<h2 id="the-weave-applicative">The weave applicative</h2>
<p>A basic approach to design a type is to make some of the operations it
should support into constructors. The weave applicative <code>WeaveS</code> has
constructors for <code>pure</code> and <code>weft</code>:</p>
<div class="sourceCode" id="cb40"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">EndS</span> <span class="fu">a</span></span>
<span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">WeftS</span> (<span class="fu">m</span> (<span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span>))</span></code></pre></div>
<p>(The suffix “S” stands for Spoilers. Read on!)</p>
<p>We instantiate the unknown functor <code>F</code> with <code>WeaveS</code>.</p>
<div class="sourceCode" id="cb41"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">F</span> <span class="ot">=</span> <span class="dt">WeaveS</span></span></code></pre></div>
<p>Astute readers will have recognized <code>WeaveS</code> as the <a href="https://hackage.haskell.org/package/free-5.2/docs/Control-Monad-Free.html#t:Free">free monad</a>.
Just as <code>Phases</code> has the same type definition as the free applicative functor but
a different <code>Applicative</code> instance, we will give <code>WeaveS</code> an <code>Applicative</code>
instance that does not coincide with the <code>Applicative</code> and <code>Monad</code> instances of
the free monad.</p>
<p>Starting with the easy functions,
<code>weft</code> is <code>WeftS</code>, and the equation for <code>mesh</code> above is basically its definition.
We just need to add an equation for <code>EndS</code>.</p>
<div class="sourceCode" id="cb42"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weft</span> <span class="ot">::</span> <span class="fu">m</span> (<span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a><span class="fu">weft</span> <span class="ot">=</span> <span class="dt">WeftS</span></span>
<span id="cb42-3"><a href="#cb42-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb42-4"><a href="#cb42-4" aria-hidden="true" tabindex="-1"></a><span class="fu">mesh</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb42-5"><a href="#cb42-5" aria-hidden="true" tabindex="-1"></a><span class="fu">mesh</span> (<span class="dt">EndS</span> <span class="fu">a</span>) <span class="ot">=</span> <span class="fu">pure</span> <span class="fu">a</span></span>
<span id="cb42-6"><a href="#cb42-6" aria-hidden="true" tabindex="-1"></a><span class="fu">mesh</span> (<span class="dt">WeftS</span> <span class="fu">u</span>) <span class="ot">=</span> <span class="fu">u</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="fu">mesh</span></span></code></pre></div>
<p>Recall that <code>WeaveS</code> represents multi-level computations.
Computations are composed level-wise with the following <code>liftS2</code>.
The interesting case is the one where both arguments are <code>WeftS</code>: we compose
the first level with <code>liftA2</code>, and the subsequent ones with <code>liftS2</code>
recursively.</p>
<div class="sourceCode" id="cb43"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a><span class="fu">liftS2</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">b</span> <span class="ot">-&gt;</span> <span class="fu">c</span>) <span class="ot">-&gt;</span> <span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">b</span> <span class="ot">-&gt;</span> <span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">c</span></span>
<span id="cb43-2"><a href="#cb43-2" aria-hidden="true" tabindex="-1"></a><span class="fu">liftS2</span> <span class="fu">f</span> (<span class="dt">EndS</span> <span class="fu">a</span>) <span class="fu">wb</span> <span class="ot">=</span> <span class="fu">f</span> <span class="fu">a</span> <span class="op">&lt;$&gt;</span> <span class="fu">wb</span></span>
<span id="cb43-3"><a href="#cb43-3" aria-hidden="true" tabindex="-1"></a><span class="fu">liftS2</span> <span class="fu">f</span> <span class="fu">wa</span> (<span class="dt">EndS</span> <span class="fu">b</span>) <span class="ot">=</span> <span class="fu">flip</span> <span class="fu">f</span> <span class="fu">b</span> <span class="op">&lt;$&gt;</span> <span class="fu">wa</span></span>
<span id="cb43-4"><a href="#cb43-4" aria-hidden="true" tabindex="-1"></a><span class="fu">liftS2</span> <span class="fu">f</span> (<span class="dt">WeftS</span> <span class="fu">wa</span>) (<span class="dt">WeftS</span> <span class="fu">wb</span>) <span class="ot">=</span> <span class="dt">WeftS</span> ((<span class="fu">liftA2</span> <span class="op">.</span> <span class="fu">liftS2</span>) <span class="fu">f</span> <span class="fu">wa</span> <span class="fu">wb</span>)</span></code></pre></div>
<p><code>liftS2</code> will be the <code>liftA2</code> in <code>WeaveS</code>’s <code>Applicative</code> instance.
The <code>Functor</code> and <code>Applicative</code> instances show that <code>WeaveS</code> is an
<em>applicative transformer</em>: for every applicative functor <code>m</code>,
<code>WeaveS m</code> is also an applicative functor.</p>
<div class="sourceCode" id="cb44"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Functor</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">Functor</span> (<span class="dt">WeaveS</span> <span class="fu">m</span>) <span class="kw">where</span></span>
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a>  <span class="fu">fmap</span> <span class="fu">f</span> (<span class="dt">EndS</span> <span class="fu">a</span>) <span class="ot">=</span> <span class="dt">EndS</span> (<span class="fu">f</span> <span class="fu">a</span>)</span>
<span id="cb44-3"><a href="#cb44-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">fmap</span> <span class="fu">f</span> (<span class="dt">WeftS</span> <span class="fu">wa</span>) <span class="ot">=</span> <span class="dt">WeftS</span> ((<span class="fu">fmap</span> <span class="op">.</span> <span class="fu">fmap</span>) <span class="fu">f</span> <span class="fu">wa</span>)</span>
<span id="cb44-4"><a href="#cb44-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb44-5"><a href="#cb44-5" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">Applicative</span> (<span class="dt">WeaveS</span> <span class="fu">m</span>) <span class="kw">where</span></span>
<span id="cb44-6"><a href="#cb44-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">pure</span> <span class="ot">=</span> <span class="dt">EndS</span></span>
<span id="cb44-7"><a href="#cb44-7" aria-hidden="true" tabindex="-1"></a>  <span class="fu">liftA2</span> <span class="ot">=</span> <span class="fu">liftS2</span></span></code></pre></div>
<p>That completes the definition of <code>unfoldM_BF</code>: a level-based, compositional
breadth-first unfold.</p>
<p>As a unit test, we copy the code for visiting a graph from earlier:</p>
<div class="sourceCode" id="cb45"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraphS</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="dt">Int</span></span>
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraphS</span> <span class="ot">=</span> (<span class="ot">`evalState`</span> <span class="fu">Set.empty</span>) <span class="op">.</span> <span class="fu">unfoldM_BF</span> <span class="fu">visitGraph</span></span></code></pre></div>
<div class="sourceCode" id="cb46"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphS</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphS</span> <span class="ot">=</span> <span class="fu">testCase</span> <span class="st">&quot;S-graph&quot;</span> <span class="op">$</span></span>
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">bfGraphS</span> <span class="dv">1</span> <span class="op">@?</span><span class="ot">=</span></span>
<span id="cb46-4"><a href="#cb46-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="dv">1</span></span>
<span id="cb46-5"><a href="#cb46-5" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">2</span> <span class="dt">Leaf</span></span>
<span id="cb46-6"><a href="#cb46-6" aria-hidden="true" tabindex="-1"></a>              (<span class="dt">Node</span> <span class="dv">5</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span>
<span id="cb46-7"><a href="#cb46-7" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">4</span> <span class="dt">Leaf</span> (<span class="dt">Node</span> <span class="dv">6</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span></code></pre></div>
<h3 id="code-golf">Code golf</h3>
<p>There is a variant of <code>weave</code> that I prefer:</p>
<div class="sourceCode" id="cb47"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weaveS</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">WeaveS</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>))</span>
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a><span class="fu">weaveS</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&lt;&amp;&gt;</span> <span class="op">\</span><span class="kw">case</span></span>
<span id="cb47-3"><a href="#cb47-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">LeafF</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="dt">Leaf</span></span>
<span id="cb47-4"><a href="#cb47-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">l</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="fu">liftA2</span> (<span class="dt">Node</span> <span class="fu">a</span>) (<span class="fu">weft</span> (<span class="fu">weaveS</span> <span class="fu">f</span> <span class="fu">l</span>)) (<span class="fu">weft</span> (<span class="fu">weaveS</span> <span class="fu">f</span> <span class="fu">r</span>))</span></code></pre></div>
<p>The outer <code>weft</code> constructor was moved into the recursive calls.
The result type has an extra <code>m</code>, which makes it more apparent that
we always start with a call to <code>f</code>. It’s the same vibe as replacing the type
<code>[a]</code> with <code>NonEmpty a</code> when we know that a list will always have at least one
element; <code>weaveS</code> always produces at least one level of computation.
We also replace <code>(&lt;$&gt;)</code> with its flipped version <code>(&lt;&amp;&gt;)</code> for aesthetic reasons:
we can apply it to a lambda without parentheses, and that change makes the
logic flow naturally from left to right: we first expand the seed <code>s</code> using
<code>f</code>, and continue depending on whether the expansion produced <code>LeafF</code> or <code>NodeF</code>.</p>
<p>To define <code>unfoldM</code>, instead of applying <code>mesh</code> directly, we chain it with
<code>(&gt;&gt;=)</code>.</p>
<div class="sourceCode" id="cb48"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_S</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_S</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">weaveS</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="fu">mesh</span></span></code></pre></div>
<h2 id="a-wrinkle-in-time">A wrinkle in time</h2>
<p>That solution is Obviously Correct™, but it has a terrible flaw:
it does not run in linear time!</p>
<p>We can demonstrate this by generating a “thin” tree whose height
is equal to its size.
The height <code>h</code> is the seed of the unfolding, and we generate a <code>NodeF</code> as long
as it is non-zero, asking for a decreased height <code>h - 1</code> on the right,
and a zero height on the left.</p>
<div class="sourceCode" id="cb49"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a><span class="fu">thinTreeS</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> ()</span>
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a><span class="fu">thinTreeS</span> <span class="ot">=</span> <span class="fu">runIdentity</span> <span class="op">.</span> <span class="fu">unfoldM_BF_S</span> <span class="fu">f</span></span>
<span id="cb49-3"><a href="#cb49-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb49-4"><a href="#cb49-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">f</span> <span class="dv">0</span> <span class="ot">=</span> <span class="fu">pure</span> <span class="dt">LeafF</span></span>
<span id="cb49-5"><a href="#cb49-5" aria-hidden="true" tabindex="-1"></a>    <span class="fu">f</span> <span class="fu">h</span> <span class="ot">=</span> <span class="fu">pure</span> (<span class="dt">NodeF</span> () <span class="dv">0</span> (<span class="fu">h</span> <span class="op">-</span> <span class="dv">1</span>))</span></code></pre></div>
<p>Compare the running times of evaluating <code>thinTreeS</code> at height 100
(the baseline)
and at height 1000 (10x the baseline).</p>
<div class="sourceCode" id="cb50"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a><span class="fu">benchS</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb50-2"><a href="#cb50-2" aria-hidden="true" tabindex="-1"></a><span class="fu">benchS</span> <span class="ot">=</span> <span class="fu">bgroup</span> <span class="st">&quot;S-thin&quot;</span></span>
<span id="cb50-3"><a href="#cb50-3" aria-hidden="true" tabindex="-1"></a>  [ <span class="fu">bench</span>  <span class="st">&quot;1x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeS</span> <span class="dv">100</span>)</span>
<span id="cb50-4"><a href="#cb50-4" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;10x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeS</span> <span class="dv">1000</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;S-thin.1x&quot;</span></span>
<span id="cb50-5"><a href="#cb50-5" aria-hidden="true" tabindex="-1"></a>  ]</span></code></pre></div>
<p>Benchmark output (relative):</p>
<table>
<thead>
<tr>
<th>height</th>
<th>time</th>
</tr>
</thead>
<tbody>
<tr>
<td>baseline</td>
<td>1x</td>
</tr>
<tr>
<td>10x</td>
<td>105x</td>
</tr>
</tbody>
</table>
<details>
<summary>
Raw output
</summary>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;S-thin&quot;
All
  S-thin
    1x:  OK
      27.6 μs ± 2.6 μs, 267 KB allocated, 317 B  copied, 6.0 MB peak memory
    10x: OK
      2.90 ms ± 181 μs,  23 MB allocated, 178 KB copied, 7.0 MB peak memory, 105.35x</code></pre>
</details>
<p>Multiplying the height by 10x makes the function run 100x slower.
Dramatically quadratic.</p>
<h3 id="complexity-analysis">Complexity analysis</h3>
<p>We can compare this implementation with <code>level</code> from earlier, which is linear-time.
In particular, looking at <code>zipLevels</code> with <code>liftS2</code>—which play similar
roles—there is a crucial difference when one of the arguments is empty
(<code>[]</code> or <code>EndS</code>):
<code>zipLevels</code> simply returns the other argument, whereas <code>liftS2</code> calls <code>(&lt;$&gt;)</code>,
continuing the recursion down the other argument.
So <code>zipLevels</code> stops working after reaching the end of either argument, whereas
<code>liftS2</code> walks to the end of both arguments. There is at least one
call to <code>liftS2</code> on every level which will walk to the bottom of the tree,
so we get a quadratic lower bound <span class="math inline"><em>Ω</em>(height<sup>2</sup>)</span>.</p>
<h2 id="out-of-sight-out-of-mind">Out of sight, out of mind</h2>
<p>The problematic combinators are <code>fmap</code> and <code>liftS2</code>, which <code>weaveS</code> uses to
construct the unfolded tree. If we don’t care about that tree—wanting only
the effect of a monadic unfold—then we can get rid of the complexity
associated with those combinators.</p>
<p>With no result to return, we remove the <code>a</code> type parameter from the definition
of <code>WeaveS</code>, yielding the <em>oblivious</em> (“O”) variant:</p>
<div class="sourceCode" id="cb52"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">WeaveO</span> <span class="fu">m</span></span>
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">EndO</span></span>
<span id="cb52-3"><a href="#cb52-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">WeftO</span> (<span class="fu">m</span> (<span class="dt">WeaveO</span> <span class="fu">m</span>))</span></code></pre></div>
<p>We rewrite <code>mesh</code> into <code>meshO</code>, reducing a <code>WeaveO m</code> computation
into <code>m ()</code> instead of <code>m a</code>.</p>
<div class="sourceCode" id="cb53"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb53-1"><a href="#cb53-1" aria-hidden="true" tabindex="-1"></a><span class="fu">meshO</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">WeaveO</span> <span class="fu">m</span> <span class="ot">-&gt;</span> <span class="fu">m</span> ()</span>
<span id="cb53-2"><a href="#cb53-2" aria-hidden="true" tabindex="-1"></a><span class="fu">meshO</span> <span class="dt">EndO</span> <span class="ot">=</span> <span class="fu">pure</span> ()</span>
<span id="cb53-3"><a href="#cb53-3" aria-hidden="true" tabindex="-1"></a><span class="fu">meshO</span> (<span class="dt">WeftO</span> <span class="fu">u</span>) <span class="ot">=</span> <span class="fu">u</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="fu">meshO</span></span></code></pre></div>
<p>The <code>Applicative</code> instance for <code>WeaveS</code> becomes a <code>Monoid</code> instance for <code>WeaveO</code>.
<code>liftA2</code> is replaced with <code>(&lt;&gt;)</code>, zipping two computations level-wise.</p>
<div class="sourceCode" id="cb54"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">Semigroup</span> (<span class="dt">WeaveO</span> <span class="fu">m</span>) <span class="kw">where</span></span>
<span id="cb54-2"><a href="#cb54-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">EndO</span> <span class="op">&lt;&gt;</span> <span class="fu">v</span> <span class="ot">=</span> <span class="fu">v</span></span>
<span id="cb54-3"><a href="#cb54-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">u</span> <span class="op">&lt;&gt;</span> <span class="dt">EndO</span> <span class="ot">=</span> <span class="fu">u</span></span>
<span id="cb54-4"><a href="#cb54-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">WeftO</span> <span class="fu">u</span> <span class="op">&lt;&gt;</span> <span class="dt">WeftO</span> <span class="fu">v</span> <span class="ot">=</span> <span class="dt">WeftO</span> (<span class="fu">liftA2</span> (<span class="op">&lt;&gt;</span>) <span class="fu">u</span> <span class="fu">v</span>)</span>
<span id="cb54-5"><a href="#cb54-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb54-6"><a href="#cb54-6" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">Monoid</span> (<span class="dt">WeaveO</span> <span class="fu">m</span>) <span class="kw">where</span></span>
<span id="cb54-7"><a href="#cb54-7" aria-hidden="true" tabindex="-1"></a>  <span class="fu">mempty</span> <span class="ot">=</span> <span class="dt">EndO</span></span>
<span id="cb54-8"><a href="#cb54-8" aria-hidden="true" tabindex="-1"></a>  <span class="fu">mappend</span> <span class="ot">=</span> (<span class="op">&lt;&gt;</span>)</span></code></pre></div>
<p>To implement a breadth-first walk, we modify <code>weaveS</code> above by replacing
<code>liftA2 (Node a)</code> with <code>(&lt;&gt;)</code>. Note that the type parameter <code>a</code> is no longer in
the result. It was only used in the tree that we decided to forget.</p>
<div class="sourceCode" id="cb55"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb55-1"><a href="#cb55-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weaveO</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">WeaveO</span> <span class="fu">m</span>)</span>
<span id="cb55-2"><a href="#cb55-2" aria-hidden="true" tabindex="-1"></a><span class="fu">weaveO</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&lt;&amp;&gt;</span> <span class="op">\</span><span class="kw">case</span></span>
<span id="cb55-3"><a href="#cb55-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">LeafF</span> <span class="ot">-&gt;</span> <span class="fu">mempty</span></span>
<span id="cb55-4"><a href="#cb55-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">NodeF</span> <span class="fu">_</span> <span class="fu">l</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">WeftO</span> (<span class="fu">weaveO</span> <span class="fu">f</span> <span class="fu">l</span>) <span class="op">&lt;&gt;</span> <span class="dt">WeftO</span> (<span class="fu">weaveO</span> <span class="fu">f</span> <span class="fu">r</span>)</span></code></pre></div>
<p>Running <code>weaveO</code> with <code>meshO</code> yields a <em>oblivious monadic unfold</em>:
it produces <code>m ()</code> instead of <code>m (Tree a)</code>.
(You may remember seeing another implementation of that same signature
just earlier, <code>unfoldM_BF_G_</code>.)</p>
<div class="sourceCode" id="cb56"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_O_</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> ()</span>
<span id="cb56-2"><a href="#cb56-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_O_</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">weaveO</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="fu">meshO</span></span></code></pre></div>
<p>Previously, we benchmarked the function <code>thinTreeS</code> that outputs a tree by
forcing the tree. With an oblivious unfold, there is no tree to force.
Instead we will count the number of generated <code>NodeF</code> constructors:</p>
<div class="sourceCode" id="cb57"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a><span class="fu">thinTreeO</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb57-2"><a href="#cb57-2" aria-hidden="true" tabindex="-1"></a><span class="fu">thinTreeO</span> <span class="ot">=</span> (<span class="ot">`execState`</span> <span class="dv">0</span>) <span class="op">.</span> <span class="fu">unfoldM_BF_O_</span> (<span class="fu">state</span> <span class="op">.</span> <span class="fu">f</span>)</span>
<span id="cb57-3"><a href="#cb57-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb57-4"><a href="#cb57-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">f</span> <span class="dv">0</span> <span class="fu">counter</span> <span class="ot">=</span> (<span class="dt">LeafF</span>, <span class="fu">counter</span>)</span>
<span id="cb57-5"><a href="#cb57-5" aria-hidden="true" tabindex="-1"></a>    <span class="fu">f</span> <span class="fu">h</span> <span class="fu">counter</span> <span class="ot">=</span> (<span class="dt">NodeF</span> () <span class="dv">0</span> (<span class="fu">h</span> <span class="op">-</span> <span class="dv">1</span>), <span class="fu">counter</span> <span class="op">+</span> <span class="dv">1</span>)  <span class="co">-- increment the counter for every NodeF</span></span></code></pre></div>
<p>We adapt the benchmark from before to measure the complexity of
unfolding thin trees. We have to increase the baseline height from 100 to 500
because this benchmark runs so much faster than the previous ones.</p>
<div class="sourceCode" id="cb58"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a><span class="fu">benchO</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a><span class="fu">benchO</span> <span class="ot">=</span> <span class="fu">bgroup</span> <span class="st">&quot;O-thin&quot;</span></span>
<span id="cb58-3"><a href="#cb58-3" aria-hidden="true" tabindex="-1"></a>  [ <span class="fu">bench</span>  <span class="st">&quot;1x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeO</span> <span class="dv">500</span>)</span>
<span id="cb58-4"><a href="#cb58-4" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;10x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeO</span> <span class="dv">5000</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;O-thin.1x&quot;</span></span>
<span id="cb58-5"><a href="#cb58-5" aria-hidden="true" tabindex="-1"></a>  ]</span></code></pre></div>
<p>Benchmark output (relative):</p>
<table>
<thead>
<tr>
<th>height</th>
<th>time</th>
</tr>
</thead>
<tbody>
<tr>
<td>baseline</td>
<td>1x</td>
</tr>
<tr>
<td>10x</td>
<td>9.8x</td>
</tr>
</tbody>
</table>
<details>
<summary>
Raw output
</summary>
<pre><code>$ cabal exec breadth-first-unfolds -- -p O-thin
All
  O-thin
    1x:  OK
      148  μs ± 8.3 μs, 543 KB allocated, 773 B  copied, 6.0 MB peak memory
    10x: OK
      1.45 ms ± 113 μs, 5.4 MB allocated,  82 KB copied, 7.0 MB peak memory, 9.78x</code></pre>
</details>
<p>The growth is linear, as desired:
the “10x” bench is 10x slower than the baseline “1x” bench.</p>
<h2 id="laziness-for-the-win">Laziness for the win</h2>
<p>The oblivious unfold avoided quadratic explosion by simplifying the problem.
Now let’s solve the original problem again,
so we can’t just get rid of <code>fmap</code> and <code>liftA2</code>.
As mentioned previously, the root cause was that (1) <code>liftA2</code> calls <code>fmap</code> when
one of the constructors is <code>EndS</code>, and (2) <code>fmap</code> traverses the other argument.
The next solution will be to make <code>fmap</code> take constant time,
by storing the “mapped function” in the constructor.
Behold the “L” variant of <code>WeaveS</code>, which is a GADT:</p>
<div class="sourceCode" id="cb60"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">WeaveL</span> <span class="fu">m</span> <span class="fu">a</span> <span class="kw">where</span></span>
<span id="cb60-2"><a href="#cb60-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">EndL</span> <span class="ot">::</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">WeaveL</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb60-3"><a href="#cb60-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">WeftL</span> <span class="ot">::</span> <span class="fu">m</span> (<span class="dt">WeaveL</span> <span class="fu">m</span> <span class="fu">b</span>) <span class="ot">-&gt;</span> (<span class="fu">b</span> <span class="ot">-&gt;</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">WeaveL</span> <span class="fu">m</span> <span class="fu">a</span></span></code></pre></div>
<p>For comparison, here is the previous “S” variant with GADT syntax:</p>
<div class="sourceCode" id="cb61"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span> <span class="kw">where</span></span>
<span id="cb61-2"><a href="#cb61-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">EndS</span> <span class="ot">::</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb61-3"><a href="#cb61-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">WeftS</span> <span class="ot">::</span> <span class="fu">m</span> (<span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">WeaveS</span> <span class="fu">m</span> <span class="fu">a</span></span></code></pre></div>
<p>This trick is also known as the <a href="https://hackage.haskell.org/package/kan-extensions-5.2.6/docs/Data-Functor-Coyoneda.html">“co-Yoneda construction”</a>.</p>
<p>The definition of <code>fmap</code> is no longer recursive.
It doesn’t even need <code>m</code> to be a functor anymore!</p>
<div class="sourceCode" id="cb62"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Functor</span> (<span class="dt">WeaveL</span> <span class="fu">m</span>) <span class="kw">where</span></span>
<span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a>  <span class="fu">fmap</span> <span class="fu">f</span> (<span class="dt">EndL</span> <span class="fu">a</span>) <span class="ot">=</span> <span class="dt">EndL</span> (<span class="fu">f</span> <span class="fu">a</span>)</span>
<span id="cb62-3"><a href="#cb62-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">fmap</span> <span class="fu">f</span> (<span class="dt">WeftL</span> <span class="fu">wa</span> <span class="fu">g</span>) <span class="ot">=</span> <span class="dt">WeftL</span> <span class="fu">wa</span> (<span class="fu">f</span> <span class="op">.</span> <span class="fu">g</span>)</span></code></pre></div>
<p>The <code>Applicative</code> instance is… a good exercise for the reader.
The details are not immediately important—we only care about improving <code>fmap</code>
for now—we will come back to have a look at the <code>Applicative</code> instance soon.</p>
<p>The runner function <code>meshL</code> is a simple bit of type Tetris.</p>
<div class="sourceCode" id="cb63"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a><span class="fu">meshL</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">WeaveL</span> <span class="fu">m</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb63-2"><a href="#cb63-2" aria-hidden="true" tabindex="-1"></a><span class="fu">meshL</span> (<span class="dt">EndL</span> <span class="fu">a</span>) <span class="ot">=</span> <span class="fu">pure</span> <span class="fu">a</span></span>
<span id="cb63-3"><a href="#cb63-3" aria-hidden="true" tabindex="-1"></a><span class="fu">meshL</span> (<span class="dt">WeftL</span> <span class="fu">wa</span> <span class="fu">f</span>) <span class="ot">=</span> <span class="fu">f</span> <span class="op">&lt;$&gt;</span> (<span class="fu">wa</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="fu">meshL</span>)</span></code></pre></div>
<p>By partially applying <code>WeftL</code> to <code>id</code> as its second argument,
we obtain a counterpart to the unary <code>WeftS</code> constructor:</p>
<div class="sourceCode" id="cb64"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb64-1"><a href="#cb64-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weftL</span> <span class="ot">::</span> <span class="fu">m</span> (<span class="dt">WeaveL</span> <span class="fu">m</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">WeaveL</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb64-2"><a href="#cb64-2" aria-hidden="true" tabindex="-1"></a><span class="fu">weftL</span> <span class="fu">wa</span> <span class="ot">=</span> <span class="dt">WeftL</span> <span class="fu">wa</span> <span class="fu">id</span></span></code></pre></div>
<p>With those primitives redefined, the “weave” and “unfold” are identical.
Below, we only renamed the “S” suffixes to “L”:</p>
<div class="sourceCode" id="cb65"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb65-1"><a href="#cb65-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weaveL</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">WeaveL</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>))</span>
<span id="cb65-2"><a href="#cb65-2" aria-hidden="true" tabindex="-1"></a><span class="fu">weaveL</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&lt;&amp;&gt;</span> <span class="op">\</span><span class="kw">case</span></span>
<span id="cb65-3"><a href="#cb65-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">LeafF</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="dt">Leaf</span></span>
<span id="cb65-4"><a href="#cb65-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">s1</span> <span class="fu">s2</span> <span class="ot">-&gt;</span> <span class="fu">liftA2</span> (<span class="dt">Node</span> <span class="fu">a</span>) (<span class="fu">weftL</span> (<span class="fu">weaveL</span> <span class="fu">f</span> <span class="fu">s1</span>)) (<span class="fu">weftL</span> (<span class="fu">weaveL</span> <span class="fu">f</span> <span class="fu">s2</span>))</span>
<span id="cb65-5"><a href="#cb65-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb65-6"><a href="#cb65-6" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_L</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb65-7"><a href="#cb65-7" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_L</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">weaveL</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="fu">meshL</span></span></code></pre></div>
<p>The benchmarks show that 10x the height takes 10x the time.
Linear growth again.</p>
<details class="code-details">
<summary>
Benchmark code and output: <code>thinTreeL</code> and <code>benchL</code>
</summary>
<p>Copy of the <code>benchS</code> benchmark.</p>
<div class="sourceCode" id="cb66"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb66-1"><a href="#cb66-1" aria-hidden="true" tabindex="-1"></a><span class="fu">thinTreeL</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> ()</span>
<span id="cb66-2"><a href="#cb66-2" aria-hidden="true" tabindex="-1"></a><span class="fu">thinTreeL</span> <span class="ot">=</span> <span class="fu">runIdentity</span> <span class="op">.</span> <span class="fu">unfoldM_BF_L</span> <span class="fu">f</span></span>
<span id="cb66-3"><a href="#cb66-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb66-4"><a href="#cb66-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">f</span> <span class="dv">0</span> <span class="ot">=</span> <span class="fu">pure</span> <span class="dt">LeafF</span></span>
<span id="cb66-5"><a href="#cb66-5" aria-hidden="true" tabindex="-1"></a>    <span class="fu">f</span> <span class="fu">h</span> <span class="ot">=</span> <span class="fu">pure</span> (<span class="dt">NodeF</span> () <span class="dv">0</span> (<span class="fu">h</span> <span class="op">-</span> <span class="dv">1</span>))</span>
<span id="cb66-6"><a href="#cb66-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb66-7"><a href="#cb66-7" aria-hidden="true" tabindex="-1"></a><span class="fu">benchL</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb66-8"><a href="#cb66-8" aria-hidden="true" tabindex="-1"></a><span class="fu">benchL</span> <span class="ot">=</span> <span class="fu">bgroup</span> <span class="st">&quot;L-thin&quot;</span></span>
<span id="cb66-9"><a href="#cb66-9" aria-hidden="true" tabindex="-1"></a>  [ <span class="fu">bench</span>  <span class="st">&quot;1x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeL</span>  <span class="dv">100</span>)</span>
<span id="cb66-10"><a href="#cb66-10" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;10x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeL</span> <span class="dv">1000</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;L-thin.1x&quot;</span></span>
<span id="cb66-11"><a href="#cb66-11" aria-hidden="true" tabindex="-1"></a>  ]</span></code></pre></div>
<p>Benchmark output (relative):</p>
<table>
<thead>
<tr>
<th>height</th>
<th>time</th>
</tr>
</thead>
<tbody>
<tr>
<td>baseline</td>
<td>1x</td>
</tr>
<tr>
<td>10x</td>
<td>9.93x</td>
</tr>
</tbody>
</table>
<p>Raw output:</p>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;L-thin&quot;     
All
  L-thin
    1x:  OK
      14.1 μs ± 782 ns,  59 KB allocated,   5 B  copied, 6.0 MB peak memory
    10x: OK
      140  μs ±  13 μs, 586 KB allocated,  51 B  copied, 6.0 MB peak memory, 9.93x</code></pre>
</details>
<h3 id="lazy-in-more-ways-than-one">Lazy in more ways than one</h3>
<p>As hinted by the “L” and “S” suffixes,
<code>WeaveL</code> is a “lazy” variant of <code>WeaveS</code>: <code>fmap</code> for <code>WeaveL</code> “postpones”
work by accumulating functions in the <code>WeftL</code> constructor.
That work is “forced” by <code>meshL</code>, which is where the <code>fmap</code> (<code>(&lt;$&gt;)</code>) of the
underlying monad <code>m</code> is called, performing the work accumulated
by possibly many calls to <code>WeaveL</code>’s <code>fmap</code>.</p>
<p>One subtlety is that there are multiple “lazinesses” at play.
The main benefit of using <code>WeaveL</code> is really to delay computation,
that is a kind of laziness, but <code>WeaveL</code> doesn’t need to be
implemented in a lazy language.
We can rewrite all of the code we’ve seen so far in a strict language
with minor changes, and we will still observe the quadratic vs linear behavior
of <code>WeaveS</code> vs <code>WeaveL</code> on thin trees.
The “manufactured laziness” of <code>WeaveL</code> is a concept independent of the
“ambient laziness” in Haskell.</p>
<p>Nevertheless, we can still find an interesting role for that “ambient laziness”
in this story. Indeed, the function <code>weaveL</code> also happens to be lazier than
<code>weaveS</code> in the usual sense.</p>
<p>A concrete test case is worth a thousand words. Consider the following
tree generator which keeps unfolding left subtrees while making
every right subtree <code>undefined</code>:</p>
<div class="sourceCode" id="cb68"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb68-1"><a href="#cb68-1" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeF</span> <span class="ot">::</span> <span class="dt">Bool</span> <span class="ot">-&gt;</span> <span class="dt">TreeF</span> () <span class="dt">Bool</span></span>
<span id="cb68-2"><a href="#cb68-2" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeF</span> <span class="dt">True</span> <span class="ot">=</span> <span class="dt">NodeF</span> () <span class="dt">True</span> <span class="dt">False</span></span>
<span id="cb68-3"><a href="#cb68-3" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeF</span> <span class="dt">False</span> <span class="ot">=</span> <span class="fu">undefined</span></span></code></pre></div>
<p>If we used the pure <code>unfold</code>, we would get the same tree as this
recursive definition:</p>
<div class="sourceCode" id="cb69"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb69-1"><a href="#cb69-1" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTree</span> <span class="ot">::</span> <span class="dt">Tree</span> ()</span>
<span id="cb69-2"><a href="#cb69-2" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTree</span> <span class="ot">=</span> <span class="dt">Node</span> () <span class="fu">partialTree</span> <span class="fu">undefined</span></span></code></pre></div>
<p>What happens if we use one of the monadic unfolds? For example
<code>unfoldM_BF_S</code>:</p>
<div class="sourceCode" id="cb70"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb70-1"><a href="#cb70-1" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeS</span> <span class="ot">::</span> <span class="dt">Tree</span> ()</span>
<span id="cb70-2"><a href="#cb70-2" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeS</span> <span class="ot">=</span> <span class="fu">runIdentity</span> (<span class="fu">unfoldM_BF_S</span> (<span class="dt">Identity</span> <span class="op">.</span> <span class="fu">partialTreeF</span>) <span class="dt">True</span>)</span></code></pre></div>
<p>Try to force the first <code>Node</code> constructor.</p>
<div class="sourceCode" id="cb71"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb71-1"><a href="#cb71-1" aria-hidden="true" tabindex="-1"></a><span class="fu">whnfTreeS</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb71-2"><a href="#cb71-2" aria-hidden="true" tabindex="-1"></a><span class="fu">whnfTreeS</span> <span class="ot">=</span> <span class="fu">expectFail</span> <span class="op">$</span> <span class="fu">testCase</span> <span class="st">&quot;S-whnf&quot;</span> <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb71-3"><a href="#cb71-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">case</span> <span class="fu">partialTreeS</span> <span class="kw">of</span></span>
<span id="cb71-4"><a href="#cb71-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="fu">_</span> <span class="fu">_</span> <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> ()  <span class="co">-- Succeed</span></span>
<span id="cb71-5"><a href="#cb71-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Leaf</span> <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="st">&quot;unreachable&quot;</span> <span class="co">-- definitely not a Leaf</span></span></code></pre></div>
<p>As it turns out, this test using the “S” variant fails. (That’s
why the test is marked with <code>expectFail</code>.)
Forcing <code>partialTreeS</code> evaluates the <code>undefined</code> in <code>partialTreeF</code>.
Therefore <code>partialTreeS</code> is not equivalent to <code>partialTree</code>.</p>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;S-whnf&quot;
All
  S-whnf: FAIL (expected)
    Exception: Prelude.undefined
    CallStack ...</code></pre>
<p>In contrast, the “L” variant makes that same test succeed.</p>
<div class="sourceCode" id="cb73"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb73-1"><a href="#cb73-1" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeL</span> <span class="ot">::</span> <span class="dt">Tree</span> ()</span>
<span id="cb73-2"><a href="#cb73-2" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeL</span> <span class="ot">=</span> <span class="fu">runIdentity</span> (<span class="fu">unfoldM_BF_L</span> (<span class="dt">Identity</span> <span class="op">.</span> <span class="fu">partialTreeF</span>) <span class="dt">True</span>)</span>
<span id="cb73-3"><a href="#cb73-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb73-4"><a href="#cb73-4" aria-hidden="true" tabindex="-1"></a><span class="fu">whnfTreeL</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb73-5"><a href="#cb73-5" aria-hidden="true" tabindex="-1"></a><span class="fu">whnfTreeL</span> <span class="ot">=</span> <span class="fu">testCase</span> <span class="st">&quot;L-whnf&quot;</span> <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb73-6"><a href="#cb73-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">case</span> <span class="fu">partialTreeL</span> <span class="kw">of</span></span>
<span id="cb73-7"><a href="#cb73-7" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="fu">_</span> <span class="fu">_</span> <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> ()  <span class="co">-- Succeed</span></span>
<span id="cb73-8"><a href="#cb73-8" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Leaf</span> <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="st">&quot;unreachable&quot;</span></span></code></pre></div>
<p>Test output:</p>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;L-whnf&quot;
All
  L-whnf: OK</code></pre>
<p>This difference can only be seen with “lazy monads”, where <code>(&gt;&gt;=)</code> is
lazy in its first argument.
(If this definition sounds not quite right, that’s probably because of <code>seq</code>.
It makes a precise definition of “lazy monad” more complicated.)
Examples of lazy monads from <a href="https://hackage.haskell.org/package/transformers">the <em>transformers</em> library</a>
are <code>Identity</code>, <code>Reader</code>, lazy <code>State</code>, lazy <code>Writer</code>, and <code>Accum</code>.</p>
<p>The secret sauce is the definition of <code>liftA2</code> for <code>WeaveL</code>:</p>
<div class="sourceCode" id="cb75"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb75-1"><a href="#cb75-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">Applicative</span> (<span class="dt">WeaveL</span> <span class="fu">m</span>) <span class="kw">where</span></span>
<span id="cb75-2"><a href="#cb75-2" aria-hidden="true" tabindex="-1"></a>  <span class="fu">pure</span> <span class="ot">=</span> <span class="dt">EndL</span></span>
<span id="cb75-3"><a href="#cb75-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">liftA2</span> <span class="fu">f</span> (<span class="dt">EndL</span> <span class="fu">a</span>) <span class="fu">wb</span> <span class="ot">=</span> <span class="fu">f</span> <span class="fu">a</span> <span class="op">&lt;$&gt;</span> <span class="fu">wb</span></span>
<span id="cb75-4"><a href="#cb75-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">liftA2</span> <span class="fu">f</span> <span class="fu">wa</span> (<span class="dt">EndL</span> <span class="fu">b</span>) <span class="ot">=</span> <span class="fu">flip</span> <span class="fu">f</span> <span class="fu">b</span> <span class="op">&lt;$&gt;</span> <span class="fu">wa</span></span>
<span id="cb75-5"><a href="#cb75-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">liftA2</span> <span class="fu">f</span> (<span class="dt">WeftL</span> <span class="fu">wa</span> <span class="fu">g</span>) (<span class="dt">WeftL</span> <span class="fu">wb</span> <span class="fu">h</span>)</span>
<span id="cb75-6"><a href="#cb75-6" aria-hidden="true" tabindex="-1"></a>    <span class="ot">=</span> <span class="dt">WeftL</span> ((<span class="fu">liftA2</span> <span class="op">.</span> <span class="fu">liftA2</span>) (,) <span class="fu">wa</span> <span class="fu">wb</span>) (<span class="op">\</span> <span class="op">~</span>(<span class="fu">a</span>, <span class="fu">b</span>) <span class="ot">-&gt;</span> <span class="fu">f</span> (<span class="fu">g</span> <span class="fu">a</span>) (<span class="fu">h</span> <span class="fu">b</span>))</span></code></pre></div>
<p>In the third clause of <code>liftA2</code>, we put the function <code>f</code> in a lambda with a
lazy pattern (<code>~(a, b)</code>) directly under the topmost constructor <code>WeftL</code>.
Thus, we can access the result of <code>f</code> from the second field of <code>WeftL</code>
without looking at the first field. In <code>liftS2</code> earlier, <code>f</code> was
passed as an argument to <code>(liftA2 . liftS2)</code>, that forces us to run the
computation before we can get a hold on the result of <code>f</code>.</p>
<h4 id="maximizing-laziness">Maximizing laziness</h4>
<p>The “L” variant of <code>unfoldM</code> is lazier than the “S” variant,
but there is still a gap between <code>partialTreeL</code> and the pure <code>partialTree</code>:
if we force not only the root, but also the left subtree of <code>partialTreeL</code>,
then we run into <code>undefined</code> again.</p>
<div class="sourceCode" id="cb76"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb76-1"><a href="#cb76-1" aria-hidden="true" tabindex="-1"></a><span class="fu">forceLeftTreeL</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb76-2"><a href="#cb76-2" aria-hidden="true" tabindex="-1"></a><span class="fu">forceLeftTreeL</span> <span class="ot">=</span> <span class="fu">expectFail</span> <span class="op">$</span> <span class="fu">testCase</span> <span class="st">&quot;L-left&quot;</span> <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb76-3"><a href="#cb76-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">case</span> <span class="fu">partialTreeL</span> <span class="kw">of</span></span>
<span id="cb76-4"><a href="#cb76-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="fu">_</span> (<span class="dt">Node</span> <span class="fu">_</span> <span class="fu">_</span> <span class="fu">_</span>) <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> ()  <span class="co">-- Succeed</span></span>
<span id="cb76-5"><a href="#cb76-5" aria-hidden="true" tabindex="-1"></a>    <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="st">&quot;unreachable&quot;</span></span></code></pre></div>
<p>Test output:</p>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;L-left&quot; 
All
  L-left: FAIL (expected)
    Exception: Prelude.undefined</code></pre>
<p>Although the unfold using <code>WeaveL</code> is lazier than using <code>WeaveS</code>,
it is not yet as lazy as it could be.
The reason is that, strictly speaking, <code>WeaveL</code>’s <code>liftA2</code> is a strict function.
The expansion function <code>partialTreeF</code> produces a level with an <code>undefined</code>
sub-computation, which crashes the whole level.
Each level in a computation will be either completely defined or undefined.</p>
<p>To recap, we’ve been looking at the following trees:</p>
<pre><code>partialTreeS = undefined
partialTreeL = Node () undefined undefined
partialTree  = Node () partialTree undefined</code></pre>
<p>It is natural to ask: can we define a breadth-first unfold that, when applied
to <code>partialTreeF</code>, will yield the same tree as <code>partialTree</code>?</p>
<p>More generally, the new problem is to define a breadth-first <code>unfoldM</code>
whose specialization with the <code>Identity</code> functor is equivalent to
the pure <code>unfold</code> even on partially-defined values. That is, it satisfies
the following equation:</p>
<pre><code>unfold f = runIdentity . unfoldM (Identity . f)</code></pre>
<h2 id="laziness-without-end">Laziness without end</h2>
<p>The strictness of <code>liftA2</code> is caused by <code>WeaveL</code> having two constructors.
Let’s get rid of <code>EndL</code>.</p>
<div class="sourceCode" id="cb80"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb80-1"><a href="#cb80-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">WeaveE</span> <span class="fu">m</span> <span class="fu">a</span> <span class="kw">where</span></span>
<span id="cb80-2"><a href="#cb80-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">WeftE</span> <span class="ot">::</span> <span class="fu">m</span> (<span class="dt">WeaveE</span> <span class="fu">m</span> <span class="fu">b</span>) <span class="ot">-&gt;</span> (<span class="fu">b</span> <span class="ot">-&gt;</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">WeaveE</span> <span class="fu">m</span> <span class="fu">a</span></span></code></pre></div>
<p>Having only one constructor lets us use lazy patterns:</p>
<div class="sourceCode" id="cb81"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb81-1"><a href="#cb81-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Functor</span> (<span class="dt">WeaveE</span> <span class="fu">m</span>) <span class="kw">where</span></span>
<span id="cb81-2"><a href="#cb81-2" aria-hidden="true" tabindex="-1"></a>  <span class="fu">fmap</span> <span class="fu">f</span> <span class="op">~</span>(<span class="dt">WeftE</span> <span class="fu">wa</span> <span class="fu">g</span>) <span class="ot">=</span> <span class="dt">WeftE</span> <span class="fu">wa</span> (<span class="fu">f</span> <span class="op">.</span> <span class="fu">g</span>)</span></code></pre></div>
<p>Wait a second. I spoke too fast, GHC gives us an error:</p>
<pre><code>error: [GHC-87005]
    • An existential or GADT data constructor cannot be used
        inside a lazy (~) pattern
    • In the pattern: WeftE wa g
      In the pattern: ~(WeftE wa g)
      In an equation for ‘fmap’: fmap f ~(WeftE wa g) = WeftE wa (f . g)
    |
641 | &gt;   fmap f ~(WeftE wa g) = WeftE wa (f . g)
    |              ^^^^^^^^^^</code></pre>
<p>The feature we need is “first-class existentials”,
for which there is an <a href="https://github.com/ghc-proposals/ghc-proposals/pull/473">open GHC proposal</a>.</p>
<p>Not letting that stop us, there is a simple version of first-class existentials
available in the package <em>some</em>,
as the module <a href="https://hackage.haskell.org/package/some-1.0.6/docs/Data-Some-Newtype.html"><code>Data.Some.Newtype</code></a> (internally using <code>unsafeCoerce</code>).
That will be sufficient for our purposes.
All we need is an abstract type <code>Some</code> and a pattern synonym:</p>
<div class="sourceCode" id="cb83"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb83-1"><a href="#cb83-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- imported from Data.Some.Newtype</span></span>
<span id="cb83-2"><a href="#cb83-2" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Some</span> <span class="fu">f</span></span>
<span id="cb83-3"><a href="#cb83-3" aria-hidden="true" tabindex="-1"></a><span class="fu">pattern</span> <span class="dt">Some</span> <span class="ot">::</span> <span class="fu">f</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Some</span> <span class="fu">f</span></span></code></pre></div>
<p>And we’re back on track. Here comes the actual “E” (endless) variant:</p>
<div class="sourceCode" id="cb84"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb84-1"><a href="#cb84-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">WeaveE</span> <span class="fu">m</span> <span class="fu">a</span> <span class="ot">=</span> <span class="dt">MkWeaveE</span> (<span class="dt">Some</span> (<span class="dt">WeavingE</span> <span class="fu">m</span> <span class="fu">a</span>))</span>
<span id="cb84-2"><a href="#cb84-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb84-3"><a href="#cb84-3" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">WeavingE</span> <span class="fu">m</span> <span class="fu">a</span> <span class="fu">b</span> <span class="kw">where</span></span>
<span id="cb84-4"><a href="#cb84-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">WeftE</span> <span class="ot">::</span> <span class="fu">m</span> (<span class="dt">WeaveE</span> <span class="fu">m</span> <span class="fu">b</span>) <span class="ot">-&gt;</span> (<span class="fu">b</span> <span class="ot">-&gt;</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">WeavingE</span> <span class="fu">m</span> <span class="fu">a</span> <span class="fu">b</span></span></code></pre></div>
<p>I spare you the details.</p>
<details class="code-details">
<summary>
<code>Functor</code>, <code>Applicative</code>, <code>weftE</code>, <code>meshE</code>
</summary>
<div class="sourceCode" id="cb85"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb85-1"><a href="#cb85-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Functor</span> (<span class="dt">WeaveE</span> <span class="fu">m</span>) <span class="kw">where</span></span>
<span id="cb85-2"><a href="#cb85-2" aria-hidden="true" tabindex="-1"></a>  <span class="fu">fmap</span> <span class="fu">f</span> (<span class="dt">MkWeaveE</span> (<span class="dt">Some</span> <span class="op">~</span>(<span class="dt">WeftE</span> <span class="fu">u</span> <span class="fu">g</span>))) <span class="ot">=</span> <span class="dt">MkWeaveE</span> (<span class="dt">Some</span> (<span class="dt">WeftE</span> <span class="fu">u</span> (<span class="fu">f</span> <span class="op">.</span> <span class="fu">g</span>)))</span>
<span id="cb85-3"><a href="#cb85-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb85-4"><a href="#cb85-4" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">Applicative</span> (<span class="dt">WeaveE</span> <span class="fu">m</span>) <span class="kw">where</span></span>
<span id="cb85-5"><a href="#cb85-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">pure</span> <span class="fu">x</span> <span class="ot">=</span> <span class="dt">MkWeaveE</span> (<span class="dt">Some</span> (<span class="dt">WeftE</span> (<span class="fu">pure</span> (<span class="fu">pure</span> ())) (<span class="op">\</span><span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">x</span>)))</span>
<span id="cb85-6"><a href="#cb85-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">liftA2</span> <span class="fu">f</span> (<span class="dt">MkWeaveE</span> (<span class="dt">Some</span> <span class="op">~</span>(<span class="dt">WeftE</span> <span class="fu">u</span> <span class="fu">g</span>))) (<span class="dt">MkWeaveE</span> (<span class="dt">Some</span> <span class="op">~</span>(<span class="dt">WeftE</span> <span class="fu">v</span> <span class="fu">h</span>)))</span>
<span id="cb85-7"><a href="#cb85-7" aria-hidden="true" tabindex="-1"></a>    <span class="ot">=</span> <span class="dt">MkWeaveE</span> (<span class="dt">Some</span> (<span class="dt">WeftE</span> ((<span class="fu">liftA2</span> <span class="op">.</span> <span class="fu">liftA2</span>) (,) <span class="fu">u</span> <span class="fu">v</span>) (<span class="op">\</span> <span class="op">~</span>(<span class="fu">x</span>, <span class="fu">y</span>) <span class="ot">-&gt;</span> <span class="fu">f</span> (<span class="fu">g</span> <span class="fu">x</span>) (<span class="fu">h</span> <span class="fu">y</span>))))</span>
<span id="cb85-8"><a href="#cb85-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb85-9"><a href="#cb85-9" aria-hidden="true" tabindex="-1"></a><span class="fu">weftE</span> <span class="ot">::</span> <span class="fu">m</span> (<span class="dt">WeaveE</span> <span class="fu">m</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">WeaveE</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb85-10"><a href="#cb85-10" aria-hidden="true" tabindex="-1"></a><span class="fu">weftE</span> <span class="fu">u</span> <span class="ot">=</span> <span class="dt">MkWeaveE</span> (<span class="dt">Some</span> (<span class="dt">WeftE</span> <span class="fu">u</span> <span class="fu">id</span>))</span>
<span id="cb85-11"><a href="#cb85-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb85-12"><a href="#cb85-12" aria-hidden="true" tabindex="-1"></a><span class="fu">meshE</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">WeaveE</span> <span class="fu">m</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">m</span> <span class="fu">a</span></span>
<span id="cb85-13"><a href="#cb85-13" aria-hidden="true" tabindex="-1"></a><span class="fu">meshE</span> (<span class="dt">MkWeaveE</span> (<span class="dt">Some</span> (<span class="dt">WeftE</span> <span class="fu">u</span> <span class="fu">f</span>))) <span class="ot">=</span> <span class="fu">f</span> <span class="op">&lt;$&gt;</span> (<span class="fu">u</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="fu">meshE</span>)</span></code></pre></div>
</details>
<details class="code-details">
<summary>
Breadth-first unfold, “E” variant: <code>weaveE</code> and <code>unfoldM_BF_E</code>
</summary>
<div class="sourceCode" id="cb86"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb86-1"><a href="#cb86-1" aria-hidden="true" tabindex="-1"></a><span class="fu">weaveE</span> <span class="ot">::</span> <span class="dt">Applicative</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">WeaveE</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>))</span>
<span id="cb86-2"><a href="#cb86-2" aria-hidden="true" tabindex="-1"></a><span class="fu">weaveE</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&lt;&amp;&gt;</span> <span class="op">\</span><span class="kw">case</span></span>
<span id="cb86-3"><a href="#cb86-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">LeafF</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="dt">Leaf</span></span>
<span id="cb86-4"><a href="#cb86-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">NodeF</span> <span class="fu">a</span> <span class="fu">s1</span> <span class="fu">s2</span> <span class="ot">-&gt;</span> <span class="fu">liftA2</span> (<span class="dt">Node</span> <span class="fu">a</span>) (<span class="fu">weftE</span> (<span class="fu">weaveE</span> <span class="fu">f</span> <span class="fu">s1</span>)) (<span class="fu">weftE</span> (<span class="fu">weaveE</span> <span class="fu">f</span> <span class="fu">s2</span>))</span>
<span id="cb86-5"><a href="#cb86-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb86-6"><a href="#cb86-6" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_E</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">Tree</span> <span class="fu">a</span>)</span>
<span id="cb86-7"><a href="#cb86-7" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_E</span> <span class="fu">f</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">weaveE</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="fu">meshE</span></span></code></pre></div>
</details>
<p>The endless <code>WeaveE</code> enables an even lazier implementation of <code>unfoldM</code>.
When specialized to the identity monad, it lets us force the resulting
tree in any order. The <code>forceLeftTreeE</code> test passes (unlike <code>forceLeftTreeL</code>).</p>
<div class="sourceCode" id="cb87"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb87-1"><a href="#cb87-1" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeE</span> <span class="ot">::</span> <span class="dt">Tree</span> ()</span>
<span id="cb87-2"><a href="#cb87-2" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeE</span> <span class="ot">=</span> <span class="fu">runIdentity</span> (<span class="fu">unfoldM_BF_E</span> (<span class="dt">Identity</span> <span class="op">.</span> <span class="fu">partialTreeF</span>) <span class="dt">True</span>)</span>
<span id="cb87-3"><a href="#cb87-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb87-4"><a href="#cb87-4" aria-hidden="true" tabindex="-1"></a><span class="fu">forceLeftTreeE</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb87-5"><a href="#cb87-5" aria-hidden="true" tabindex="-1"></a><span class="fu">forceLeftTreeE</span> <span class="ot">=</span> <span class="fu">testCase</span> <span class="st">&quot;E-left&quot;</span> <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb87-6"><a href="#cb87-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">case</span> <span class="fu">partialTreeE</span> <span class="kw">of</span></span>
<span id="cb87-7"><a href="#cb87-7" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="fu">_</span> (<span class="dt">Node</span> <span class="fu">_</span> <span class="fu">_</span> <span class="fu">_</span>) <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> ()  <span class="co">-- Succeed</span></span>
<span id="cb87-8"><a href="#cb87-8" aria-hidden="true" tabindex="-1"></a>    <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="st">&quot;unreachable&quot;</span></span></code></pre></div>
<p>Test output:</p>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;E-left&quot;
All
  E-left: OK</code></pre>
<p>One can also check that forcing the left spine of <code>partialTreeE</code>
arbitrarily deep throws no errors.</p>
<p>We made it lazy, but at what cost?
First, this “Endless” variant only works for lazy monads.
With a strict monad, the runner <code>meshE</code> will loop forever.
It is possible to run things more incrementally by pattern-matching on
<code>WeaveE</code>, but you’re better off using the oblivious <code>WeaveO</code> anyway.</p>
<p>Second, when you aren’t running into an unproductive loop, the “Endless” variant of
<code>unfoldM</code> has quadratic time complexity <span class="math inline"><em>Ω</em>(height<sup>2</sup>)</span>. The reason
is essentially the same as the “Strict” variant: <code>liftA2</code> keeps looping even if
one argument is a <code>pure</code> weave—before, that was to traverse the other
non-<code>pure</code> argument, now, there isn’t even a way to tell when the computation
has ended.
Thus, every leaf may create work proportional to the height of the tree.</p>
<p>Running the same benchmark as before, we measure even more baffling timings:</p>
<table>
<thead>
<tr>
<th>height</th>
<th>time</th>
</tr>
</thead>
<tbody>
<tr>
<td>baseline</td>
<td>1x</td>
</tr>
<tr>
<td>10x</td>
<td>738x</td>
</tr>
</tbody>
</table>
<details class="code-details">
<summary>
Benchmark: <code>thinTreeE</code> and <code>benchE</code>
</summary>
<div class="sourceCode" id="cb89"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb89-1"><a href="#cb89-1" aria-hidden="true" tabindex="-1"></a><span class="fu">thinTreeE</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> ()</span>
<span id="cb89-2"><a href="#cb89-2" aria-hidden="true" tabindex="-1"></a><span class="fu">thinTreeE</span> <span class="ot">=</span> <span class="fu">runIdentity</span> <span class="op">.</span> <span class="fu">unfoldM_BF_E</span> <span class="fu">f</span></span>
<span id="cb89-3"><a href="#cb89-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb89-4"><a href="#cb89-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">f</span> <span class="dv">0</span> <span class="ot">=</span> <span class="fu">pure</span> <span class="dt">LeafF</span></span>
<span id="cb89-5"><a href="#cb89-5" aria-hidden="true" tabindex="-1"></a>    <span class="fu">f</span> <span class="fu">h</span> <span class="ot">=</span> <span class="fu">pure</span> (<span class="dt">NodeF</span> () <span class="dv">0</span> (<span class="fu">h</span> <span class="op">-</span> <span class="dv">1</span>))</span>
<span id="cb89-6"><a href="#cb89-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb89-7"><a href="#cb89-7" aria-hidden="true" tabindex="-1"></a><span class="fu">benchE</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb89-8"><a href="#cb89-8" aria-hidden="true" tabindex="-1"></a><span class="fu">benchE</span> <span class="ot">=</span> <span class="co">{- localOption mutatorCpuTime $ -}</span> <span class="fu">bgroup</span> <span class="st">&quot;E-thin&quot;</span></span>
<span id="cb89-9"><a href="#cb89-9" aria-hidden="true" tabindex="-1"></a>  [ <span class="fu">bench</span> <span class="st">&quot;1x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeE</span> <span class="dv">100</span>)</span>
<span id="cb89-10"><a href="#cb89-10" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;10x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeE</span> <span class="dv">1000</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;E-thin.1x&quot;</span></span>
<span id="cb89-11"><a href="#cb89-11" aria-hidden="true" tabindex="-1"></a>  ]</span></code></pre></div>
<p>Raw output:</p>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;E-thin.&quot;
All
  E-thin
    1x:  OK
      243  μs ±  22 μs, 1.2 MB allocated,  13 KB copied, 6.0 MB peak memory
    10x: OK
      179  ms ±  17 ms, 119 MB allocated,  29 MB copied,  21 MB peak memory, 737.76x</code></pre>
</details>
<p>Using the previous setup comparing a baseline and a 10x run, we see a more than
700x slowdown, so much worse than the 100x predicted by a quadratic model.
Interestingly, the raw output shows that the total cumulative allocations did
grow by a 100x factor.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<p>But it gets weirder with more data points: it does not follow a clear power law.
If <span class="math inline">Time(<em>n</em>)</span> grew as
<span class="math inline"><em>n</em><sup><em>c</em></sup></span> for some fixed exponent <span class="math inline"><em>c</em></span>, then the ratio
<span class="math inline">Time(<em>M</em><em>n</em>)/Time(<em>n</em>)</span> would be <span class="math inline"><em>M</em><sup><em>c</em></sup></span>,
a constant that does not depend on <span class="math inline"><em>n</em></span>.</p>
<p>In the following benchmark, we keep doubling the height (<span class="math inline"><em>M</em> = 2</span>) for every
test case, and we measure the time relative to the preceding case each time.
A quadratic model predicts a 4x slowdown at every step. Instead, we
observe wildly varying factors.</p>
<p>Benchmark output (each time factor is relative to the preceding line,
for example, the “4x” benchmark is 9.5x slower than the “2x” benchmark):</p>
<table>
<thead>
<tr>
<th>height</th>
<th>time</th>
</tr>
</thead>
<tbody>
<tr>
<td>1x</td>
<td></td>
</tr>
<tr>
<td>2x</td>
<td>10.9x</td>
</tr>
<tr>
<td>4x</td>
<td>9.5x</td>
</tr>
<tr>
<td>8x</td>
<td>5.4x</td>
</tr>
<tr>
<td>16x</td>
<td>1.4x</td>
</tr>
</tbody>
</table>
<details>
<summary>
Code and raw output
</summary>
<div class="sourceCode" id="cb91"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb91-1"><a href="#cb91-1" aria-hidden="true" tabindex="-1"></a><span class="fu">benchE&#39;</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb91-2"><a href="#cb91-2" aria-hidden="true" tabindex="-1"></a><span class="fu">benchE&#39;</span> <span class="ot">=</span> <span class="co">{- localOption mutatorCpuTime $ -}</span> <span class="fu">bgroup</span> <span class="st">&quot;E-thin-more&quot;</span></span>
<span id="cb91-3"><a href="#cb91-3" aria-hidden="true" tabindex="-1"></a>  [ <span class="fu">bench</span> <span class="st">&quot;1x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeE</span> <span class="dv">100</span>)</span>
<span id="cb91-4"><a href="#cb91-4" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;2x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeE</span> <span class="dv">200</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;E-thin-more.1x&quot;</span></span>
<span id="cb91-5"><a href="#cb91-5" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;4x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeE</span> <span class="dv">400</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;E-thin-more.2x&quot;</span></span>
<span id="cb91-6"><a href="#cb91-6" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;8x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeE</span> <span class="dv">800</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;E-thin-more.4x&quot;</span></span>
<span id="cb91-7"><a href="#cb91-7" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;16x&quot;</span> (<span class="fu">nf</span> <span class="fu">thinTreeE</span> <span class="dv">1000</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;E-thin-more.8x&quot;</span></span>
<span id="cb91-8"><a href="#cb91-8" aria-hidden="true" tabindex="-1"></a>  ]</span></code></pre></div>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;E-thin-more&quot;
All
  E-thin-more
    1x:  OK
      222  μs ± 9.3 μs, 1.2 MB allocated,  13 KB copied, 6.0 MB peak memory
    2x:  OK
      2.43 ms ±  85 μs, 4.8 MB allocated, 236 KB copied, 7.0 MB peak memory, 10.94x
    4x:  OK
      23.1 ms ± 1.2 ms,  19 MB allocated, 2.7 MB copied,  10 MB peak memory, 9.53x
    8x:  OK
      126  ms ± 7.8 ms,  76 MB allocated,  18 MB copied,  24 MB peak memory, 5.44x
    16x: OK
      181  ms ± 7.0 ms, 119 MB allocated,  30 MB copied,  24 MB peak memory, 1.44x</code></pre>
</details>
<p>I believe this benchmark is triggering some pathological behavior in the garbage
collector. I modified <code>tasty-bench</code> with an option to measure CPU time without GC
(mutator time). At time of writing, <code>tasty-bench</code> is still waiting for a new release.
We can point Cabal to an unreleased commit of <code>tasty-bench</code> by adding the following
lines to <code>cabal.project.local</code>.</p>
<pre><code>source-repository-package
    type: git
    location: https://github.com/Bodigrim/tasty-bench.git
    tag: 81ff742a3db1d514461377729e00a74e5a9ac1b8</code></pre>
<p>Then, uncomment the setting “<code>localOption mutatorCpuTime $</code>” in <code>benchE</code> and
<code>benchE'</code> above and uncomment the import of <code>mutatorCpuTime</code> at the top.</p>
<p>Benchmark output (excluding GC time, relative):</p>
<table>
<thead>
<tr>
<th>height</th>
<th>time</th>
</tr>
</thead>
<tbody>
<tr>
<td>baseline</td>
<td>1x</td>
</tr>
<tr>
<td>1x</td>
<td>95x</td>
</tr>
</tbody>
</table>
<details>
<summary>
Raw output
</summary>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;E-thin.&quot;
All
  E-thin
    1x:  OK
      216  μs ±  18 μs, 1.2 MB allocated,  13 KB copied, 6.0 MB peak memory
    10x: OK
      20.5 ms ± 1.9 ms, 119 MB allocated,  29 MB copied,  21 MB peak memory, 94.91x</code></pre>
</details>
<p>For the “2x” benchmarks, we are closer the expected 4x slowdown, but there is
still a noticeable gap.
I’m going to chalk the rest to inherent measurement errors (the cost of
<em>tasty-bench</em>’s simplicity) exacerbated by the pathological GC behavior;
a possible explanation is that the pattern of memory usage becomes so bad that
it affects non-GC time.</p>
<p>Benchmark output (excluding GC time, each measurement is relative to the
preceding line):</p>
<table>
<thead>
<tr>
<th>height</th>
<th>time</th>
</tr>
</thead>
<tbody>
<tr>
<td>1x</td>
<td></td>
</tr>
<tr>
<td>2x</td>
<td>3.2x</td>
</tr>
<tr>
<td>4x</td>
<td>4.2x</td>
</tr>
<tr>
<td>8x</td>
<td>4.5x</td>
</tr>
<tr>
<td>16x</td>
<td>1.7x</td>
</tr>
</tbody>
</table>
<details>
<summary>
Raw output
</summary>
<pre><code>$ cabal exec breadth-first-unfolds -- -p &quot;E-thin-more&quot;
All
  E-thin-more
    1x:  OK
      186  μs ±  16 μs, 1.2 MB allocated,  13 KB copied,  21 MB peak memory
    2x:  OK
      597  μs ±  28 μs, 4.8 MB allocated, 236 KB copied,  21 MB peak memory, 3.20x
    4x:  OK
      2.48 ms ± 148 μs,  19 MB allocated, 2.9 MB copied,  21 MB peak memory, 4.15x
    8x:  OK
      11.2 ms ± 986 μs,  76 MB allocated,  18 MB copied,  24 MB peak memory, 4.50x
    16x: OK
      18.4 ms ± 1.7 ms, 119 MB allocated,  29 MB copied,  24 MB peak memory, 1.65x</code></pre>
</details>
<p>It doesn’t seem possible for a breadth-first unfold to be
<a href="https://stackoverflow.com/questions/27748526/is-a-lazy-breadth-first-monadic-rose-tree-unfold-possible">both maximally lazy and of linear time complexity</a>,
but I don’t know how to formally prove that impossibility either.</p>
<h2 id="benchmarks">Microbenchmarks: Queues vs Global Levels vs Weaves</h2>
<p>So far we’ve focused on asymptotics (linear vs quadratic). Some readers
will inevitably wonder about real speed.
Among the linear-time algorithms—queues (“Q”), global levels (“G”),
and weaves (lazy “L” or oblivious “O”)—which one is faster?</p>
<p>tl;dr: Queues are (much) faster in these microbenchmarks (up to 25x!),
but keep in mind that these are all quite naive implementations.</p>
<p>There are two categories to measure separately: unfolds which produce trees,
and oblivious unfolds—which don’t produce trees. These microbenchmarks
construct full trees up to a chosen number of nodes. When there is an
output tree, we force it (using <code>nf</code>), otherwise we force a counter of the
number of nodes. We run on different sufficiently large sizes (500 and 5000)
to check the stability of the measured factors, ensuring that we are only
comparing the time components that dominate at scale.</p>
<p>The tables list times relative to the queue benchmark for each tree size.</p>
<h3 id="tree-producing-unfolds">Tree-producing unfolds</h3>
<table>
<thead>
<tr>
<th>algorithm</th>
<th>size</th>
<th>time</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Queue</strong></td>
<td>500</td>
<td>1x</td>
</tr>
<tr>
<td><strong>Global Levels</strong></td>
<td>500</td>
<td>1.4x</td>
</tr>
<tr>
<td><strong>Lazy Weave</strong></td>
<td>500</td>
<td>3.1x</td>
</tr>
<tr>
<td><strong>Queue</strong></td>
<td>5000</td>
<td>1x</td>
</tr>
<tr>
<td><strong>Global Levels</strong></td>
<td>5000</td>
<td>1.2x</td>
</tr>
<tr>
<td><strong>Lazy Weave</strong></td>
<td>5000</td>
<td>3.3x</td>
</tr>
</tbody>
</table>
<details>
<summary>
Code and raw output
</summary>
<div class="sourceCode" id="cb96"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb96-1"><a href="#cb96-1" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTreeF</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">TreeF</span> <span class="dt">Int</span> <span class="dt">Int</span></span>
<span id="cb96-2"><a href="#cb96-2" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTreeF</span> <span class="fu">size</span> <span class="fu">n</span> <span class="op">|</span> <span class="fu">n</span> <span class="op">&gt;</span><span class="ot">=</span> <span class="fu">size</span> <span class="ot">=</span> <span class="dt">LeafF</span></span>
<span id="cb96-3"><a href="#cb96-3" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTreeF</span> <span class="fu">size</span> <span class="fu">n</span> <span class="ot">=</span> <span class="dt">NodeF</span> <span class="fu">n</span> (<span class="dv">2</span> <span class="op">*</span> <span class="fu">n</span>) (<span class="dv">2</span> <span class="op">*</span> <span class="fu">n</span> <span class="op">+</span> <span class="dv">1</span>)</span>
<span id="cb96-4"><a href="#cb96-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb96-5"><a href="#cb96-5" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTree_Q</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="dt">Int</span></span>
<span id="cb96-6"><a href="#cb96-6" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTree_Q</span> <span class="fu">size</span> <span class="ot">=</span> <span class="fu">runIdentity</span> (<span class="fu">unfoldM_BF_Q</span> (<span class="dt">Identity</span> <span class="op">.</span> <span class="fu">fullTreeF</span> <span class="fu">size</span>) <span class="dv">1</span>)</span>
<span id="cb96-7"><a href="#cb96-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb96-8"><a href="#cb96-8" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTree_G</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="dt">Int</span></span>
<span id="cb96-9"><a href="#cb96-9" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTree_G</span> <span class="fu">size</span> <span class="ot">=</span> <span class="fu">runIdentity</span> (<span class="fu">unfoldM_BF_G</span> (<span class="dt">Identity</span> <span class="op">.</span> <span class="fu">fullTreeF</span> <span class="fu">size</span>) <span class="dv">1</span>)</span>
<span id="cb96-10"><a href="#cb96-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb96-11"><a href="#cb96-11" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTree_L</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="dt">Int</span></span>
<span id="cb96-12"><a href="#cb96-12" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTree_L</span> <span class="fu">size</span> <span class="ot">=</span> <span class="fu">runIdentity</span> (<span class="fu">unfoldM_BF_L</span> (<span class="dt">Identity</span> <span class="op">.</span> <span class="fu">fullTreeF</span> <span class="fu">size</span>) <span class="dv">1</span>)</span>
<span id="cb96-13"><a href="#cb96-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb96-14"><a href="#cb96-14" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTree</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb96-15"><a href="#cb96-15" aria-hidden="true" tabindex="-1"></a><span class="fu">fullTree</span> <span class="ot">=</span> <span class="fu">bgroup</span> <span class="st">&quot;fullTree&quot;</span></span>
<span id="cb96-16"><a href="#cb96-16" aria-hidden="true" tabindex="-1"></a>  [ <span class="fu">bench</span> <span class="st">&quot;Q-1x&quot;</span> (<span class="fu">nf</span> <span class="fu">fullTree_Q</span> <span class="dv">500</span>)</span>
<span id="cb96-17"><a href="#cb96-17" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;G-1x&quot;</span> (<span class="fu">nf</span> <span class="fu">fullTree_G</span> <span class="dv">500</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;fullTree.Q-1x&quot;</span></span>
<span id="cb96-18"><a href="#cb96-18" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;L-1x&quot;</span> (<span class="fu">nf</span> <span class="fu">fullTree_L</span> <span class="dv">500</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;fullTree.Q-1x&quot;</span></span>
<span id="cb96-19"><a href="#cb96-19" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;Q-10x&quot;</span> (<span class="fu">nf</span> <span class="fu">fullTree_Q</span> <span class="dv">5000</span>)</span>
<span id="cb96-20"><a href="#cb96-20" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;G-10x&quot;</span> (<span class="fu">nf</span> <span class="fu">fullTree_G</span> <span class="dv">5000</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;fullTree.Q-10x&quot;</span></span>
<span id="cb96-21"><a href="#cb96-21" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;L-10x&quot;</span> (<span class="fu">nf</span> <span class="fu">fullTree_L</span> <span class="dv">5000</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;fullTree.Q-10x&quot;</span></span>
<span id="cb96-22"><a href="#cb96-22" aria-hidden="true" tabindex="-1"></a>  ]</span></code></pre></div>
<pre><code>$ cabal exec breadth-first-unfolds -- -p fullTree
All
  fullTree
    Q-1x:  OK
      20.6 μs ± 1.1 μs, 141 KB allocated, 477 B  copied, 6.0 MB peak memory
    G-1x:  OK
      28.6 μs ± 2.4 μs, 223 KB allocated, 928 B  copied, 6.0 MB peak memory, 1.39x
    L-1x:  OK
      64.3 μs ± 5.6 μs, 353 KB allocated, 3.7 KB copied, 6.0 MB peak memory, 3.13x
    Q-10x: OK
      287  μs ±  26 μs, 1.5 MB allocated,  57 KB copied, 7.0 MB peak memory
    G-10x: OK
      349  μs ±  30 μs, 2.2 MB allocated,  94 KB copied, 7.0 MB peak memory, 1.22x
    L-10x: OK
      935  μs ±  73 μs, 3.5 MB allocated, 386 KB copied, 7.0 MB peak memory, 3.25x</code></pre>
</details>
<h3 id="oblivious-unfolds">Oblivious unfolds</h3>
<table>
<thead>
<tr>
<th>algorithm</th>
<th>size</th>
<th>time</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Queue</strong></td>
<td>500</td>
<td>1x</td>
</tr>
<tr>
<td><strong>Global Levels</strong></td>
<td>500</td>
<td>11x</td>
</tr>
<tr>
<td><strong>Oblivious Weave</strong></td>
<td>500</td>
<td>25x</td>
</tr>
<tr>
<td><strong>Queue</strong></td>
<td>5000</td>
<td>1x</td>
</tr>
<tr>
<td><strong>Global Levels</strong></td>
<td>5000</td>
<td>10x</td>
</tr>
<tr>
<td><strong>Oblivious Weave</strong></td>
<td>5000</td>
<td>24x</td>
</tr>
</tbody>
</table>
<details>
<summary>
Code and raw output
</summary>
<div class="sourceCode" id="cb98"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb98-1"><a href="#cb98-1" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_Q_</span> <span class="ot">::</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> (<span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> (<span class="dt">TreeF</span> <span class="fu">a</span> <span class="fu">s</span>)) <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="fu">m</span> ()</span>
<span id="cb98-2"><a href="#cb98-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unfoldM_BF_Q_</span> <span class="fu">f</span> <span class="fu">s0</span> <span class="ot">=</span> <span class="fu">unfoldM_f</span> (<span class="fu">s0</span> <span class="op">&lt;+</span> <span class="dt">Empty</span>)</span>
<span id="cb98-3"><a href="#cb98-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb98-4"><a href="#cb98-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">unfoldM_f</span> (<span class="fu">q</span> <span class="op">:&gt;</span> <span class="fu">s</span>) <span class="ot">=</span> <span class="fu">f</span> <span class="fu">s</span> <span class="op">&gt;&gt;</span><span class="ot">=</span> <span class="op">\</span><span class="kw">case</span></span>
<span id="cb98-5"><a href="#cb98-5" aria-hidden="true" tabindex="-1"></a>      <span class="dt">LeafF</span> <span class="ot">-&gt;</span> <span class="fu">unfoldM_f</span> <span class="fu">q</span></span>
<span id="cb98-6"><a href="#cb98-6" aria-hidden="true" tabindex="-1"></a>      <span class="dt">NodeF</span> <span class="fu">_</span> <span class="fu">l</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="fu">unfoldM_f</span> (<span class="fu">r</span> <span class="op">&lt;+</span> <span class="fu">l</span> <span class="op">&lt;+</span> <span class="fu">q</span>)</span>
<span id="cb98-7"><a href="#cb98-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">unfoldM_f</span> <span class="dt">Empty</span> <span class="ot">=</span> <span class="fu">pure</span> ()</span></code></pre></div>
<div class="sourceCode" id="cb99"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb99-1"><a href="#cb99-1" aria-hidden="true" tabindex="-1"></a><span class="fu">eatFullTree_Q</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb99-2"><a href="#cb99-2" aria-hidden="true" tabindex="-1"></a><span class="fu">eatFullTree_Q</span> <span class="fu">size</span> <span class="ot">=</span> (<span class="ot">`execState`</span> <span class="dv">0</span>) (<span class="fu">unfoldM_BF_Q_</span> (<span class="fu">state</span> <span class="op">.</span> <span class="op">\</span><span class="fu">n</span> <span class="fu">c</span> <span class="ot">-&gt;</span> (<span class="fu">fullTreeF</span> <span class="fu">size</span> <span class="fu">n</span>, <span class="fu">c</span> <span class="op">+</span> <span class="dv">1</span>)) <span class="dv">1</span>)</span>
<span id="cb99-3"><a href="#cb99-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb99-4"><a href="#cb99-4" aria-hidden="true" tabindex="-1"></a><span class="fu">eatFullTree_G</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb99-5"><a href="#cb99-5" aria-hidden="true" tabindex="-1"></a><span class="fu">eatFullTree_G</span> <span class="fu">size</span> <span class="ot">=</span> (<span class="ot">`execState`</span> <span class="dv">0</span>) (<span class="fu">unfoldM_BF_G_</span> (<span class="fu">state</span> <span class="op">.</span> <span class="op">\</span><span class="fu">n</span> <span class="fu">c</span> <span class="ot">-&gt;</span> (<span class="fu">fullTreeF</span> <span class="fu">size</span> <span class="fu">n</span>, <span class="fu">c</span> <span class="op">+</span> <span class="dv">1</span>)) <span class="dv">1</span>)</span>
<span id="cb99-6"><a href="#cb99-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb99-7"><a href="#cb99-7" aria-hidden="true" tabindex="-1"></a><span class="fu">eatFullTree_O</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb99-8"><a href="#cb99-8" aria-hidden="true" tabindex="-1"></a><span class="fu">eatFullTree_O</span> <span class="fu">size</span> <span class="ot">=</span> (<span class="ot">`execState`</span> <span class="dv">0</span>) (<span class="fu">unfoldM_BF_O_</span> (<span class="fu">state</span> <span class="op">.</span> <span class="op">\</span><span class="fu">n</span> <span class="fu">c</span> <span class="ot">-&gt;</span> (<span class="fu">fullTreeF</span> <span class="fu">size</span> <span class="fu">n</span>, <span class="fu">c</span> <span class="op">+</span> <span class="dv">1</span>)) <span class="dv">1</span>)</span>
<span id="cb99-9"><a href="#cb99-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb99-10"><a href="#cb99-10" aria-hidden="true" tabindex="-1"></a><span class="fu">eatFullTree</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb99-11"><a href="#cb99-11" aria-hidden="true" tabindex="-1"></a><span class="fu">eatFullTree</span> <span class="ot">=</span> <span class="fu">bgroup</span> <span class="st">&quot;eatFullTree&quot;</span></span>
<span id="cb99-12"><a href="#cb99-12" aria-hidden="true" tabindex="-1"></a>  [ <span class="fu">bench</span> <span class="st">&quot;Q-1x&quot;</span> (<span class="fu">nf</span> <span class="fu">eatFullTree_Q</span> <span class="dv">500</span>)</span>
<span id="cb99-13"><a href="#cb99-13" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;G-1x&quot;</span> (<span class="fu">nf</span> <span class="fu">eatFullTree_G</span> <span class="dv">500</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;eatFullTree.Q-1x&quot;</span></span>
<span id="cb99-14"><a href="#cb99-14" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;W-1x&quot;</span> (<span class="fu">nf</span> <span class="fu">eatFullTree_O</span> <span class="dv">500</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;eatFullTree.Q-1x&quot;</span></span>
<span id="cb99-15"><a href="#cb99-15" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;Q-10x&quot;</span> (<span class="fu">nf</span> <span class="fu">eatFullTree_Q</span> <span class="dv">5000</span>)</span>
<span id="cb99-16"><a href="#cb99-16" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;G-10x&quot;</span> (<span class="fu">nf</span> <span class="fu">eatFullTree_G</span> <span class="dv">5000</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;eatFullTree.Q-10x&quot;</span></span>
<span id="cb99-17"><a href="#cb99-17" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">bench</span> <span class="st">&quot;W-10x&quot;</span> (<span class="fu">nf</span> <span class="fu">eatFullTree_O</span> <span class="dv">5000</span>) <span class="op">&amp;</span> <span class="fu">bcompare</span> <span class="st">&quot;eatFullTree.Q-10x&quot;</span></span>
<span id="cb99-18"><a href="#cb99-18" aria-hidden="true" tabindex="-1"></a>  ]</span></code></pre></div>
<pre><code>$ cabal exec breadth-first-unfolds -- -p eatFullTree
All
  eatFullTree
    Q-1x:  OK
      11.0 μs ± 724 ns,  78 KB allocated, 338 B  copied, 6.0 MB peak memory
    G-1x:  OK
      116  μs ±  11 μs, 379 KB allocated, 1.3 KB copied, 6.0 MB peak memory, 10.57x
    W-1x:  OK
      278  μs ±  14 μs, 830 KB allocated, 5.9 KB copied, 6.0 MB peak memory, 25.36x
    Q-10x: OK
      120  μs ±  11 μs, 781 KB allocated,  21 KB copied, 6.0 MB peak memory
    G-10x: OK
      1.23 ms ± 122 μs, 3.9 MB allocated, 109 KB copied, 7.0 MB peak memory, 10.27x
    W-10x: OK
      2.92 ms ± 255 μs, 8.4 MB allocated, 631 KB copied, 7.0 MB peak memory, 24.43x</code></pre>
</details>
<hr />
<h2 id="conclusion">Conclusion</h2>
<p>I hope to have piqued your interest in breadth-first unfolds without
using queues.
To the best of my knowledge, this specific problem hasn’t been studied in the
literature. It is of course related to breadth-first traversals,
previously solved using the <code>Phases</code> applicative.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>
The intersection of functional programming and breadth-first walks is a small
niche, which makes it quick to survey that corner of the world for any related
ideas to those presented here.</p>
<p>The paper <a href="https://dl.acm.org/doi/abs/10.1145/3607850"><em>Modular models of monoids with operations</em></a> by Zhixuan Yang
and Nicolas Wu, in ICFP 2023, mentions a general construction of <code>Phases</code> as an
example application of their theory. Basically, <code>Phases</code> is defined by a
fixed-point equation:</p>
<pre><code>Phases f = Day f Phases :+: Identity</code></pre>
<p>We can express <code>Phases</code> abstractly as a least fixed-point
<span class="math inline"><em>μ</em><em>x</em>.<em>f</em>▫<em>x</em> + Id</span> in any monoidal category with a suitable structure.
If we instantiate the monoidal product <span class="math inline">▫</span> not with <code>Day</code> convolution,
but with functor composition (<code>Compose</code>), then we get <code>Weave</code>.</p>
<p>In another coincidence, the <a href="https://hackage.haskell.org/package/monad-coroutine"><em>monad-coroutine</em></a> package
implements <a href="https://hackage.haskell.org/package/monad-coroutine-0.9.2/docs/Control-Monad-Coroutine.html#v:weave">a <code>weave</code> function</a> which is a generalization of
<code>liftS2</code>—this may require some squinting.
While <code>WeaveS</code> as a data type coincides with the free monad <a href="https://hackage.haskell.org/package/free-5.2/docs/Control-Monad-Free.html#t:Free"><code>Free</code></a>,
<em>monad-coroutine</em>’s core data type <a href="https://hackage.haskell.org/package/monad-coroutine-0.9.2/docs/Control-Monad-Coroutine.html#t:Coroutine"><code>Coroutine</code></a> coincides
with the free monad transformer <a href="https://hackage.haskell.org/package/free-5.2/docs/Control-Monad-Trans-Free.html#t:FreeT"><code>FreeT</code></a>.</p>
<p>We can view <code>Phases</code> as a generalization of “zipping” from
lists to free applicatives—which are essentially lists of actions,
and <code>Weave</code> generalizes that further to free monads. To recap, the surprise was
that the naive data type of free monads results in a quadratic-time unfold.
That issue motivated a “lazy” variant<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> which achieves a linear-time
breadth-first unfold. That in turn suggested an even “lazier” variant which
enables more control on evaluation order at the cost of efficiency.</p>
<p>I’ve just released the <a href="https://hackage.haskell.org/package/weave"><em>weave</em></a> library which implements
the main ideas of this post. I don’t expect it to have many users, given
how much slower it is compared to queue-based solutions.
But I would be curious to find a use case for the new <em>compositionality</em>
afforded by this abstraction.</p>
<h2 id="recap-table">Recap table</h2>
<div class="small">
<table>
<colgroup>
<col style="width: 27%" />
<col style="width: 18%" />
<col style="width: 18%" />
<col style="width: 18%" />
<col style="width: 18%" />
</colgroup>
<thead>
<tr>
<th></th>
<th>Unfolds</th>
<th>Time</th>
<th>Laziness</th>
<th>Compositional</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Phases</strong><sup>*</sup></td>
<td>No</td>
<td>linear<sup>†</sup></td>
<td>by levels</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Queue</strong> (Q)</td>
<td>Yes</td>
<td>linear<sup>†</sup></td>
<td>strict</td>
<td>No</td>
</tr>
<tr>
<td><strong>Global Levels</strong> (G)</td>
<td>Yes</td>
<td>linear<sup>†</sup></td>
<td>by levels</td>
<td>No</td>
</tr>
<tr>
<td><strong>Strict Weave</strong> (S)</td>
<td>Yes</td>
<td>quadratic<sup>‡</sup></td>
<td>strict</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Oblivious Weave</strong> (O)</td>
<td>Oblivious only</td>
<td>linear<sup>†</sup></td>
<td>N/A</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Lazy Weave</strong> (L)</td>
<td>Yes</td>
<td>linear<sup>†</sup></td>
<td>by levels</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Endless Weave</strong> (E)</td>
<td>Yes</td>
<td>quadratic<sup>‡E</sup></td>
<td>maximally lazy<sup>◊</sup></td>
<td>Yes</td>
</tr>
</tbody>
</table>
</div>
<p><sup>†</sup>Linear wrt. size: <span class="math inline"><em>Θ</em>(size)</span>.<br />
<sup>‡</sup>Quadratic wrt. height: lower bound <span class="math inline"><em>Ω</em>(height<sup>2</sup>)</span>, upper bound <span class="math inline"><em>O</em>(height × size)</span>.<br />
<sup>E</sup>The “Endless” <code>meshE</code> only terminates with lazy monads.<br />
<sup>*</sup>I guess there exists an “endless Phases” variant, that
would be quadratic and maximally lazy.<br />
<sup>◊</sup>The definition of “maximally lazy” in this post actually misses a
range of possible lazy behaviors with monads other than <code>Identity</code>. A further
refinement seems to be another can of worms.</p>
<hr />
<details class="code-details">
<summary>
The <code>main</code> action of this Literate Haskell program
</summary>
<div class="sourceCode" id="cb102"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb102-1"><a href="#cb102-1" aria-hidden="true" tabindex="-1"></a><span class="fu">main</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb102-2"><a href="#cb102-2" aria-hidden="true" tabindex="-1"></a><span class="fu">main</span> <span class="ot">=</span> <span class="fu">defaultMain</span></span>
<span id="cb102-3"><a href="#cb102-3" aria-hidden="true" tabindex="-1"></a>  [ <span class="fu">testGraphQ</span></span>
<span id="cb102-4"><a href="#cb102-4" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">testGraphG</span></span>
<span id="cb102-5"><a href="#cb102-5" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">testGraphS</span></span>
<span id="cb102-6"><a href="#cb102-6" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">testGraphL</span></span>
<span id="cb102-7"><a href="#cb102-7" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">testGraphE</span></span>
<span id="cb102-8"><a href="#cb102-8" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">whnfTreeQ</span></span>
<span id="cb102-9"><a href="#cb102-9" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">whnfTreeS</span></span>
<span id="cb102-10"><a href="#cb102-10" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">whnfTreeL</span></span>
<span id="cb102-11"><a href="#cb102-11" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">whnfTreeE</span></span>
<span id="cb102-12"><a href="#cb102-12" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">forceLeftTreeL</span></span>
<span id="cb102-13"><a href="#cb102-13" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">forceLeftTreeE</span></span>
<span id="cb102-14"><a href="#cb102-14" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">benchS</span></span>
<span id="cb102-15"><a href="#cb102-15" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">benchO</span></span>
<span id="cb102-16"><a href="#cb102-16" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">benchL</span></span>
<span id="cb102-17"><a href="#cb102-17" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">benchE</span></span>
<span id="cb102-18"><a href="#cb102-18" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">benchE&#39;</span></span>
<span id="cb102-19"><a href="#cb102-19" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">fullTree</span></span>
<span id="cb102-20"><a href="#cb102-20" aria-hidden="true" tabindex="-1"></a>  , <span class="fu">eatFullTree</span></span>
<span id="cb102-21"><a href="#cb102-21" aria-hidden="true" tabindex="-1"></a>  ]</span></code></pre></div>
</details>
<details class="code-details">
<summary>
Extra test cases
</summary>
<div class="sourceCode" id="cb103"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb103-1"><a href="#cb103-1" aria-hidden="true" tabindex="-1"></a><span class="fu">whnfTreeE</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb103-2"><a href="#cb103-2" aria-hidden="true" tabindex="-1"></a><span class="fu">whnfTreeE</span> <span class="ot">=</span> <span class="fu">testCase</span> <span class="st">&quot;E-whnf&quot;</span> <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb103-3"><a href="#cb103-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">case</span> <span class="fu">partialTreeE</span> <span class="kw">of</span></span>
<span id="cb103-4"><a href="#cb103-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="fu">_</span> <span class="fu">_</span> <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> ()  <span class="co">-- Succeed</span></span>
<span id="cb103-5"><a href="#cb103-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Leaf</span> <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="st">&quot;unreachable&quot;</span></span>
<span id="cb103-6"><a href="#cb103-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb103-7"><a href="#cb103-7" aria-hidden="true" tabindex="-1"></a><span class="fu">whnfTreeQ</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb103-8"><a href="#cb103-8" aria-hidden="true" tabindex="-1"></a><span class="fu">whnfTreeQ</span> <span class="ot">=</span> <span class="fu">expectFail</span> <span class="op">$</span> <span class="fu">testCase</span> <span class="st">&quot;Q-whnf&quot;</span> <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb103-9"><a href="#cb103-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">case</span> <span class="fu">partialTreeQ</span> <span class="kw">of</span></span>
<span id="cb103-10"><a href="#cb103-10" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="fu">_</span> <span class="fu">_</span> <span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> ()  <span class="co">-- Succeed</span></span>
<span id="cb103-11"><a href="#cb103-11" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Leaf</span> <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="st">&quot;unreachable&quot;</span></span>
<span id="cb103-12"><a href="#cb103-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb103-13"><a href="#cb103-13" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeQ</span> <span class="ot">::</span> <span class="dt">Tree</span> ()</span>
<span id="cb103-14"><a href="#cb103-14" aria-hidden="true" tabindex="-1"></a><span class="fu">partialTreeQ</span> <span class="ot">=</span> <span class="fu">runIdentity</span> (<span class="fu">unfoldM_BF_Q</span> (<span class="dt">Identity</span> <span class="op">.</span> <span class="fu">partialTreeF</span>) <span class="dt">True</span>)</span>
<span id="cb103-15"><a href="#cb103-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb103-16"><a href="#cb103-16" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraph_L</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="dt">Int</span></span>
<span id="cb103-17"><a href="#cb103-17" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraph_L</span> <span class="ot">=</span> (<span class="ot">`evalState`</span> <span class="fu">Set.empty</span>) <span class="op">.</span> <span class="fu">unfoldM_BF_L</span> <span class="fu">visitGraph</span></span>
<span id="cb103-18"><a href="#cb103-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb103-19"><a href="#cb103-19" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphL</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb103-20"><a href="#cb103-20" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphL</span> <span class="ot">=</span> <span class="fu">testCase</span> <span class="st">&quot;L-graph&quot;</span> <span class="op">$</span></span>
<span id="cb103-21"><a href="#cb103-21" aria-hidden="true" tabindex="-1"></a>  <span class="fu">bfGraph_L</span> <span class="dv">1</span> <span class="op">@?</span><span class="ot">=</span></span>
<span id="cb103-22"><a href="#cb103-22" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="dv">1</span></span>
<span id="cb103-23"><a href="#cb103-23" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">2</span> <span class="dt">Leaf</span></span>
<span id="cb103-24"><a href="#cb103-24" aria-hidden="true" tabindex="-1"></a>              (<span class="dt">Node</span> <span class="dv">5</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span>
<span id="cb103-25"><a href="#cb103-25" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">4</span> <span class="dt">Leaf</span> (<span class="dt">Node</span> <span class="dv">6</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span>
<span id="cb103-26"><a href="#cb103-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb103-27"><a href="#cb103-27" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraph_E</span> <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Tree</span> <span class="dt">Int</span></span>
<span id="cb103-28"><a href="#cb103-28" aria-hidden="true" tabindex="-1"></a><span class="fu">bfGraph_E</span> <span class="ot">=</span> (<span class="ot">`evalState`</span> <span class="fu">Set.empty</span>) <span class="op">.</span> <span class="fu">unfoldM_BF_E</span> <span class="fu">visitGraph</span></span>
<span id="cb103-29"><a href="#cb103-29" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb103-30"><a href="#cb103-30" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphE</span> <span class="ot">::</span> <span class="dt">TestTree</span></span>
<span id="cb103-31"><a href="#cb103-31" aria-hidden="true" tabindex="-1"></a><span class="fu">testGraphE</span> <span class="ot">=</span> <span class="fu">testCase</span> <span class="st">&quot;E-graph&quot;</span> <span class="op">$</span></span>
<span id="cb103-32"><a href="#cb103-32" aria-hidden="true" tabindex="-1"></a>  <span class="fu">bfGraph_E</span> <span class="dv">1</span> <span class="op">@?</span><span class="ot">=</span></span>
<span id="cb103-33"><a href="#cb103-33" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Node</span> <span class="dv">1</span></span>
<span id="cb103-34"><a href="#cb103-34" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">2</span> <span class="dt">Leaf</span></span>
<span id="cb103-35"><a href="#cb103-35" aria-hidden="true" tabindex="-1"></a>              (<span class="dt">Node</span> <span class="dv">5</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span>
<span id="cb103-36"><a href="#cb103-36" aria-hidden="true" tabindex="-1"></a>      (<span class="dt">Node</span> <span class="dv">4</span> <span class="dt">Leaf</span> (<span class="dt">Node</span> <span class="dv">6</span> <span class="dt">Leaf</span> <span class="dt">Leaf</span>))</span></code></pre></div>
</details>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Note that <code>tasty-bench</code> also reports memory statistics
(allocated, copied, and peak memory) when certain RTS options are enabled,
which I’ve done by compiling the test executable with <code>-with-rtsopts=-T</code>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><ul>
<li>The <a href="https://hackage.haskell.org/package/tree-traversals"><em>tree-traversals</em></a> library by Noah Easterly.</li>
<li><a href="https://dl.acm.org/doi/10.1145/3473577"><em>Algebras for weighted search</em></a>,
by Donnacha Oisín Kidney and Nicolas Wu, in ICFP 2021.</li>
<li><a href="https://link.springer.com/chapter/10.1007/978-3-031-16912-0_1"><em>Breadth-first traversal via staging</em></a>,
by Jeremy Gibbons, Donnacha Oisín Kidney, Tom Shrijvers, and Nicolas Wu, in MPC 2022.
It has been revised into a short version (5 pages):
<a href="https://dl.acm.org/doi/10.1145/3609025.3609479"><em>Phases in software architecture</em></a> in FUNARCH 2023.</li>
</ul>
<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></li>
<li id="fn3"><p>Speaking of variants of free monads, one might think
of the <a href="https://okmij.org/ftp/Computation/free-monad.html">“freer” monad</a>, which has different motivations and
which does not help us here.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></summary>
</entry>
<entry>
    <title>Unicode shenanigans: Martine Ã©crit en UTF-8</title>
    <link href="https://blog.poisson.chat/posts/2024-10-05-unicode-shenanigans.html" />
    <id>https://blog.poisson.chat/posts/2024-10-05-unicode-shenanigans.html</id>
    <published>2024-10-05T00:00:00Z</published>
    <updated>2024-10-05T00:00:00Z</updated>
    <summary type="html"><![CDATA[<figure>
<figcaption>
An old French meme
</figcaption>
<img src="/images/martine-ecrit-en-utf8.png"
       alt="Martine Ã©crit en UTF-8 (parody cover of the Martine series of French children's books)"></img>
</figure>
<p>On my feed aggregator <a href="https://haskell.pl-a.net">haskell.pl-a.net</a>,
I occasionally saw posts with broken titles like this
(<a href="http://blog.ezyang.com/2024/10/whats-different-this-time-llm-edition/">from ezyang’s blog</a>):</p>
<blockquote>
Whatâ€™s different this time? LLM edition
</blockquote>
<p>Yesterday I decided to do something about it.</p>
<h2 id="locating-the-problem">Locating the problem</h2>
<p>Tracing back where it came from, that title was sent already broken by
<a href="http://planet.haskell.org/">Planet Haskell</a>, which is itself a
<a href="https://en.wikipedia.org/wiki/Planet_(software)">feed aggregator for blogs</a>.
The blog originally produces the good not broken title.
Therefore the blame lies with Planet Haskell.
It’s probably a misconfigured locale. Maybe someone will fix it.
It seems to be running archaic software on an old machine,
stuff I wouldn’t deal with myself so I won’t ask someone else to.</p>
<figure>
<figcaption>
ASCII diagram of how a blog title travels through the relevant parties
</figcaption>
<div class="center">
<pre><code>      Blog
       |
       | What’s
       v
 Planet Haskell
       | 
       | Whatâ€™s
       v
haskell.pl-a.net (my site)
       |
       | Whatâ€™s
       v
  Your screen</code></pre>
</div>
</figure>
<p>In any case, this mistake can be fixed after the fact. Mis-encoded text is such
an ubiquitous issue that there are nicely packaged solutions out there, like
<a href="https://ftfy.readthedocs.io/en/latest/">ftfy</a>.</p>
<blockquote>
ftfy has been used as a data processing step in major NLP research, including
OpenAI’s original GPT.
</blockquote>
<p>But my hobby site is written in OCaml and I would rather have fun solving
this encoding problem than figure out how to install a Python program and call
it from OCaml.</p>
<h2 id="explaining-the-problem">Explaining the problem</h2>
<p>This is the typical situation where a program is assuming the wrong text encoding.</p>
<h3 id="text-encodings">Text encodings</h3>
<p>A quick summary for those who don’t know about text encodings.</p>
<p>Humans read and write sequences of characters,
while computers talk to each other using sequences of bytes.
If Alice writes a blog, and Bob wants to read it from across the world,
the characters that Alice writes must be encoded into bytes so
her computer can send it over the internet to Bob’s computer,
and Bob’s computer must decode those bytes to display them on his screen.
The mapping between sequences of characters and sequences of bytes
is called an encoding.</p>
<p>Multiple encodings are possible, but it’s not always obvious which encoding
to use to decode a given byte string.
There are good and bad reasons for this, but the net effect is that
many text-processing programs arbitrarily guess and assume the encoding in use,
and sometimes they assume wrong.</p>
<h3 id="back-to-the-problem">Back to the problem</h3>
<p>UTF-8 is the most prevalent encoding nowadays.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> I’d be surprised if one of the
Planet Haskell blogs doesn’t use it, which is ironic considering the issue
we’re dealing with.</p>
<ol type="1">
<li>A blog using UTF-8 encodes the right single quote<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> " ’ " as three consecutive bytes
(226, 128, 153) in its RSS or Atom feed.</li>
<li>The culprit, Planet Haskell, read those bytes but wrongly assumed an encoding
different from UTF-8 where each byte corresponds to one character.</li>
<li>It did some transformation to the decoded text
(extract the title and body and put it on a webpage with other blogs).</li>
<li>It encoded the final result in UTF-8.</li>
</ol>
<figure>
<figcaption>
ASCII diagram of how text gets encoded and decoded (wrongly)
</figcaption>
<div class="center">
<pre><code>      What the blog sees →       &#39;’&#39;
                                  |
                                  | UTF-8 encode (one character into three bytes)
                                  v
                             226 128 153
                                  |
                                  | ??? decode (not UTF-8)
                                  v
What Planet Haskell sees →   &#39;â&#39; &#39;€&#39; &#39;™&#39;
                                  |
                                  | UTF-8 encode
                                  v
                                (...)
                                  |
                                  | UTF-8 decode
                                  v
            What you see →   &#39;â&#39; &#39;€&#39; &#39;™&#39;</code></pre>
</div>
</figure>
<p>The final encoding doesn’t really matter, as long as everyone else downstream
agrees with it.
The point is that Planet Haskell outputs three characters “â€™” in place of the
right single quote " ’ ", all because UTF-8 represents " ’ " with three bytes.</p>
<p>In spite of their differences, most encodings in practice agree at least about
ASCII characters, in the range 0-127, which is sufficient to contain the majority of
English language writing if you can compromise on details such as confusing the
apostrophe and the single quotes. That’s why in the title “Whatâ€™s different
this time?” everything but one character was transferred fine.</p>
<h2 id="solving-the-problem">Solving the problem</h2>
<p>The fix is simple: replace “â€™” with " ’ ". Of course, we also want to do that
with all other characters that are mis-encoded the same way:
those are exactly all the non-ASCII Unicode characters.
The more general fix is to invert Planet Haskell’s decoding logic.
Thank the world that this mistake can be reversed to begin with.
If information had been lost by mis-encoding, I may have been forced to use one
of those dreadful LLMs to reconstruct titles.<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a></p>
<ol type="1">
<li>Decode Planet Haskell’s output in UTF-8.</li>
<li>Encode each character as a byte to recover the original output from the blog.</li>
<li>Decode the original output correctly, in UTF-8.</li>
</ol>
<p>There is one missing detail: what encoding to use in step 2?
I first tried the naive thing: each character is canonically a Unicode code point,
which is a number between 0 and 1114111, and I just hoped that those which did
occur would fit in the range 0-255.
That amounts to making the hypothesis that Planet Haskell is decoding blog
posts in <a href="https://en.wikipedia.org/wiki/ISO/IEC_8859-1">Latin-1</a>.
That seems likely enough, but you will have guessed correctly that the
naive thing did not reconstruct the right single quote in this case.
The Latin-1 hypothesis was proven false.</p>
<p>As it turns out, the euro sign “€” and the trademark symbol “™” are not
in the Latin-1 alphabet. They are code points numbers 8364 and 8482 in Unicode,
which are not in the range 0-255.
Planet Haskell has to be using an encoding that features these two symbols.
I needed to find which one.</p>
<p>Faffing about, I came across the Wikipedia article on <em>Western Latin character
sets</em> which lists a <a href="https://en.wikipedia.org/wiki/Western_Latin_character_sets_(computing)#Comparison_table">comparison table</a>. How convenient.
I looked up the two symbols to find what encoding had them, if any.
There were two candidates: Windows-1252 and Macintosh. Flip a coin.
It was Windows-1252.</p>
<p><a href="https://en.wikipedia.org/wiki/Windows-1252">Windows-1252</a> differs from Latin-1 (and thus Unicode) in 27 positions,
those whose byte starts with 8 or 9 in hexadecimal
(27 valid characters + 5 unused positions):
that’s 27 characters that I had to map manually to the range 0-255
according to the Windows-1252 encoding,
and the remaining characters would be mapped for free by Unicode.
This data entry task was autocompleted halfway through by Copilot,
because of course GPT-* knows Windows-1252 by heart.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode ocaml"><code class="sourceCode ocaml"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> windows1252_hack (c : <span class="dt">Uchar</span>.t) : <span class="dt">int</span> =</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> c = <span class="dt">Uchar</span>.to_int c <span class="kw">in</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">if</span>      c = <span class="bn">0x20AC</span> <span class="kw">then</span> <span class="bn">0x80</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x201A</span> <span class="kw">then</span> <span class="bn">0x82</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x0192</span> <span class="kw">then</span> <span class="bn">0x83</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x201E</span> <span class="kw">then</span> <span class="bn">0x84</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2026</span> <span class="kw">then</span> <span class="bn">0x85</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2020</span> <span class="kw">then</span> <span class="bn">0x86</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2021</span> <span class="kw">then</span> <span class="bn">0x87</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x02C6</span> <span class="kw">then</span> <span class="bn">0x88</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2030</span> <span class="kw">then</span> <span class="bn">0x89</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x0160</span> <span class="kw">then</span> <span class="bn">0x8A</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2039</span> <span class="kw">then</span> <span class="bn">0x8B</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x0152</span> <span class="kw">then</span> <span class="bn">0x8C</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x017D</span> <span class="kw">then</span> <span class="bn">0x8E</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2018</span> <span class="kw">then</span> <span class="bn">0x91</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2019</span> <span class="kw">then</span> <span class="bn">0x92</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x201C</span> <span class="kw">then</span> <span class="bn">0x93</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x201D</span> <span class="kw">then</span> <span class="bn">0x94</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2022</span> <span class="kw">then</span> <span class="bn">0x95</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2013</span> <span class="kw">then</span> <span class="bn">0x96</span></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2014</span> <span class="kw">then</span> <span class="bn">0x97</span></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x02DC</span> <span class="kw">then</span> <span class="bn">0x98</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x2122</span> <span class="kw">then</span> <span class="bn">0x99</span></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x0161</span> <span class="kw">then</span> <span class="bn">0x9A</span></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x203A</span> <span class="kw">then</span> <span class="bn">0x9B</span></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x0153</span> <span class="kw">then</span> <span class="bn">0x9C</span></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x017E</span> <span class="kw">then</span> <span class="bn">0x9E</span></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> <span class="kw">if</span> c = <span class="bn">0x0178</span> <span class="kw">then</span> <span class="bn">0x9F</span></span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a>  <span class="kw">else</span> c</span></code></pre></div>
<p>And that’s how I restored the quotes, apostrophes,
guillemets, accents, et autres in my feed.</p>
<hr />
<h2 id="see-also">See also</h2>
<ul>
<li><a href="https://www.datafix.com.au/BASHing2/2024-07-19.html">Mojibake, anyone?</a> from <em>BASHing data 2</em></li>
</ul>
<hr />
<p>Update: When Planet Haskell picked up this post, it fixed the intentional mojibake in the title.</p>
<figure>
<img src="/images/wtf-planet-haskell.png"
     alt="Screenshot of Planet Haskell with a correctly displayed diacritic. October 05, 2024. Lysxia's blog. Unicode shenanigans: Martine écrit en UTF-8"></img>
</figure>
<p>There is no room for this in my mental model. Planet Haskell is doing something wild to parse blog titles.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p><a href="https://w3techs.com/technologies/cross/character_encoding/ranking">As of September 2024, UTF-8 is used by 98.3% of surveyed web sites.</a><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>The Unicode <a href="https://en.wikipedia.org/wiki/Right_single_quotation_mark">right single quote</a>
is sometimes used as an apostrophe, to much <a href="https://tedclancy.wordpress.com/2015/06/03/which-unicode-character-should-represent-the-english-apostrophe-and-why-the-unicode-committee-is-very-wrong/">disapproval</a>.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>Or I could just query the blogs directly for their titles.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></summary>
</entry>
<entry>
    <title>Where does the name "algebraic data type" come from?</title>
    <link href="https://blog.poisson.chat/posts/2024-07-26-adt-history.html" />
    <id>https://blog.poisson.chat/posts/2024-07-26-adt-history.html</id>
    <published>2024-07-26T00:00:00Z</published>
    <updated>2024-07-26T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>“Algebraic data types” is a beloved feature of programming languages with
such a mysterious name. Where does this name come from?</p>
<p>There are two main episodes in this saga: Hope and Miranda.
The primary conclusion is that the name comes from universal algebra,
whereas another common interpretation of “algebraic” as a reference
to “sums of products” is not historically accurate.
We drive the point home with Clear. CLU is extra.</p>
<p>Disclaimer: I’m no historian and I’m nowhere as old as these languages
to have any first-hand perspective.
Corrections and suggestions for additional information are welcome.</p>
<pre class="toc"><code></code></pre>
<h2 id="hope-1980hope">Hope (<a href="https://homepages.inf.ed.ac.uk/dts/pub/hope.pdf">1980</a>)</h2>
<p>Algebraic data types were at first simply called “data types”.
This programming language feature is commonly attributed to
<a href="https://homepages.inf.ed.ac.uk/dts/pub/hope.pdf">Hope, an experimental applicative language</a> by Rod Burstall et al..
Here is the relevant excerpt from the paper, illustrating its concrete syntax:</p>
<blockquote>
<p>A <em>data declaration</em> is used to introduce a new data type along with
the data constructors which create elements of that type. For example,
the data declaration for natural numbers would be:</p>
<pre><code>data num == 0 ++ succ(num)</code></pre>
<p>(…) To define a type ‘tree of numbers’, we could say</p>
<pre><code>data numtree == empty ++ tip(num)
                      ++ node(numtree#numtree)</code></pre>
<p>(The sign <code>#</code> gives the cartesian product of types).
One of the elements of <code>numtree</code> is:</p>
<pre><code>node(tip(succ(0)),
     node(tip(succ(succ(0))), tip(0)))</code></pre>
<p>But we would like to have trees of lists and trees of trees as well,
without having to redefine them all separately. So we declare a
<em>type variable</em></p>
<pre><code>typevar alpha</code></pre>
<p>which when used in a type expression denotes any type
(including second- and higher-order types).
A general definition of <code>tree</code> as a parametric type is now possible:</p>
<pre><code>data tree(alpha) == empty ++ tip(alpha)
                          ++ node(tree(alpha)#tree(alpha))</code></pre>
Now <code>tree</code> is not a type but a unary <em>type constructor</em> – the type
<code>numtree</code> can be dispensed with in favour of <code>tree(num)</code>.
</blockquote>
<p>Pattern matching in Hope is done in multi-clause function declarations or multi-clause lambdas.
There was no <code>case</code> expression.</p>
<blockquote>
<pre><code>reverse(nil) &lt;= nil
reverse(a::l) &lt;= reverse(l) &lt;&gt; [a]</code></pre>
<pre><code>lambda true, p =&gt; p
     | false, p =&gt; false</code></pre>
</blockquote>
<p>As far as I can tell, other early programming languages cite Hope or one of its descendants
as their inspiration for data types.
There is a slightly earlier appearance in NPL by Darlington and the same Burstall,
but I couldn’t find a source describing the language or any samples of data type declarations.
Given the proximity, it seems reasonable to consider them the same language to a large extent.
<a href="https://dl.acm.org/doi/10.1145/321992.321996">This paper</a> by Burstall and Darlington (1977) seems to be using NPL
in its examples, but data types are only introduced informally;
see on page 62 (page 19 of the PDF):</p>
<blockquote>
<p>We need a data type <code>atom</code>, from which we derive a data type <code>tree</code>, using constructor
functions <code>tip</code> to indicate a tip and <code>tree</code> to combine two subtrees</p>
<pre><code>tip : atoms → trees
tree : trees x trees → trees</code></pre>
<p>We also need lists of atoms and of trees, so for any type <code>alpha</code> let</p>
<pre><code>nil : alpha-lists
cons : alphas x alpha-lists → alpha-lists</code></pre>
</blockquote>
<p>Hope inspired ML (OCaml’s grandpa) to adopt data types. In Standard ML:</p>
<pre><code>datatype &#39;a option = Nothing | Some of &#39;a</code></pre>
<p>Before it became Standard, ML started out as the “tactic language” of the
<a href="https://en.wikipedia.org/wiki/Logic_for_Computable_Functions">LCF proof assistant</a>
by Robin Milner, and early versions did not feature data types
(see <a href="https://github.com/theoremprover-museum/LCF77">the first version of Edinburgh LCF</a>).
it’s unclear when data types were added exactly, but
<a href="https://smlfamily.github.io/sml97-defn.pdf">The Definition of Standard ML</a>
by Milner et al. credits Hope for it (in Appendix F: The Development of ML):</p>
<blockquote>
Two movements led to the re-design of ML. One was the work of Rod Burstall
and his group on specifications, crystallised in the specification language
Clear and in the functional programming language Hope; the
latter was for expressing executable specifications. The outcome of this work
which is relevant here was twofold. First, there were elegant programming
features in Hope, particularly pattern matching and clausal function definitions;
second, there were ideas on modular construction of specifications,
using signatures in the interfaces. A smaller but significant movement was
by Luca Cardelli, who extended the data-type repertoire in ML by adding
named records and variant types.
</blockquote>
<h2 id="miranda-1985miranda">Miranda (<a href="https://www.cs.kent.ac.uk/people/staff/dat/miranda/nancypaper.pdf">1985</a>)</h2>
<p>“Data types” as a programming language feature appeared in Hope,
but its first mention under the name “algebraic data types” that I could find is in
<a href="https://www.cs.kent.ac.uk/people/staff/dat/miranda/nancypaper.pdf">Miranda: a non-strict functional language with polymorphic types</a>
by David Turner in 1985:</p>
<blockquote>
<h3 id="algebraic-data-types">Algebraic data types</h3>
<p>The basic method of introducing a new concrete data type, as in a number of
other languages, is to declare a free algebra. In Miranda this is done by an
equation using the symbol <code>::=</code>,</p>
<pre><code>tree ::= Niltree | Node num tree tree</code></pre>
being a typical example. (…)
The idea of using free algebras to define data types has a long and respectable
history [Landin 64], [Burstall 69], [Hoare 75]. We call it a free algebra, because
there are no associated laws, such as a law equating a tree with its mirror image.
Two trees are equal only if they are constructed in exactly the same way.
</blockquote>
<p>In case you aren’t aware, Miranda is a direct precursor of Haskell.
A minor similarity with Haskell that we can see here
is that data constructors are curried in Miranda, unlike in Hope and ML.
Another distinguishing feature of Miranda is laziness.
See also <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/history.pdf">A History of Haskell: being lazy with class</a>.</p>
<p>Below are links to the articles cited in the quote above.
The first [Landin 64] doesn’t explicitly talk about algebra in
this sense, while [Burstall 69] and [Hoare 75] refer to “word algebra” rather than
“free algebra” to describe the same structure, without putting “algebra” in the
same phrase as “type” yet.</p>
<ul>
<li><a href="https://www.cs.tufts.edu/comp/150FP/archive/peter-landin/mechanical-eval.pdf">The mechanical evaluation of expression</a>, by Peter Landin (1964)</li>
<li><a href="https://www.cse.chalmers.se/edu/year/2016/course/course/DAT140/Burstall.pdf">Proving properties by structural induction</a>, by Rod Burstall (1969)</li>
<li><a href="https://apps.dtic.mil/sti/pdfs/AD0772509.pdf">Recursive data structures</a> by Tony Hoare (1975)</li>
</ul>
<p>Hoare’s paper contains some futuristic pseudocode in particular:</p>
<blockquote>
<p>A possible notation for such a type definition was
suggested by Knuth; it is a mixture of BNF (the <code>|</code> symbol) and the
PASCAL definition of a type by enumeration:</p>
<pre><code>type proposition = (prop (letter) | neg (proposition) |
                   conj, disj (proposition, proposition));</code></pre>
<p>(…)
In defining operations on a data structure, it is usually necessary
to enquire which of the various forms the structure takes, and what are
its components. For this, I suggest an elegant notation which has been
implemented by Fred McBride in his pattern-matching LISP. Consider
for example a function intended to count the number of <code>&amp;</code>s contained in
a proposition. (…)</p>
<pre><code>function andcount (p: proposition): integer;
  andcount := cases p of
    (prop(c) → 0|
     neg(q) → andcount(q)|
     conj(q,r) → andcount(q) + andcount(r)+1|
     disj(q,r) → andcount(q) + andcount(r));</code></pre>
</blockquote>
<p>Fred McBride’s pattern-matching LISP is the topic of
<a href="https://personal.cis.strath.ac.uk/conor.mcbride/FVMcB-PhD.pdf">his PhD dissertation</a>.
There is not enough room on this page to write about the groundbreaking history of LISP.</p>
<h3 id="unfree-algebras-in-miranda">Unfree algebras in Miranda</h3>
<p>If algebraic data types are “free algebras”,
one may naturally wonder whether “unfree algebras” have a role to play.
Miranda allows quotienting data type definitions by equations (“laws” or “rewrite rules”).
You could then define the integers like this, with a constructor
to decrement numbers, and equations to reduce integers to a canonical representation:</p>
<pre><code>int ::= Zero | Suc int | Pred int
Suc (Pred n) =&gt; n
Pred (Suc n) =&gt; n</code></pre>
<p>In hindsight this is superfluous, but it’s fun to see this kind of
old experiments in programming languages.
The modern equivalent in Haskell would be to hide the data constructors
and expose smart constructors instead.
There are uses for quotient types in proof assistants and dependently
typed languages, but they work quite differently.</p>
<h2 id="sums-of-products">Sums of products?</h2>
<p>There is another folklore interpretation of “algebraic” in “algebraic data types”
as referring to “sums of products”.</p>
<p>It’s not an uncommon interpretation. In fact, trying to find a source for
this folklore is what got me going on this whole adventure.
The <a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Wikipedia article on algebraic data types</a>
at the time of writing doesn’t outright say it, but it does refer to sums and
products several times while making no mention of free algebras.
Some [citation needed] tags should be sprinkled around.
The Talk page of that article contains an unresolved discussion of this issue, with links to
<a href="https://stackoverflow.com/questions/16770/haskells-algebraic-data-types/5917133#5917133">a highly upvoted SO answer</a>
and <a href="https://stackoverflow.com/questions/5911267/what-are-sums-and-products-data-structures/5914867#5914867">another one</a>
whose references don’t provide first-hand account of the origins of the term.
For sure, following that idea leads to some <a href="http://ozark.hendrix.edu/~yorgey/pub/thesis.pdf">fun combinatorics</a>,
like <a href="https://personal.cis.strath.ac.uk/conor.mcbride/Dissect.pdf">differentiation on data types</a>,
but that doesn’t seem to have been the original meaning of “algebraic data types”.</p>
<p>That interpretation might have been in some people’s mind in the 70s and 80s,
even if only as a funny coincidence, but I haven’t found any written evidence of
it except maybe this one sentence in a later paper,
<a href="https://www.cs.kent.ac.uk/people/staff/dat/tfp12/tfp12.pdf">Some history of programming languages</a> by David Turner (2012):</p>
<blockquote>
The ISWIM paper also has the first appearance of algebraic type definitions
used to define structures. This is done in words, but the sum-of-products idea is
clearly there.
</blockquote>
<p>It’s only a “maybe” because while the phrase “algebraic type” undeniably refers to
sums of products, it’s not clear that the adjective “algebraic” specifically is
meant to be associated with “sum-of-products” in that sentence. We
could replace “algebraic type” with “data type” without changing the meaning
of the sentence.</p>
<h2 id="clear-1979clear">Clear (<a href="https://dl.acm.org/doi/10.5555/647448.727240">1979</a>)</h2>
<p>In contrast, free algebras—or initial algebras as one might prefer to call them—are
a concept from the areas of universal algebra and category theory with
a well-established history in programming language theory by the time
algebraic data types came around, with influential contributions by a certain
<a href="https://www.pls-lab.org/en/ADJ_group">ADJ group</a>;
see for example <a href="https://dl.acm.org/doi/10.1145/321992.321997">Initial algebra semantics and continuous algebras</a>.</p>
<p>Ironically, much related work focused on the other ADT, “abstract data types”.
Using universal algebra as a foundation, a variety of “specification languages” have
been designed for defining algebraic structures, notably the <a href="https://en.wikipedia.org/wiki/OBJ_(programming_language)">OBJ</a> family
of languages created by Joseph Goguen (a member of the aforementioned ADJ group) and others,
and the Clear language by Rod Burstall (of Hope fame) and Joseph Goguen.
Details of the latter can be found in <a href="https://dl.acm.org/doi/10.5555/647448.727240">The Semantics of Clear, a specification language</a>.
(You may remember seeing a mention of Clear earlier
in the quote from The Definition of Standard ML.)</p>
<h3 id="example-theories-in-clear">Example theories in Clear</h3>
<p>Here is the theory of monoids in Clear. It consists of one sort named <code>carrier</code>,
an element (a nullary operation) named <code>empty</code> and a binary operation <code>append</code>.</p>
<pre><code>constant Monoid = theory
                      sorts carrier
                      opns empty : carrier
                           append : carrier,carrier -&gt; carrier
                      eqns all x: carrier . append(x,empty) = x
                           all x: carrier . append(empty,x) = x
                           all x,y,z: carrier . append(append(x,y),z) = append(x,append(y,z))
                  endth</code></pre>
<p>A theory is an interface. Its implementations are called algebras.
In that example, the algebras of “the theory of monoids” are exactly monoids.</p>
<p>In every theory, there is an <em>initial algebra</em> obtained by turning the
operations into constructors (or “uninterpreted operations”), equating elements
(which are trees of constructors) modulo the equations of the theory.
For the example above, the initial monoid is a singleton monoid, with only an empty element
(all occurrences of <code>append</code> are simplified away by the two equations for <code>empty</code>),
which is not very interesting. Better examples are those corresponding to the usual data types.</p>
<p>The booleans can be defined as the initial algebra of the theory with one sort (<code>truthvalue</code>)
and two values of that sort, <code>true</code> and <code>false</code>.</p>
<pre><code>constant Bool = theory data
                    sorts truthvalue
                    opns true,false: truthvalue
                endth</code></pre>
<p>In Clear, the initial algebra is specified by adding the <code>data</code> keyword to a <code>theory</code>.
In the semantics of Clear, rather than thinking in terms of a specific algebra,
a “data theory” is still a theory (an interface),
with additional constraints that encode “initiality”, so the only possible
algebra (implementation) is the initial one.
My guess as to why the concept of data theory is set up that way
is that it allows plain theories and data theories to be combined seamlessly.</p>
<p>The natural numbers are the initial algebra of <code>zero</code> and <code>succ</code>:</p>
<pre><code>constant Nat = theory data
                   sorts nat
                   opns zero: nat
                        succ: nat -&gt; nat
               endth</code></pre>
<p>At this point, the connection between “data theories” in Clear and data types
in Hope and subsequent languages is hopefully clear.</p>
<h3 id="more-substantial-examples-in-clear">More substantial examples in Clear</h3>
<p>Theories can be extended into bigger theories with new sorts, operations, and equations.
Here is an extended theory of booleans with two additional operations <code>not</code>, <code>and</code>,
and their equations. This should demonstrate that, beyond the usual mathematical structures,
we can define non-trivial operations in this language:</p>
<pre><code>constant Bool1 = enrich Bool by
                     opns not: truthvalue -&gt; truthvalue
                          and: truthvalue,truthvalue -&gt; truthvalue
                     eqns all . not true = false
                          all . not false = true
                          all p: truthvalue . and(false, p) = false
                          all p: truthvalue . and(true, p) = p
                 enden</code></pre>
<p>Initial algebras are also called free algebras, but that gets confusing because
“free” is an overloaded word. Earlier for instance, you might have expected the initial
monoid, or “free monoid”, to be the monoid of lists. The monoid of lists is the
initial algebra in a slightly different theory: the theory of monoids with an
embedding from a fixed set of elements <code>A</code>.</p>
<p>We might formalize it as follows in Clear.
The theory <code>List</code> is parameterized by an algebra <code>A</code> of the theory <code>Set</code>,
and its body is the same as <code>Monoid</code>, except that we renamed <code>carrier</code> to <code>list</code>,
we added an <code>embed</code> operation, and we added the <code>data</code> keyword to restrict that
theory to its initial algebra.</p>
<pre><code>constant Set = theory sorts element endth
procedure List(A : Set) = theory data
                              sorts list
                              opns empty : list
                                   append : list,list -&gt; list
                                   embed : element of A -&gt; list
                              eqns all x: list . append(x,empty) = x
                                   all x: list . append(empty,x) = x
                                   all x,y,z: list . append(append(x,y),z) = append(x,append(y,z))
                          endth</code></pre>
<p>One may certainly see a resemblance between theories in Clear, modules in ML,
and object-oriented classes.
It’s always funny to find overlaps between the worlds of functional and
object-oriented programming.</p>
<h2 id="clu-1977clu-intro">CLU (<a href="https://dl.acm.org/doi/10.1145/359763.359789">1977</a>)</h2>
<p>CLU is a programming language created at MIT by Barbara
Liskov and her students in the course of their work on data abstraction.</p>
<p>It features tagged union types, which are called “oneof types”.
(Source: <a href="https://pmg.csail.mit.edu/ftp.lcs.mit.edu/pub/pclu/CLU/3.Documents/MIT-LCS-TR-225.pdf">CLU Reference Manual</a> by Barbara Liskov et al. (1979).)</p>
<pre><code>T = oneof[empty:       null,
          integer:     int,
          real_num:    real,
          complex_num: complex]</code></pre>
<p>Values are constructed by naming the oneof type (either as an identifier bound to it,
or by spelling out the <code>oneof</code> construct) then the tag prefixed by <code>make_</code>:</p>
<pre><code>T$make_integer(42)</code></pre>
<p>The <code>tagcase</code> destructs “oneof” values.</p>
<pre><code>x: oneof[pair: pair, empty: null]
...
tagcase x
    tag empty: return(false)
    tag pair(p: pair): if (p.car = i)
                       then return(true)
                       else x := down(p.cdr)
                       end
end</code></pre>
<p>The main missing feature for parity with algebraic data types is recursive type
definitions, which are not allowed directly. They can be achieved indirectly
though inconveniently through multiple clusters (classes in modern terminology).
(Source: <a href="https://pmg.csail.mit.edu/ftp.lcs.mit.edu/pub/pclu/CLU/3.Documents/clu-history.PDF">A History of CLU</a> by Barbara Liskov (1992).)</p>
<p>Burstall’s papers on Hope and Clear cite CLU, but beyond that it doesn’t
seem easy to make precise claims about the influence of CLU, which is an object-oriented language,
on the evolution of those other declarative languages developed across the pond.</p>]]></summary>
</entry>
<entry>
    <title>Programming Turing machines with regexes</title>
    <link href="https://blog.poisson.chat/posts/2024-06-18-turing-regex.html" />
    <id>https://blog.poisson.chat/posts/2024-06-18-turing-regex.html</id>
    <published>2024-06-18T00:00:00Z</published>
    <updated>2024-06-18T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>Everybody knows that regular expressions are not Turing-complete.
That won’t stop me from doing this.</p>
<p>In a Turing machine, there is a tape and there is a program.
What is the program in a Turing machine? <sup>drumroll 🥁…</sup>
It is a finite-state machine, which is equivalent to a regular expression!</p>
<p>Just for a silly pun, I’m going to introduce this programming language in
an absurd allegory about T-rexes (“Turing regular expressions”, or just “Turing expressions”).</p>
<pre class="toc"><code></code></pre>
<h2 id="tales-of-t-rexes">Tales of T-rexes</h2>
<p>T-rexes are mysterious creatures who speak in tales (Turing expressions)
about one T-rex—usually the speaker. Tales are constructed from symbols for
the operations of a Turing machine, and the standard regex combinators.</p>
<ul>
<li><code>&gt;</code> and <code>&lt;</code> are the simple tales of the T-rex taking a single step to the left or right;</li>
<li><code>0!</code> and <code>1!</code> tell of the T-rex “writing” a <code>0</code> or <code>1</code>;</li>
<li><code>0?</code> and <code>1?</code> tell of the T-rex “observing” a <code>0</code> or <code>1</code>;</li>
<li><code>e₁ ... eₙ</code> is a tale made up of a sequence of <code>n</code> tales,
and there is an empty tale in the case <code>n = 0</code> (“nothing happened”);</li>
<li><code>(e)*</code> says that the tale <code>e</code> happened an arbitrary number of times, possibly zero;</li>
<li><code>(e₁|e₂)</code> says that one of the tales happened, <code>e₁</code> or <code>e₂</code>.</li>
</ul>
<p>As it is a foreign language from a fantasy world, the description above
shouldn’t be taken too literally.
For instance, it can be difficult to imagine a T-rex holding a pen,
much less writing with it. In truth, the actions that the tales <code>0!</code> and <code>1!</code>
describe are varied, and “writing” is only the closest approximation
among the crude words of humans.</p>
<p>Iteration <code>(e)*</code> and choice <code>(e₁|e₂)</code> make T-rex tales nondeterministic:
different sequences of events may be valid interpretations of the same tale.
The observations <code>0?</code> and <code>1?</code> enable us to prune the tree of
possibilities. T-rex communication may be convoluted sometimes,
but at least they mean to convey coherent series of events.</p>
<p>Enough exposition. Let’s meet T-rexes!</p>
<p>Two T-rexes greet us, introducing themselves as Alan and Alonzo.
They invite us for a chat in their home in Jurassic park.</p>
<h3 id="the-tale-of-alonzo">The tale of Alonzo</h3>
<p>While Alan serves tea, Alonzo shows us a mysterious drawing.
T-rex imagery is quite simplistic, owing to their poor vision and
clumsy hands.
We can sort of recognize Alonzo on the left, next to a row of circles and lines:</p>
<pre><code>🦖
0001011000</code></pre>
<p>Then, Alonzo tells us the following tale: “<code>(&gt;)*1?0!&gt;1?0!</code>”.</p>
<p>Noticing our puzzled faces, Alan fetches a small machine from the garage.
It is a machine to interpret T-rex tales in a somewhat visual rendition.
Alan demonstrates how to transcribe the tale that Alonzo told us together with
his drawing into the machine.
Here is the result:</p>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div>
    <code class="expr-src">(&gt;)*1?0!&gt;1?0!</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">0001011000</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">0001010000</code></div>
</div>
<p>Press the <code>Run</code> button to see the machine render the tale.
It happens in the blink of an eye; T-rexes are really fast!
We will break it down step by step in what follows.</p>
<p>(You can also edit the <del>program</del> tale and the <del>input</del>
drawing in these examples and see what happens then.)</p>
<p>Alonzo’s drawing is the scene where the tale takes place.
We ask Alan and Alonzo what “<code>0</code>” and “<code>1</code>” represent,
but we are not fluent enough in T-rex to understand their apparently nuanced answer.
We have no other choice than to make abstraction of it.</p>
<pre><code>🦖          ← Alonzo
0001011000  ← the world</code></pre>
<p>Alonzo first said “<code>(&gt;)*</code>”: he walked toward the right.
As is customary in T-rex discourse, this tale leaves a lot up to interpretation.
There are many possible guesses of how many steps he actually took.
He could even have walked out of the picture!</p>
<pre><code>🦖
0001011000

 🦖
0001011000

  🦖
0001011000

         🦖
0001011000</code></pre>
<p>But then the tale goes “<code>1?</code>”:
Alonzo observed a <code>1</code>, whatever that means, right where he stopped.
That narrows the possibilities down to three:</p>
<pre><code>   🦖
0001011000

     🦖
0001011000

      🦖
0001011000</code></pre>
<p>Afterwards, Alonzo tells us that “<code>0!</code>” happened,
which we think of abstractly as “writing” <code>0</code> where Alonzo is standing.</p>
<pre><code>Before &quot;0!&quot;
   🦖
0001011000

After &quot;0!&quot;
   🦖
0000011000</code></pre>
<p>Each of the three possibilities from earlier after writing <code>0</code>:</p>
<pre><code>   🦖
0000011000

     🦖
0001001000

      🦖
0001010000</code></pre>
<p>Alonzo then made one step to the right, and observed another <code>1</code> (“<code>&gt;1?</code>”).
Only the second possibility above is consistent with that subsequent observation.
Finally, he writes <code>0</code> again.</p>
<pre><code>      🦖
0001000000</code></pre>
<p>And we can see that the outcome matches the machine output above.</p>
<p>After puzzling over it for a while, we start to make sense of
Alonzo’s tale “<code>(&gt;)*1?0!&gt;1?0!</code>”, and imagine this rough translation:
“Funny story. I walked to the right, and I stopped in front of a <code>1</code>,
isn’t that right Alan? I was feeling hungry. So I
ate it. I left nothing! I was still hungry, I am a dinosaur after all,
so I took a step to the right, only to find another <code>1</code>.
Aren’t I lucky? I ate it too. It was super tasty!”</p>
<p>We have a good laugh. Alan and Alonzo share a few more tales.
More tea? Of course.
At their insistence, we try telling some of our own tales, to varied success.
It’s getting late. Thank you for your hospitality. The end.</p>
<h3 id="more-examples">More examples</h3>
<p>Exercise for the interested reader: implement the following operations using Turing expressions.
Here’s a free machine to experiment with:</p>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's empty machine <noscript>(JavaScript is disabled)</noscript></h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src"></code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src"></code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src"></code></div>
</div>
<h4 id="preamble-extra-features-and-clarifications">Preamble: extra features and clarifications</h4>
<p>For convenience, other digits can also be used in programs
(<em>i.e.</em>, <code>2?</code>, <code>2!</code>, <code>3?</code>, <code>3!</code>, up to <code>9?</code> and <code>9!</code> are allowed).
The symbol <code>2</code> will be used to mark the end of a binary input
in the exercises that follow.</p>
<p>Turing expressions are nondeterministic, but the machine only searches for
the first valid execution. The search is biased as follows:
<code>(e₁|e₂)</code> first tries <code>e₁</code> and then, if that fails, <code>e₂</code>;
<code>(e)*</code> is equivalent to <code>(|e(e)*)</code>.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<p>The tape extends infinitely to the left and to the right, initialized with zeroes
outside of the input.
The machine aborts if the tape head (🦖) walks out of the range <code>[-100, 100]</code>.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>
0 is the initial position of the tape head and where the input starts.
The machine prints the first 10 symbols starting at position 0 as the output.</p>
<p>Whitespace is ignored. A <code>#</code> starts a comment up to the end of the line.
What kind of person comments a regex?</p>
<h5 id="determinism">Determinism</h5>
<p>Although Turing expressions are nondeterministic in general,
we obtain a deterministic subset of Turing expressions by requiring
the branching combinators to be guarded.
Don’t allow unrestricted iterations <code>(e)*</code> and choices <code>(e₁|e₂)</code>,
only use while loops <code>(1?e)*0?</code><a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> and conditional statements <code>(0?e₁|1?e₂)</code>.</p>
<h4 id="not">Not</h4>
<p>Flip the bits. 0 becomes 1, 1 becomes 0.</p>
<p>Examples:</p>
<pre><code>Input:  0100110112
Output: 1011001002</code></pre>
<pre><code>Input:  1111111112
Output: 0000000002</code></pre>
<details class="code-details">
<summary>
Solution
</summary>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">((0?1!|1?0!)&gt;)*2?</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">0100110112</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">1011001002</code></div>
</div>
</details>
<h4 id="binary-increment">Binary increment</h4>
<p>Input: binary representation of a natural number n.<br />
Output: binary representation of (n + 1).</p>
<p>Cheatsheet of binary representations:</p>
<pre><code>0: 000
1: 100
2: 010
3: 110
4: 001
5: 101</code></pre>
<p>Examples:</p>
<pre><code>Input:  0010000000
Output: 1010000000</code></pre>
<pre><code>Input:  1110000000
Output: 0001000000</code></pre>
<p>No input delimiters for this exercise.</p>
<details class="code-details">
<summary>
Solution
</summary>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">(1?0!>)*0?1!</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">1110000000</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">0001000000</code></div>
</div>
</details>
<h4 id="left-shift">Left shift</h4>
<p>Move bits to the left.</p>
<p>Examples:</p>
<pre><code>Input:  0100110112
Output: 1001101102</code></pre>
<pre><code>Input:  1111111112
Output: 1111111102</code></pre>
<details class="code-details">
<summary>
Solution
</summary>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">(2?|(0!&gt;0?|1!&gt;1?)*(0!&gt;2?))</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">0100110112</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">1001101112</code></div>
</div>
Thanks to Nathanaëlle Courant for this nice solution!
</details>
<h4 id="right-shift">Right shift</h4>
<p>Move bits to the right.</p>
<p>Examples:</p>
<pre><code>Input:  0100110112
Output: 0010011012</code></pre>
<pre><code>Input:  1111111112
Output: 0111111112</code></pre>
<details class="code-details">
<summary>
Solution
</summary>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">((0?|1?)(0?&gt;)*(2?|1?0!&gt;(1?&gt;)*(2?|0?1!&gt;)))*2?</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">0100110112</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">0010011012</code></div>
</div>
Thanks to Nathanaëlle Courant for this nice solution!
</details>
<h4 id="cumulative-xor">Cumulative xor</h4>
<p>Each bit of the output is the xor of the input bits to the left of it.</p>
<p>Examples:</p>
<pre><code>Input:  0100100012
Output: 0111000012</code></pre>
<pre><code>Input:  1111111112
Output: 1010101012</code></pre>
<details class="code-details">
<summary>
Solution
</summary>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">((0?&gt;)*(1?&gt;((0?1!&gt;)*1?0!|2?)|2?))*2?</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">0100100012</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">0111000012</code></div>
</div>
</details>
<h4 id="unary-subtraction">Unary subtraction</h4>
<p>Input: Two unary numbers x and y, separated by a single 0.<br />
Output: The difference (x - y).</p>
<p>Example: evaluate (5 - 3).</p>
<pre><code>Input:  1111101110
Output: 1100000000</code></pre>
<p>Feel free to add a <code>2</code> to delimit the input.
I only decided to allow symbols other than <code>0</code> and <code>1</code>
after finishing this exercise.</p>
<details class="code-details">
<summary>
Solution
</summary>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">(1?&gt;)*0?&lt;(1?&gt;(0?&gt;)*1?0!&gt;(1?&lt;(0?&lt;)*1?0!&lt;|0?(0?&lt;)*1?0!))*0?&lt;(1?&lt;)*0?&gt;</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">1111101110</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">1100000000</code></div>
</div>
</details>
<p>Example: evaluate (3 - 5).</p>
<pre><code>Input:  1110111110
Output: 0000000110</code></pre>
<p>In my solution, the result -2 is represented by two <code>1</code> placed in the location
of the second argument rather than the first. You may use a different encoding.</p>
<details class="code-details">
<summary>
Solution (bis)
</summary>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">(1?&gt;)*0?&lt;(1?&gt;(0?&gt;)*1?0!&gt;(1?&lt;(0?&lt;)*1?0!&lt;|0?(0?&lt;)*1?0!))*0?&lt;(1?&lt;)*0?&gt;</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">1110111110</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">0000000110</code></div>
</div>
</details>
<details class="code-details">
<summary>
Commented program
</summary>
<p>The lack of delimiters in my version of the problem makes this a bit tricky.</p>
<pre><code>        # Example: evaluate (5 - 3).
        # TAPE: 111110111
        # HEAD: ^
(1?&gt;)*0?
        # TAPE: 111110111
        # HEAD:      ^
        #
        # The following line checks whether
        # the second argument is 0,
        # in which case we will skip the loop.
&gt;(0?&lt;|1?&lt;&lt;)
        # Otherwise we move the head on the last 1
        # of the first argument.
        # TAPE: 111110111
        # HEAD:     ^
        #
        # BEGIN LOOP
        # Loop invariant: the difference between
        # the two numbers on tape is constant.
(1?
        # Go to the first 1 of the second argument.
&gt;(0?&gt;)*1?
        # During the first iteration,
        # the tape looks like this:
        # TAPE: 111110111
        # HEAD:       ^
        #
        # Erase 1 to 0 and move to the right.
 0!&gt;
        # TAPE: 111110011
        # HEAD:        ^
        #
        # Check whether there remains
        # at least one 1 to the right.
        # BEGIN IF
 (1?
        # There is at least one 1 on the right.
        # Move back into the first argument.
  &lt;(0?&lt;)*1?
        # TAPE: 111110011
        # HEAD:     ^
        #
        # Erase 1 to 0. Move left.
  0!&lt;
        # TAPE: 111100011
        # HEAD:    ^
        #
        # If the 1s of the first argument ran out
        # at this point (which would mean
        # first argument &lt; second argument),
        # we will BREAK out of the loop (then terminate),
        # otherwise, CONTINUE, back to the top of loop
 |0?
        # ELSE (second branch of the IF from three lines ago)
        # The 1s of the second argument ran out
        # (which means first argument &gt;= second argument)
        # Tape when we reach this point (in the last iteration):
        # TAPE: 1110000000
        # HEAD:          ^
        #
        # Move back into the first argument.
  (0?&lt;)*1?
        # TAPE: 111000000
        # HEAD:   ^
        #
        # Erase 1 to 0.
  0!
        # TAPE: 110000000
        # HEAD:   ^
        #
        # Reading a 0 will break out of the loop.
        # BREAK
 )
        # END IF
)*0?
        # END LOOP</code></pre>
</details>
<h2 id="the-separation-of-program-and-tape">The separation of program and tape</h2>
<p>Until this point, there may remain misgivings about whether this
is actually “regular expressions”. The syntax is the same, but
is it really the same semantics?
This section spells out a precise alternative definition of Turing machines
with a clear place for the standard semantics of regular expressions
(as regular languages).</p>
<p>Instead of applying regular expressions directly to an input string,
we are using them to describe interactions
between the program and the tape of a Turing machine.
Then the regular expression might as well be the program.</p>
<figure>
<div class="img">
<svg width="400" height="120" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <marker
      id='head'
      orient="auto"
      markerWidth='3'
      markerHeight='4'
      refX='0.1'
      refY='2'
    >
      <path d='M0,0 V4 L2,2 Z' fill="black" />
    </marker>
    <marker
      id='rhead'
      orient="auto-start-reverse"
      markerWidth='3'
      markerHeight='4'
      refX='0.1'
      refY='2'
    >
      <path d='M0,0 V4 L2,2 Z' fill="black" />
    </marker>
  </defs>
  <rect width="400" height="120" x="0" y="0" rx="20" ry="20" fill="lightgreen" />
  <rect width="140" height="80" x="10" y="30" rx="20" ry="20" fill="#1E88E5" />
  <rect width="140" height="80" x="250" y="30" rx="20" ry="20" fill="#FFC107" />
  <text x="30" y="20">Turing machine</text>
  <text x="80" y="70" dominant-baseline="middle" text-anchor="middle">Program</text>
  <text x="320" y="70" dominant-baseline="middle" text-anchor="middle">Tape</text>
  <text x="200" y="30" dominant-baseline="middle" text-anchor="middle">Interactions</text>
  <text x="200" y="50" dominant-baseline="middle" text-anchor="middle"><,>,0!,1!,0?,1?</text>
  <path
    id='arrow-line'
    marker-start='url(#rhead)'
    marker-end='url(#head)'
    stroke-width='3'
    fill='none' stroke='black'
    d='M155,70, 245 70,120'
  />
</svg>
</div>
</figure>
<p>The mechanics of Turing machines are defined traditionally via a transition
relation between states.
A Turing machine state is a pair <span class="math inline">(<em>q</em>, <em>t</em>)</span> of a program state <span class="math inline"><em>q</em> ∈ <em>Q</em></span> (where
<span class="math inline"><em>Q</em></span> is the set of states of a finite-state machine) and a tape state
<span class="math inline"><em>t</em> ∈ 2<sup>ℤ</sup> × ℤ</span>
(the bits on the tape and the position of the read-write head).</p>
<p>That “small-step” formalization of Turing machines is too monolithic for our present
purpose of revealing the regular languages hidden inside Turing machines.
The issue is that the communication between the program and the tape is implicit
in the transition between states as program-tape pairs <span class="math inline">(<em>q</em>, <em>t</em>)</span>.
We will take a more modular approach using <em>trace semantics</em>:
the program and the tape each give rise to traces of interactions which make
explicit the communication between those two components.</p>
<h3 id="the-standard-semantics-of-regular-expressions">The standard semantics of regular expressions</h3>
<p>The raison d’être of regular expressions is to recognize sequences of symbols,
also known as strings, lists, or words. Here, we will refer to them as <em>traces</em>.
Regular expressions are conventionally interpreted as sets of traces, reading
the iteration <code>*</code> and choice <code>|</code> combinators are operations on sets.</p>
<p>Let <span class="math inline"><em>A</em></span> be a set of symbols; in our case
<span class="math inline"><em>A</em> = {<code>&lt;</code>, <code>&gt;</code>, <code>0!</code>, <code>1!</code>, <code>0?</code>, <code>1?</code>}</span> but the
following definition works with any <span class="math inline"><em>A</em></span>. The trace semantics of a regular
expression <code>e</code> over the alphabet <span class="math inline"><em>A</em></span> is defined inductively:</p>
<ul>
<li>An atomic expression <span class="math inline"><code>e</code> ∈ <em>A</em></span> contains a single trace which is just that symbol.
<span class="math display">Trace(<code>e</code>) = {<code>e</code>}   if <code>e</code> ∈ <em>A</em></span></li>
<li>A concatenation of expressions <span class="math inline"><code>e₁</code> … <code>eₙ</code></span> contains concatenations of traces of every <code>eᵢ</code>.
<span class="math display">Trace(<code>e₁</code> … <code>eₙ</code>) = { <em>t</em><sub>1</sub> … <em>t</em><sub><em>n</em></sub> ∣ ∀<em>i</em>, <em>t</em><sub><em>i</em></sub> ∈ Trace(<code>eᵢ</code>) }</span></li>
<li>An iteration <code>(e)*</code> contains all concatenations of traces <span class="math inline"><em>t</em><sub>1</sub> … <em>t</em><sub><em>n</em></sub></span>
such that each subtrace <span class="math inline"><em>t</em><sub><em>i</em></sub></span> is a trace of that same <code>e</code>.
<span class="math display">Trace(<code>(e)*</code>) = { <em>t</em><sub>1</sub> … <em>t</em><sub><em>n</em></sub> ∣ ∀<em>i</em>, <em>t</em><sub><em>i</em></sub> ∈ Trace(<code>e</code>) }</span></li>
<li>A choice <code>(e₁|e₂)</code> contains the union of traces of <code>e₁</code> and <code>e₂</code>.
<span class="math display">Trace(<code>(e₁|e₂)</code>) = Trace(<code>e₁</code>) ∪ Trace(<code>e₂</code>)</span></li>
</ul>
<p>Equivalently, a trace semantics can be viewed as a relation between program and trace.
We write <span class="math inline"><code>e</code> ⊢ <em>t</em></span>, pronounced “<code>e</code> recognizes <span class="math inline"><em>t</em></span>”, as an abbreviation of
<span class="math inline"><em>t</em> ∈ Trace(<code>e</code>)</span>.
This is also for uniformity with the notation in the next section.</p>
<p>A core result of automata theory is that the sets of traces definable by
regular expressions are the same as those definable by finite-state machines.
That led to our remark that Turing machines might as well be Turing regular
expressions.</p>
<h3 id="the-turing-machine-memory-model">The Turing machine memory model</h3>
<p>In the semantics of regular expressions above, the meaning of the symbols
(<code>&lt;</code>, <code>&gt;</code>, etc.) is trivial:
a symbol in a regular expression denotes itself as a singleton trace.
In this section, we will give these symbols their natural meaning as
“operations on a tape”.
The tape is the <em>memory model</em> of Turing machines.
<a href="https://en.wikipedia.org/wiki/Memory_model_(programming)">Memory models</a> are better known in the context of concurrent programming
languages, as they answer the question of how to resolve concurrent writes and
reads.</p>
<p>The tape carries a sequence of symbols extending infinitely in both directions.
A <em>head</em> on the tape reads one symbol at a time, and can move left or right,
one symbol at a time. Addresses on the tape are integers, elements of <span class="math inline">ℤ</span>.
A tape state is a pair <span class="math inline">(<em>m</em>, <em>i</em>) ∈ 2<sup>ℤ</sup> × ℤ</span>:
the memory contents is <span class="math inline"><em>m</em> ∈ 2<sup>ℤ</sup></span> (note <span class="math inline">2<sup>ℤ</sup> = ℤ → {0, 1}</span>)
and the position of the head is <span class="math inline"><em>i</em> ∈ ℤ</span>.
The behavior of the tape is defined as a ternary relation
pronounced “<span class="math inline">(<em>m</em>, <em>i</em>)</span> steps to <span class="math inline">(<em>m</em><sup>′</sup>, <em>i</em><sup>′</sup>)</span> with trace <span class="math inline"><em>t</em></span>”, written:</p>
<p><span class="math display">(<em>m</em>, <em>i</em>) ⇝ (<em>m</em><sup>′</sup>, <em>i</em><sup>′</sup>) ⊢ <em>t</em></span></p>
<p>It is defined by the following rules.
We step left and right by decrementing and incrementing the head position <span class="math inline"><em>i</em></span>.</p>
<p><span class="math display">(<em>m</em>, <em>i</em>) ⇝ (<em>m</em>, <em>i</em> − 1) ⊢ <code>&lt;</code></span>
<span class="math display">(<em>m</em>, <em>i</em>) ⇝ (<em>m</em>, <em>i</em> + 1) ⊢ <code>&gt;</code></span></p>
<p>Writing operations use the notation <span class="math inline"><em>m</em>[<em>i</em> ↦ <em>v</em>]</span> for updating the value
of the tape <span class="math inline"><em>m</em></span> at address <span class="math inline"><em>i</em></span> with <span class="math inline"><em>v</em></span>.</p>
<p><span class="math display">(<em>m</em>, <em>i</em>) ⇝ (<em>m</em>[<em>i</em> ↦ 0], <em>i</em>) ⊢ <code>!0</code></span>
<span class="math display">(<em>m</em>, <em>i</em>) ⇝ (<em>m</em>[<em>i</em> ↦ 1], <em>i</em>) ⊢ <code>!1</code></span></p>
<p>Observations, or assertions, step only when a side condition is satisfied. Otherwise,
the tape is stuck, and that triggers backtracking in the search for a valid trace.</p>
<p><span class="math display">(<em>m</em>, <em>i</em>) ⇝ (<em>m</em>, <em>i</em>) ⊢ <code>?0</code>  if <em>m</em>(<em>i</em>) = 0</span>
<span class="math display">(<em>m</em>, <em>i</em>) ⇝ (<em>m</em>, <em>i</em>) ⊢ <code>?1</code>  if <em>m</em>(<em>i</em>) = 1</span></p>
<p>We close this relation by reflexivity (indexed by the empty trace <span class="math inline"><em>ϵ</em></span>)
and transitivity (indexed by the concatenation of traces).</p>
<p><span class="math display">(<em>m</em>, <em>i</em>) ⇝ (<em>m</em>, <em>i</em>) ⊢ <em>ϵ</em></span>
<span class="math display">(<em>m</em>, <em>i</em>) ⇝ (<em>m</em><sup>′</sup>, <em>i</em><sup>′</sup>) ⊢ <em>t</em> and (<em>m</em><sup>′</sup>, <em>i</em><sup>′</sup>) ⇝ (<em>m</em><sup>″</sup>, <em>i</em><sup>″</sup>) ⊢ <em>t</em><sup>′</sup>  ⇔  (<em>m</em>, <em>i</em>) ⇝ (<em>m</em><sup>″</sup>, <em>i</em><sup>″</sup>) ⊢ <em>t</em> <em>t</em><sup>′</sup></span></p>
<h3 id="turing-regular-expressions">Turing regular expressions</h3>
<p>We now connect programs and tapes together through the trace.
A Turing regular expression <code>e</code> and an initial tape <span class="math inline">(<em>m</em>, 0)</span> step
to a final tape <span class="math inline">(<em>m</em><sup>′</sup>, <em>i</em><sup>′</sup>)</span>, written</p>
<p><span class="math display"><code>e</code>, (<em>m</em>, 0) ⇝ (<em>m</em><sup>′</sup>, <em>i</em><sup>′</sup>)</span></p>
<p>if there exists a trace <span class="math inline"><em>t</em></span> recognized by both the program and the tape:</p>
<p><span class="math display"><code>e</code> ⊢ <em>t</em></span>
<span class="math display">(<em>m</em>, 0) ⇝ (<em>m</em><sup>′</sup>, <em>i</em><sup>′</sup>) ⊢ <em>t</em></span></p>
<p>We can then consider classes of functions computable by Turing expressions via an
encoding of inputs and outputs on the tape.
Let <span class="math inline">encode : ℕ → 2<sup>ℤ</sup></span> be an encoding of natural numbers as tapes.
A Turing expression <code>e</code> computes a function <span class="math inline"><em>f</em> : ℕ → ℕ</span> if, for all <span class="math inline"><em>n</em></span>,
there is exactly one final tape <span class="math inline">(<em>m</em><sup>′</sup>, <em>i</em><sup>′</sup>)</span> such that</p>
<p><span class="math display"><code>e</code>, (encode(<em>n</em>), 0) ⇝ (<em>m</em><sup>′</sup>, <em>i</em><sup>′</sup>)</span></p>
<p>and that unique tape encodes <span class="math inline"><em>f</em>(<em>n</em>)</span>:</p>
<p><span class="math display"><em>m</em><sup>′</sup> = encode(<em>f</em>(<em>n</em>))</span></p>
<p>Et voilà. That’s how we can program Turing machines with regular expressions.</p>
<h2 id="fsm700">Finite-state machines: the next 700 programming languages</h2>
<p>Finite-state machines appear obviously in Turing machines, but you can similarly
view many programming languages in terms of finite-state machines
by reducing the state to just the program counter: “where you are currently in the
source program” can only take finitely many values in a finite program.
From that point of view, all other components of the abstract machine of your favorite
programming language—including the values of local variables—belong to the
“memory” that the program counter interacts with. Why would we do this?
For glory of course. So we can say that most programming languages are
glorified regular expressions.</p>
<p>To be fair, there are exceptions to this idea: cellular automata and homoiconic languages
(<em>i.e.</em>, with the ability to quote and unquote code at will) are those I can think of.
At most there is a boring construction where the finite-state machine writes
the source program to memory then runs a general interpreter on it.</p>
<h2 id="completely-free-from-turing-completeness">Completely free from Turing-completeness</h2>
<p>The theory of formal languages and automata has a ready-made answer about the
expressiveness of regular expressions: regular expressions denote regular languages,
which belong to a lower level of expressiveness than recursively enumerable languages
in the <a href="https://en.wikipedia.org/wiki/Chomsky_hierarchy">Chomsky hierarchy</a>.</p>
<p>What I want to point out is that theory can only ever study “expressiveness” in a
narrow sense. Real expressiveness is fundamentally open-ended: the only limit is
your imagination. Any mathematical definition of “expressiveness” must place
road blocks so that meaningful impossibility theorems can be proved.
The danger is to forget about those road blocks when extrapolating mathematical
theorems into general claims about the usefulness of a programming language.<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a></p>
<p>The expressiveness of formal languages is a delicate idea in that there
are well-established mathematical concepts and theorems about it,
but the rigor of mathematics hides a significant formalization gap between how a theory
measures “expressiveness” and the informal open-ended question of “what can we do with this?”.</p>
<p>“Regular expressions are not Turing-complete” might literally be a
theorem in some textbook; it doesn’t stop regular expressions from also being a
feasible programming language for Turing machines as demonstrated in this post.
Leaving you to come to terms with your own understanding of this paradox,
a closing thought: at the end of the day, science is no slave to mathematics,
we do mathematics in service of science.</p>
<hr />
<h2 id="bonus-track-brainfuck">Bonus track: Brainfuck</h2>
<p>Turing regular expressions look similar to Brainfuck.
Let’s extend the primitives of Turing expressions to be able to compile Brainfuck.</p>
<p>The loop operator <code>[...]</code> in Brainfuck can be written as <code>(0~...)*0?</code>,
with a new operation <code>0~</code> to observe a value not equal to zero.</p>
<p>With <code>+</code> and <code>-</code> (increment and decrement modulo 256) as additional operations
supported by our regular expressions,
a Brainfuck program is compiled to an extended Turing expression
simply by replacing <code>[</code> and <code>]</code> textually with <code>(0~</code> and <code>)*0?</code>.<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a></p>
<p>Interestingly, translating Brainfuck to extended Turing expressions does not use <code>|</code>,
yet Brainfuck is Turing-complete: while loops seem to make conditional
expressions redundant.
(Are Turing expressions without choice <code>(e₁|e₂)</code>
(<em>i.e.</em>, with only <code>&lt;</code>, <code>&gt;</code>, <code>0!</code>, <code>1!</code>, <code>0?</code>, <code>1?</code>, and <code>(e)*</code>) also Turing-complete?)</p>
<p>The machine implemented within this post supports those new constructs:
<code>+</code>, <code>-</code>, <code>0~</code>, <code>1~</code> (also <code>2~</code> to <code>9~</code>, just because; but not more, just because),
and the brackets <code>[</code> and <code>]</code>. You can write code in Brainfuck, and it will be desugared
and interpreted as an extended Turing expression. You can also directly write an extended
Turing expression.</p>
<p>The input can now be a comma-separated list prefixed by a comma (to allow multi-digit numbers).
Example: <code>,1,1,2,3,5,8,13</code>.
Trailing zeroes in the output will not be printed for clarity.</p>
<p>Brainfuck is a high-level programming language compared to Turing expressions.
Being able to increment and decrement numbers makes programming so much less tedious than
explicitly manipulating unary or binary numbers in Turing machines.</p>
<h3 id="small-examples">Small examples</h3>
<p>The idiom <code>[-]</code> zeroes out a number.</p>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">[-]</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">,42</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">,0</code></div>
</div>
<p>Add two numbers.</p>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">&gt;[-&lt;+&gt;]</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">,42,57</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">,99</code></div>
</div>
<h3 id="nondeterministic-brainfuck">Nondeterministic Brainfuck</h3>
<p>Extending Brainfuck with star <code>(e)*</code> and choice <code>(e₁|e₂)</code> equips the language with nondeterminism.</p>
<p>One thing we can do using nondeterminism is to define the inverse of a function
simply by guessing the output <code>y</code>, applying the function to it <code>f(y)</code>,
then checking that it matches the input <code>x</code>, in which case <code>y = f⁻¹(x)</code>.
It’s not efficient, but it’s a general implementation of inverses which may have its uses
in writing formal specifications.</p>
<p>Here’s a roundabout implementation of subtraction (<span class="math inline"><em>x</em> − <em>y</em></span>): guess the answer (call it <span class="math inline"><em>z</em></span>),
add one of the operands to it (<span class="math inline"><em>z</em> + <em>y</em></span>),
and compare the result with the other operand (if <span class="math inline"><em>z</em> + <em>y</em> = <em>x</em></span> then <span class="math inline"><em>z</em> = <em>x</em> − <em>y</em></span>).</p>
<div class="machine machine-src">
  <h6 class="machine-title">Alan's machine</h6>
  <div class="inline"><div class="machine-desc">Program: </div><code class="expr-src">[-&gt;&gt;&gt;+&lt;&lt;&lt;](+&gt;&gt;+&lt;&lt;)*&gt;[-&gt;+&lt;]&gt;[-&gt;-&lt;]&gt;0?</code></div>
  <div class="inline"><div class="machine-desc">Input: </div><code class="tape-src">,10,3</code></div>
  <div class="inline"><div class="machine-desc">Output: </div><code class="output-src">,7</code></div>
</div>
<details class="code-details">
<summary>
Commented program
</summary>
<pre><code># Using four consecutive cells, named A, B, C, D
# Expected result: value of (A - B) placed in A

# D ← A
# A ← 0
[-&gt;&gt;&gt;+&lt;&lt;&lt;]

# A ← GUESS
# C ← A
(+&gt;&gt;+&lt;&lt;)*

# C ← B + C
# B ← 0
&gt;[-&gt;+&lt;]

# D ← D - C
# C ← 0
&gt;[-&gt;-&lt;]

# Assert(D == 0)
&gt;0?</code></pre>
</details>
<template id="template">
<form class="machine">
<h6 class="machine-title">Alan's machine</h6>
<div class="inline">
  <div class="machine-desc">Program: </div>
  <textarea name="expr" cols=50 rows=2></textarea>
</div>
<div class="error expr-error"></div>
<div class="inline">
  <div class="machine-desc">Input: </div>
  <div><input name="tape" type="text"></div>
  <!-- if you move this button, make sure to adapt the run function to find the form element -->
  <button type="button" onclick="run(this)">Run</button>
  <button type="reset">Reset</button>
</div>
<div class="error tape-error"></div>
<div class="inline">
  <div class="machine-desc">Output: </div>
  <input name="output" type="text" readonly>
</div>
<div class="error output-error"></div>
</form>
</template>
<style>
.machine {
    background: lightgreen;
    padding: 0.5em;
    margin-bottom: 1em;
}
.machine-title {
    margin-top: 0;
    font-style: italic;
}
.machine textarea {
    vertical-align: top;
    margin: 0;
}
.machine [name="output"] {
    color: black;
}
.machine textarea, .machine input[type="text"] {
    font-family: monospace;
}
.inline div {
    display: inline-block;
}
.error {
    color: red;
}
.machine-desc {
    font-weight: bold;
    width: 4.5em;
}
</style>
<script src="/js/immutable.min.js"></script>
<script src="/js/2024-06-18-turing-regex-machine.js"></script>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Note that this is the opposite of most real-world regex engines:
<code>*</code> is usually eager (equivalent to <code>(e(e)*|)</code> rather than <code>(|e(e)*)</code>).
The lazy variant is usually written <code>*?</code> and you could add it to
the syntax of Turing regexes if you want.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>It’s very easy to accidentally write an infinite loop; this is a half-assed
safeguard to catch some of them.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>Extra care must be taken when more than two distinct symbols may be
encountered on the tape.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4"><p>Another hot take in the same vein is that the simply-typed lambda
calculus—the simplest <a href="https://www.jucs.org/jucs_10_7/total_functional_programming/jucs_10_07_0751_0768_turner.pdf">total functional programming</a> language—is
Turing-complete:
you can encode Turing machines/general recursive functions/your favorite
Turing-complete gadget by controlling nontermination with fuel,
for a concrete example.
Another idea is that a language where functions terminate
can easily be extended with <a href="https://personal.cis.strath.ac.uk/conor.mcbride/TotallyFree.pdf">nontermination or recursion as an explicit effect</a>.</p>
<p>More generally, <a href="https://proofwiki.org/wiki/Kleene&#39;s_Normal_Form_Theorem">Kleene’s normal form theorem</a> says that if you can
“express” primitive recursion, then you can “express” general recursion.
Some might view this theorem as a counterargument,
pointing to a boundary between “Turing-completeness” (can “express”
general recursion) and “weak Turing-completeness” (can “express” primitive
recursion) which can be made precise.
While I recognize that there is a rich theory behind these concepts,
I rather view Kleene’s normal form theorem as an argument why such a
distinction is too subtle to be relevant to expressiveness in a broad sense
of what we can and cannot do using a programming language.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5"><p>We might even say that Brainfuck can be compiled to regular expressions using
regular expressions. The regular expressions to do those substitutions are
trivial though. Using <code>sed</code>: <code>sed 's/\[/(0~/g;s/\]/)\*0?/g'</code>, where the two actual regular
expressions are <code>\[</code> and <code>\]</code>.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></summary>
</entry>
<entry>
    <title>Abstract nonsense</title>
    <link href="https://blog.poisson.chat/posts/2023-09-05-abstract-nonsense.html" />
    <id>https://blog.poisson.chat/posts/2023-09-05-abstract-nonsense.html</id>
    <published>2023-09-05T00:00:00Z</published>
    <updated>2023-09-05T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>I’ve been reading The Joy of Abstraction, by Eugenia Cheng. Very accessible. Would recommend.
It’s doing good stuff to my mind.</p>
<hr />
<h2 id="abstraction-food-for-thought">Abstraction, food for thought</h2>
<p>Two apples are the same as two apples.</p>
<p>Two apples are not the same as two oranges.</p>
<p>Two ripe apples are not the same as two rotten apples, even though they are both two apples and two apples.</p>
<p>Two fruits are the same as two fruits, even though they could be two apples and two oranges.</p>]]></summary>
</entry>
<entry>
    <title>From delimited continuations to algebraic effects in Haskell</title>
    <link href="https://blog.poisson.chat/posts/2023-01-02-del-cont-examples.html" />
    <id>https://blog.poisson.chat/posts/2023-01-02-del-cont-examples.html</id>
    <published>2023-01-02T00:00:00Z</published>
    <updated>2023-01-02T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>The upcoming version of GHC will feature primitives for
<a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0313-delimited-continuation-primops.rst">delimited continuations</a>. Let’s put them to use and build a bare bones algebraic effect
system.</p>
<h2 id="algebraic-effects">Algebraic effects</h2>
<p>In Haskell, different sorts of effectful computations can be expressed using monads.
Monads for individual effects are fairly well understood. The challenge now is
to combine many different effects. Applications manage many kinds of resources
(files, network, databases…), handle many types of errors, and run in
different environments (production vs testing with mock components).
Can that be done while maintaining a reasonable level of separation of concerns?</p>
<p>Currently, a common approach is to use monad transformers and type classes (<em>mtl</em>-style).
But when you have a big stack of monad transformers, it may not be easy to even
understand what <code>(&gt;&gt;=)</code> does, because its behavior arises from the composition
of all of these transformers. So the actual control flow of the program is
opaque to us, which can be an obstacle to locating errors and guaranteeing
performance.</p>
<p>Algebraic effects are another approach to combine effects.
Whereas with transformers, every monad transformer must be defined from
scratch, algebraic effects start from a few core primitives: you have one
(parameterized) monad with abilities to “call” an operation and to “handle”
such calls. The hope is that those core primitives:</p>
<ol type="1">
<li>are simple to implement and to optimize;</li>
<li>make it easy to define and reason about effects,
in terms of both behavior and performance.</li>
</ol>
<p>Until now, algebraic effect systems in Haskell used free monads or the
continuation monad. Continuations were emulated as closures; this comes
with a level of indirection whose cost is difficult to mitigate.
The newly implemented
<a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0313-delimited-continuation-primops.rst">delimited continuations primops</a>
let us directly manipulate native continuations.</p>
<p>This post uses delimited continuations to implement programs with various effects.
The usual culprits:</p>
<ul>
<li><a href="#exceptions">Exceptions</a></li>
<li><a href="#output">Output</a></li>
<li><a href="#combining-exceptions-and-output">Combining exceptions and output</a></li>
<li><a href="#input">Input</a></li>
<li><a href="#streaming">Combining input and output: streaming</a></li>
<li><a href="#real-world">Interacting with the real world</a></li>
<li><a href="#state">State</a></li>
<li><a href="#nondeterminism">Nondeterminism</a></li>
<li><a href="#concurrency">Concurrency</a></li>
</ul>
<p>The example programs leveraging this mini effect library will look like your
standard-fare monadic code. What makes them interesting is that, operationally,
they are all in the <code>IO</code> monad. Unlike with monad transformers, adding a new
effect does not change the underlying monad, so code that doesn’t use that
effect does not pay a price for it. Another notable consequence is that
“unlifting” abstractions like <code>UnliftIO</code> or <code>MonadBaseControl</code> are no longer
relevant: there is nothing to “unlift” if you never leave <code>IO</code>.</p>
<p>The abstraction layer of algebraic effects over continuations is so thin that I
just use <code>prompt</code> and <code>control0</code> directly, but the bits that are “operations”
and the bits that are “handlers” are clearly identifiable. The system
implemented here is untyped as far as effects are concerned, but features
named handlers as a mitigating alternative;
a complete effect system which would keep track of what operations each
computation may call and would provide safe primitives to define new effects is
left as an exercise for the reader.</p>
<p>This post is written in Literate Haskell (<a href="https://gitlab.com/lysxia/blog.poisson.chat/-/blob/1ad376570de24798cca8171cf96f52b7231d58b5/posts/2023-01-02-del-cont-examples.lhs">source code</a>).
It can be compiled using the development version of GHC (or GHC 9.6 if it has been released).</p>
<pre><code>$ ghc 2023-01-02-del-cont-examples.lhs -main-is DelContExamples.main -o run-tests
$ ./run-tests
All tests passed!</code></pre>
<details class="code-details">
<summary>
Extensions and imports
</summary>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ot">  BangPatterns,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="ot">  BlockArguments,</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="ot">  DerivingStrategies,</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="ot">  GADTs,</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="ot">  GeneralizedNewtypeDeriving,</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="ot">  MagicHash,</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="ot">  UnboxedTuples #-}</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">DelContExamples</span> <span class="kw">where</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Control.Exception</span> <span class="kw">as</span> <span class="dt">E</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Exception.Base</span> (<span class="dt">NoMatchingContinuationPrompt</span>(..))</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Either</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Foldable</span> (<span class="fu">for_</span>)</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Functor</span> (<span class="fu">void</span>)</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Functor.Sum</span> (<span class="dt">Sum</span>(..))</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Maybe</span> (<span class="fu">fromMaybe</span>, <span class="fu">maybe</span>)</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">System.IO.Unsafe</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">System.Environment</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">GHC.Exts</span> (<span class="dt">PromptTag</span>#, <span class="fu">newPromptTag</span>#, <span class="fu">prompt</span>#, <span class="fu">control0</span>#)</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">GHC.IO</span> (<span class="dt">IO</span>(..))</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">GHC.Stack</span> (<span class="dt">HasCallStack</span>)</span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Prelude</span> <span class="kw">hiding</span> (<span class="fu">log</span>)</span></code></pre></div>
</details>
<h2 id="the-mother-of-all-monads">The mother of all monads</h2>
<p>Capturing continuations is the power of the continuation monad,
in which we can embed all other monads. It’s
<a href="http://blog.sigfpe.com/2008/12/mother-of-all-monads.html">the mother of all monads.</a></p>
<p><code>Mom</code> is defined identically to <code>IO</code>, but its only operations are the new
delimited continuation primitives.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Mom</span> <span class="fu">a</span> <span class="ot">=</span> <span class="dt">Mom</span> (<span class="dt">IO</span> <span class="fu">a</span>)</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> <span class="kw">newtype</span> (<span class="dt">Functor</span>, <span class="dt">Applicative</span>, <span class="dt">Monad</span>)</span></code></pre></div>
<p>The available operations wrap the RTS primitives <code>newPromptTag#</code>,
<code>prompt#</code> and <code>control0#</code>.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Unsafe primitives</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">PromptTag</span> <span class="fu">a</span> <span class="ot">=</span> <span class="dt">PromptTag</span> (<span class="dt">PromptTag</span><span class="op">#</span> <span class="fu">a</span>)</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="fu">newPromptTag</span> <span class="ot">::</span> <span class="dt">Mom</span> (<span class="dt">PromptTag</span> <span class="fu">a</span>)</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="fu">newPromptTag</span> <span class="ot">=</span> <span class="dt">Mom</span> (<span class="dt">IO</span> (<span class="op">\</span><span class="fu">s</span> <span class="ot">-&gt;</span> <span class="kw">case</span> <span class="fu">newPromptTag</span><span class="op">#</span> <span class="fu">s</span> <span class="kw">of</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>  (<span class="op">#</span> <span class="fu">s&#39;</span>, <span class="fu">tag</span> <span class="op">#</span>) <span class="ot">-&gt;</span> (<span class="op">#</span> <span class="fu">s&#39;</span>, <span class="dt">PromptTag</span> <span class="fu">tag</span> <span class="op">#</span>)))</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="fu">prompt</span> <span class="ot">::</span> <span class="dt">PromptTag</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="fu">prompt</span> (<span class="dt">PromptTag</span> <span class="fu">tag</span>) (<span class="dt">Mom</span> (<span class="dt">IO</span> <span class="fu">m</span>)) <span class="ot">=</span> <span class="dt">Mom</span> (<span class="dt">IO</span> (<span class="fu">prompt</span><span class="op">#</span> <span class="fu">tag</span> <span class="fu">m</span>))</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="fu">control0</span> <span class="ot">::</span> <span class="dt">PromptTag</span> <span class="fu">a</span> <span class="ot">-&gt;</span> ((<span class="dt">Mom</span> <span class="fu">b</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">b</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="fu">control0</span> (<span class="dt">PromptTag</span> <span class="fu">tag</span>) <span class="fu">f</span> <span class="ot">=</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Mom</span> (<span class="dt">IO</span> (<span class="fu">control0</span><span class="op">#</span> <span class="fu">tag</span> (<span class="op">\</span><span class="fu">k</span> <span class="ot">-&gt;</span> <span class="kw">case</span> <span class="fu">f</span> (<span class="op">\</span>(<span class="dt">Mom</span> (<span class="dt">IO</span> <span class="fu">a</span>)) <span class="ot">-&gt;</span> <span class="dt">Mom</span> (<span class="dt">IO</span> (<span class="fu">k</span> <span class="fu">a</span>))) <span class="kw">of</span> <span class="dt">Mom</span> (<span class="dt">IO</span> <span class="fu">b</span>) <span class="ot">-&gt;</span> <span class="fu">b</span>)))</span></code></pre></div>
<p>The boxing of the continuation <code>k</code> in <code>control0</code> could be avoided by
introducing a new type for continuations, replacing <code>(Mom b -&gt; Mom a)</code>.
I’m not sure whether there is much to gain from that optimization.
I leave it like this for simplicity.</p>
<h3 id="prompt-and-control0-goto-with-extra-steps"><code>prompt</code> and <code>control0</code>, “goto” with extra steps?</h3>
<p>When a function terminates normally, it returns its result to its caller,
its predecessor in the call stack. <code>prompt</code> lets you prepare another return point
earlier in the call stack, and <code>control0</code> returns to that point. What happens
to all the stack frames that were skipped that way? They are copied to the heap so they
can be restored later.</p>
<p>In more concrete terms, when you call <code>control0 t f :: Mom b</code>, the caller expects a
result of some type <code>b</code>. It is assumed that you have previously set up a
<code>prompt t :: Mom a -&gt; Mom a</code> in the call stack with the same tag <code>t :: PromptTag a</code>.
The slice of the stack up to that <code>prompt t</code> is unwinded and stored as a function
<code>continue :: Mom b -&gt; Mom a</code> (<code>IO b -&gt; IO a</code>).
<code>prompt t</code> is popped off the stack, and the program carries on as <code>f continue</code>.</p>
<p>It sounds completely insane the first time you learn about it,
it’s like “goto” with extra steps. And yet, when you get down to it, delimited
continuations have rather clean semantics, both operationally and
denotationally.
The implementation was a surprisingly small change in GHC.</p>
<blockquote>
<p>The changes required to implement <code>prompt#</code> and <code>control0#</code> are relatively minimal.
They only impact the RTS, and they do not require any changes to existing
functionality. Though capturing portions of the RTS stack may seem like a
radical proposition, GHC actually already does it when raising an asynchronous
exception to avoid the need to duplicate work for any blackholed thunks. In
fact, getting that right is significantly more subtle than implementing
<code>control0#</code>, which is quite straightforward in comparison.</p>
— <a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0313-delimited-continuation-primops.rst">The GHC Proposal</a>
</blockquote>
<p>The richness of continuations, both theoretically and practically, suggests that these
control operators are not as arbitrary as they seem.</p>
<h3 id="effectful-code-pure-semantics">Effectful code, pure semantics</h3>
<p>The code in this post can be split in two levels. Library-level code uses the delimited
continuation primitives to implement effects—operations and handlers, and user-level
code uses those effects in example programs.
Without direct access to delimited continuations, user-level code cannot
observe any mutation, so it will be safe to use the following pure <code>run</code>
function.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Look Ma&#39;, no IO!</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="fu">run</span> <span class="ot">::</span> <span class="dt">Mom</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="fu">a</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="fu">run</span> (<span class="dt">Mom</span> <span class="fu">m</span>) <span class="ot">=</span> <span class="fu">unsafePerformIO</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>  (<span class="fu">E.catch</span> (<span class="dt">Just</span> <span class="op">&lt;$&gt;</span> <span class="fu">m</span>) <span class="op">\</span><span class="dt">NoMatchingContinuationPrompt</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="dt">Nothing</span>)</span></code></pre></div>
<p>Hiding the delimited continuations primitives avoids the danger of duplicating
and observing the creation of fresh <code>PromptTag</code>s in a pure context.
Some partiality remains (<code>Maybe</code>) due to potentially mismatched
<code>control0#</code> calls. Such errors would be prevented by a type system for effects,
which is beyond the scope of this post.</p>
<h3 id="further-reading">Further reading</h3>
<p>On <code>prompt#</code>, <code>control0#</code>, and <code>newPromptTag#</code>:</p>
<ul>
<li><a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0313-delimited-continuation-primops.rst">The GHC proposal: Delimited continuations primops;</a></li>
<li><a href="https://github.com/ghc-proposals/ghc-proposals/pull/313#issuecomment-590075484">Comment by <span class="citation" data-cites="tomjaguarpaw">@tomjaguarpaw</span> in the discussion of the GHC proposal</a> illustrating the semantics of the primops;</li>
<li><a href="https://gist.github.com/lexi-lambda/d97b8187a9b63619af29689e9fa1b880">Gist by <span class="citation" data-cites="lexi-lambda">@lexi-lambda</span></a> with
background on reduction semantics and continuations;</li>
<li><a href="https://legacy.cs.indiana.edu/~dyb/pubs/monadicDC.pdf"><em>A Monadic Framework for Delimited Continuations</em></a> by Kent Dybvig, Simon Peyton Jones, and Amr Sabry (JFP 2007).</li>
<li><a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/7942">The patch implementing the proposal</a>.</li>
</ul>
<p>On the continuation monad:</p>
<ul>
<li><a href="https://blog.poisson.chat/posts/2019-10-26-reasonable-continuations.html">The reasonable effectiveness of the continuation monad.</a></li>
<li><a href="https://dl.acm.org/doi/10.1145/143165.143169">The essence of functional programming</a>,
by Philip Wadler (POPL 1992).</li>
</ul>
<h2 id="exceptions">Exceptions</h2>
<p>To begin, let’s implement exceptions using delimited continuations.
This effect has an operation <code>throw</code> and a handler <code>catch</code>.</p>
<h3 id="operation">Operation</h3>
<p>We first declare the uninterpreted operation <code>Throw</code> as a constructor
in a functor. The parameter <code>a</code> is ignored by exceptions; it will be
used by other effects.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Exception</span> <span class="fu">e</span> <span class="fu">a</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Throw</span> <span class="fu">e</span></span></code></pre></div>
<p>We wrap this constructor in a user-facing function <code>throw</code>.
Every <code>throw</code> should have a matching <code>catch</code>, and we ensure this
by requiring a <code>tag</code> that identifies the corresponding <code>catch</code>.
The exact type of <code>tag</code> will be revealed in a moment.
<code>control0</code> uses that <code>tag</code> to look up the matching <code>catch</code> in the call stack,
and returns to it with the exception <code>e</code> wrapped in <code>Throw</code>.
The underscore is the continuation, which is the slice of the stack below the
<code>catch</code>, which is thus discarded.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="fu">throw</span> <span class="ot">::</span> <span class="dt">Exception</span> <span class="fu">e</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="fu">e</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="fu">throw</span> <span class="fu">tag</span> <span class="fu">e</span> <span class="ot">=</span> <span class="fu">control0</span> <span class="fu">tag</span> <span class="op">\</span><span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Op</span> (<span class="dt">Throw</span> <span class="fu">e</span>))</span></code></pre></div>
<h3 id="handler">Handler</h3>
<p>The type of <code>catch</code> should also look familiar, with the novelty that the
handled computation <code>f</code> expects a tag—so that it may call <code>throw</code>.
In <code>catch f onThrow</code>, a fresh <code>tag</code> is generated, then
<code>f tag</code> either (1) returns normally, and its result is wrapped in <code>Pure a</code>,
or (2) <code>f tag</code> throws an exception wrapped in <code>Op (Throw e)</code>.
We then return the result or apply the handler <code>onThrow</code> accordingly.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="fu">catch</span> <span class="ot">::</span> (<span class="dt">Exception</span> <span class="fu">e</span> <span class="op">%</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> (<span class="fu">e</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="fu">catch</span> <span class="fu">f</span> <span class="fu">onThrow</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">tag</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> (<span class="fu">f</span> <span class="fu">tag</span>)</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">action</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">tag</span> (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">action</span>)</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Throw</span> <span class="fu">e</span>) <span class="ot">-&gt;</span> <span class="fu">onThrow</span> <span class="fu">e</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="fu">a</span></span></code></pre></div>
<p>You might have guessed that the <code>Exception e % a</code> tag is just a <code>PromptTag</code>.
More surprisingly, the tag index involves a free monad.
For exceptions, <code>Free (Exception e) a</code> is equivalent to <code>Either e a</code>:
we expect the computation under <code>prompt</code> to produce either an exception <code>e</code> or
a result <code>a</code>. More generally, for an effect expressed as a functor <code>f</code>,
things will be set up exactly so that handlers will be matching on a
computation/tree of type <code>Free f r</code>.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="fu">f</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">=</span> <span class="dt">PromptTag</span> (<span class="dt">Free</span> <span class="fu">f</span> <span class="fu">r</span>)</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Free</span> <span class="fu">f</span> <span class="fu">r</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Op</span> (<span class="fu">f</span> (<span class="dt">Free</span> <span class="fu">f</span> <span class="fu">r</span>))</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Pure</span> <span class="fu">r</span></span></code></pre></div>
<p>Using <code>catch</code>, we can implement <code>try</code>.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="fu">try</span> <span class="ot">::</span> (<span class="dt">Exception</span> <span class="fu">e</span> <span class="op">%</span> <span class="dt">Either</span> <span class="fu">e</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Mom</span> (<span class="dt">Either</span> <span class="fu">e</span> <span class="fu">a</span>)</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="fu">try</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">catch</span> (<span class="op">\</span><span class="fu">tag</span> <span class="ot">-&gt;</span> <span class="dt">Right</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">tag</span>) (<span class="op">\</span><span class="fu">e</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Left</span> <span class="fu">e</span>))</span></code></pre></div>
<p>The explicit tags serve as a form of <em>capabilities</em>, handles that functions
take as explicit arguments, granting the permission to use the associated
effects. This partly makes up for the lack of effect typing.
It’s not watertight: you can easily capture the tag to call <code>throw</code> outside of
<code>try</code>/<code>catch</code>. But from a non-adversarial perspective, this mechanism may
prevent quite a few mistakes.</p>
<h3 id="test">Test</h3>
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testThrow</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testThrow</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> (<span class="fu">isRight&#39;</span> (<span class="fu">run</span> (<span class="fu">try</span> (<span class="op">\</span><span class="fu">_</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="st">&quot;Result&quot;</span>))))</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> (<span class="fu">isLeft&#39;</span>  (<span class="fu">run</span> (<span class="fu">try</span> (<span class="op">\</span><span class="fu">exc</span> <span class="ot">-&gt;</span> <span class="fu">throw</span> <span class="fu">exc</span> <span class="st">&quot;Error&quot;</span>))))</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">isRight&#39;</span> <span class="ot">=</span> <span class="fu">maybe</span> <span class="dt">False</span> <span class="fu">isRight</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>  <span class="fu">isLeft&#39;</span> <span class="ot">=</span> <span class="fu">maybe</span> <span class="dt">False</span> <span class="fu">isLeft</span></span></code></pre></div>
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Minimalistic unit testing framework</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="fu">assert</span> <span class="ot">::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">Bool</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="fu">assert</span> <span class="dt">True</span> <span class="ot">=</span> <span class="fu">pure</span> ()</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a><span class="fu">assert</span> <span class="dt">False</span> <span class="ot">=</span> <span class="fu">error</span> <span class="st">&quot;Assertion failed&quot;</span></span></code></pre></div>
<h2 id="output">Output</h2>
<p>Algebraic effects are also known as “resumable exceptions”, extending
exceptions with the ability to continue the computation right where
the exception was thrown.</p>
<p>The next simplest effect after exceptions is to produce some output.
Like <code>Throw</code>, we represent the <code>Output</code> operation as a constructor,
containing the value to output, and now also a continuation.</p>
<h3 id="operation-1">Operation</h3>
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Out</span> <span class="fu">o</span> <span class="fu">a</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Output</span> <span class="fu">o</span> (<span class="dt">Mom</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) </span></code></pre></div>
<p>The <code>output</code> wrapper is similar to <code>throw</code>, additionally storing the
continuation in the <code>Output</code> constructor.
The expected argument of the continuation <code>continue</code> is a computation which is
to replace the operation call.
When we call <code>output o :: Mom ()</code>, that call “bubbles
up” like an exception, gets caught by a handler, and the call gets replaced by
<code>pure ()</code> or some other <code>Mom ()</code> computation.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="fu">output</span> <span class="ot">::</span> <span class="dt">Out</span> <span class="fu">o</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="fu">o</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="fu">output</span> <span class="fu">tag</span> <span class="fu">o</span> <span class="ot">=</span> <span class="fu">control0</span> <span class="fu">tag</span> <span class="op">\</span><span class="fu">continue</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Op</span> (<span class="dt">Output</span> <span class="fu">o</span> <span class="fu">continue</span>))</span></code></pre></div>
<p>A synonym specialized to strings.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="fu">log</span> <span class="ot">::</span> <span class="dt">Out</span> <span class="dt">String</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="fu">log</span> <span class="ot">=</span> <span class="fu">output</span></span></code></pre></div>
<h3 id="example">Example</h3>
<p>An infinite output stream of the Fibonacci sequence.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="fu">fibonacci</span> <span class="ot">::</span> <span class="dt">Out</span> <span class="dt">Int</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="fu">fibonacci</span> <span class="fu">out</span> <span class="ot">=</span> <span class="fu">fib</span> <span class="dv">0</span> <span class="dv">1</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">fib</span> <span class="op">!</span><span class="fu">a</span> <span class="op">!</span><span class="fu">b</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>      <span class="fu">output</span> <span class="fu">out</span> <span class="fu">a</span></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>      <span class="fu">fib</span> <span class="fu">b</span> (<span class="fu">a</span> <span class="op">+</span> <span class="fu">b</span>)</span></code></pre></div>
<h3 id="handler-1">Handler</h3>
<p>Run a computation lazily and collect its output in a list.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="fu">collect</span> <span class="ot">::</span> (<span class="dt">Out</span> <span class="fu">o</span> <span class="op">%</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()) <span class="ot">-&gt;</span> [<span class="fu">o</span>]</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="fu">collect</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">runList</span> <span class="kw">do</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">tag</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">tag</span>)</span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">action</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">tag</span> <span class="fu">action</span></span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Output</span> <span class="fu">o</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span></span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a>        <span class="fu">pure</span> (<span class="fu">o</span> <span class="op">:</span> <span class="fu">runList</span> (<span class="fu">handle</span> <span class="fu">tag</span> (<span class="fu">continue</span> (<span class="fu">pure</span> ()))))</span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> () <span class="ot">-&gt;</span> <span class="fu">pure</span> []</span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a>  <span class="fu">runList</span> <span class="ot">=</span> <span class="fu">fromMaybe</span> [] <span class="op">.</span> <span class="fu">run</span></span></code></pre></div>
<h3 id="test-1">Test</h3>
<div class="sourceCode" id="cb18"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testFibonacci</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testFibonacci</span> <span class="ot">=</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> (<span class="fu">take</span> <span class="dv">8</span> (<span class="fu">collect</span> <span class="fu">fibonacci</span>)</span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>          <span class="op">=</span><span class="ot">=</span> [<span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">5</span>, <span class="dv">8</span>, <span class="dv">13</span>])</span></code></pre></div>
<h2 id="combining-exceptions-and-output">Combining exceptions and output</h2>
<h3 id="example-1">Example</h3>
<p>The big selling point of algebraic effects is that effects can be
combined smoothly. We can thus use <code>log</code> to trace the
execution flow of a program using <code>throw</code> and <code>catch</code>
without further ceremony.</p>
<p>This looks like your usual monadic program. The point is that everything lives
in the same monad <code>Mom</code> (which is operationally equal to <code>IO</code>),
so you do not have to worry about “lifting” or “unlifting” anything through a
transformer: the semantics of <code>(&gt;&gt;=)</code> do not change with every new effect, and
there isn’t the problem that “lifting” <code>catch</code> and other operations that are
actually handlers is counter-intuitive for many transformers, if possible at all.
To be fair, there remain
<a href="https://github.com/hasura/eff/issues/12">difficulties</a> in this area even with
algebraic effects.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="fu">tracedCatch</span> <span class="ot">::</span> <span class="dt">Out</span> <span class="dt">String</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="dt">Bool</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="fu">tracedCatch</span> <span class="fu">out</span> <span class="ot">=</span> <span class="fu">catch</span> <span class="fu">this</span> <span class="fu">onThrow</span> </span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">this</span> <span class="fu">exc</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>    <span class="fu">log</span> <span class="fu">out</span> <span class="st">&quot;Start&quot;</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a>    <span class="fu">_</span> <span class="ot">&lt;-</span> <span class="fu">throw</span> <span class="fu">exc</span> <span class="st">&quot;Boom&quot;</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">log</span> <span class="fu">out</span> <span class="st">&quot;This is unreachable&quot;</span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a>    <span class="fu">pure</span> <span class="dt">False</span></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>  <span class="fu">onThrow</span> <span class="fu">msg</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a>    <span class="fu">log</span> <span class="fu">out</span> (<span class="st">&quot;Error: &quot;</span> <span class="op">++</span> <span class="fu">msg</span>)</span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a>    <span class="fu">pure</span> <span class="dt">True</span></span></code></pre></div>
<h3 id="test-2">Test</h3>
<div class="sourceCode" id="cb20"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testTracedCatch</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testTracedCatch</span> <span class="ot">=</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> (<span class="fu">collect</span> (<span class="fu">void</span> <span class="op">.</span> <span class="fu">tracedCatch</span>) <span class="op">=</span><span class="ot">=</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>    [ <span class="st">&quot;Start&quot;</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;Error: Boom&quot;</span> ])</span></code></pre></div>
<h3 id="silent-handler">Silent handler</h3>
<p>There can also be different ways of handling an effect.
The following handler discards output instead of collecting it,
for example to ignore debugging logs.</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="fu">discardOutput</span> <span class="ot">::</span> (<span class="dt">Out</span> <span class="fu">o</span> <span class="op">%</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="fu">discardOutput</span> <span class="fu">f</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">tag</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">tag</span>)</span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">action</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">tag</span> <span class="fu">action</span></span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Output</span> <span class="fu">_o</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span> <span class="fu">handle</span> <span class="fu">tag</span> (<span class="fu">continue</span> (<span class="fu">pure</span> ()))</span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="fu">a</span></span></code></pre></div>
<div class="sourceCode" id="cb22"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testDiscard</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testDiscard</span> <span class="ot">=</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> (<span class="fu">run</span> (<span class="fu">discardOutput</span> <span class="fu">tracedCatch</span>) <span class="op">=</span><span class="ot">=</span> <span class="dt">Just</span> <span class="dt">True</span>)</span></code></pre></div>
<h2 id="input">Input</h2>
<p>Dually, there is an effect to request some input.</p>
<h3 id="operation-2">Operation</h3>
<div class="sourceCode" id="cb23"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">In</span> <span class="fu">i</span> <span class="fu">a</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Input</span> (<span class="dt">Mom</span> <span class="fu">i</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) </span></code></pre></div>
<p>The <code>input</code> call is expected to return a result <code>i</code>. As before, the type of the
<code>input _</code> operation must coincide with the domain <code>Mom i</code> of the continuation.</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="fu">input</span> <span class="ot">::</span> <span class="dt">In</span> <span class="fu">i</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">i</span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a><span class="fu">input</span> <span class="fu">tag</span> <span class="ot">=</span> <span class="fu">control0</span> <span class="fu">tag</span> <span class="op">\</span><span class="fu">continue</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Op</span> (<span class="dt">Input</span> <span class="fu">continue</span>))</span></code></pre></div>
<h3 id="example-2">Example</h3>
<p>Output the cumulative sum of an input stream.
Like <code>fibonacci</code>, this is an infinite loop in <code>IO</code>.
It gets broken by <code>control0</code> in <code>input</code>.
Until now, an infinite loop in <code>IO</code> would either have to be broken by an
exception (which makes it not actually infinite), or have to involve
concurrency.</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="fu">csum</span> <span class="ot">::</span> <span class="dt">In</span> <span class="dt">Int</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Out</span> <span class="dt">Int</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a><span class="fu">csum</span> <span class="fu">inp</span> <span class="fu">out</span> <span class="ot">=</span> <span class="fu">go</span> <span class="dv">0</span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">go</span> <span class="op">!</span><span class="fu">acc</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>      <span class="fu">n</span> <span class="ot">&lt;-</span> <span class="fu">input</span> <span class="fu">inp</span></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a>      <span class="kw">let</span> <span class="fu">acc&#39;</span> <span class="ot">=</span> <span class="fu">acc</span> <span class="op">+</span> <span class="fu">n</span></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a>      <span class="fu">output</span> <span class="fu">out</span> <span class="fu">acc&#39;</span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a>      <span class="fu">go</span> <span class="fu">acc&#39;</span></span></code></pre></div>
<h3 id="handler-2">Handler</h3>
<p>Supply a list of inputs and stop when we run out.</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="fu">listInput</span> <span class="ot">::</span> [<span class="fu">i</span>] <span class="ot">-&gt;</span> (<span class="dt">In</span> <span class="fu">i</span> <span class="op">%</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Mom</span> (<span class="dt">Maybe</span> <span class="fu">a</span>)</span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a><span class="fu">listInput</span> <span class="fu">is</span> <span class="fu">f</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">tag</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">catch</span> (<span class="op">\</span><span class="fu">exc</span> <span class="ot">-&gt;</span> <span class="fu">handle</span> <span class="fu">exc</span> <span class="fu">tag</span> <span class="fu">is</span> (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">tag</span>))</span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a>    (<span class="op">\</span>() <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="dt">Nothing</span>)</span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">exc</span> <span class="fu">tag</span> <span class="fu">is</span> <span class="fu">action</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">tag</span> <span class="fu">action</span></span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Input</span> <span class="fu">continue</span>)</span>
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> <span class="fu">i</span> <span class="op">:</span> <span class="fu">is&#39;</span> <span class="ot">&lt;-</span> <span class="fu">is</span> <span class="ot">-&gt;</span> <span class="fu">handle</span> <span class="fu">exc</span> <span class="fu">tag</span> <span class="fu">is&#39;</span> (<span class="fu">continue</span> (<span class="fu">pure</span> <span class="fu">i</span>))</span>
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">-&gt;</span> <span class="fu">handle</span> <span class="fu">exc</span> <span class="fu">tag</span> [] (<span class="fu">continue</span> (<span class="fu">throw</span> <span class="fu">exc</span> ()))</span>
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Just</span> <span class="fu">a</span>)</span></code></pre></div>
<h3 id="test-3">Test</h3>
<div class="sourceCode" id="cb27"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testCsum</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testCsum</span> <span class="ot">=</span></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> ((<span class="fu">collect</span> <span class="op">\</span><span class="fu">out</span> <span class="ot">-&gt;</span></span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a>           <span class="fu">void</span> <span class="op">$</span> <span class="fu">listInput</span> [<span class="dv">1</span> <span class="op">..</span> <span class="dv">5</span>] <span class="op">\</span><span class="fu">inp</span> <span class="ot">-&gt;</span></span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a>           <span class="fu">csum</span> <span class="fu">inp</span> <span class="fu">out</span></span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a>          ) <span class="op">=</span><span class="ot">=</span> [<span class="dv">1</span>, <span class="dv">3</span>, <span class="dv">6</span>, <span class="dv">10</span>, <span class="dv">15</span>])</span></code></pre></div>
<h2 id="streaming">Combining input and output: streaming</h2>
<p>The input and output effect can be combined in a streaming fashion,
alternating execution between the consumer and the producer.</p>
<h3 id="handler-3">Handler</h3>
<p>Feed the output of one computation into the input of the other.
Terminate whenever one side terminates, discarding the other.</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="fu">connect</span> <span class="ot">::</span> (<span class="dt">Out</span> <span class="fu">x</span> <span class="op">%</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> (<span class="dt">In</span> <span class="fu">x</span> <span class="op">%</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a><span class="fu">connect</span> <span class="fu">producer</span> <span class="fu">consumer</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">out</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">inp</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handleI</span> <span class="fu">out</span> <span class="fu">inp</span> (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">producer</span> <span class="fu">out</span>) (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">consumer</span> <span class="fu">inp</span>)</span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handleI</span> <span class="fu">out</span> <span class="fu">inp</span> <span class="fu">produce</span> <span class="fu">consume</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">inp</span> <span class="fu">consume</span></span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb28-10"><a href="#cb28-10" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Input</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span> <span class="fu">handleO</span> <span class="fu">out</span> <span class="fu">inp</span> <span class="fu">produce</span> <span class="fu">continue</span></span>
<span id="cb28-11"><a href="#cb28-11" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="fu">a</span></span>
<span id="cb28-12"><a href="#cb28-12" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handleO</span> <span class="fu">out</span> <span class="fu">inp</span> <span class="fu">produce</span> <span class="fu">consuming</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb28-13"><a href="#cb28-13" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">out</span> <span class="fu">produce</span></span>
<span id="cb28-14"><a href="#cb28-14" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb28-15"><a href="#cb28-15" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Output</span> <span class="fu">o</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span></span>
<span id="cb28-16"><a href="#cb28-16" aria-hidden="true" tabindex="-1"></a>        <span class="fu">handleI</span> <span class="fu">out</span> <span class="fu">inp</span> (<span class="fu">continue</span> (<span class="fu">pure</span> ())) (<span class="fu">consuming</span> (<span class="fu">pure</span> <span class="fu">o</span>))</span>
<span id="cb28-17"><a href="#cb28-17" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="fu">a</span></span></code></pre></div>
<h3 id="test-4">Test</h3>
<p>Connect two copies of the cumulative sum process: compute the cumulative sum
of the cumulative sum.</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="fu">csum2</span> <span class="ot">::</span> <span class="dt">In</span> <span class="dt">Int</span> <span class="op">%</span> () <span class="ot">-&gt;</span> <span class="dt">Out</span> <span class="dt">Int</span> <span class="op">%</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a><span class="fu">csum2</span> <span class="fu">inp</span> <span class="fu">out</span> <span class="ot">=</span> <span class="fu">connect</span> (<span class="op">\</span><span class="fu">out&#39;</span> <span class="ot">-&gt;</span> <span class="fu">csum</span> <span class="fu">inp</span> <span class="fu">out&#39;</span>) (<span class="op">\</span><span class="fu">inp&#39;</span> <span class="ot">-&gt;</span> <span class="fu">csum</span> <span class="fu">inp&#39;</span> <span class="fu">out</span>)</span></code></pre></div>
<div class="sourceCode" id="cb30"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testConnect</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testConnect</span> <span class="ot">=</span></span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> ((<span class="fu">collect</span> <span class="op">\</span><span class="fu">out</span> <span class="ot">-&gt;</span></span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a>           <span class="fu">void</span> <span class="op">$</span> <span class="fu">listInput</span> [<span class="dv">1</span> <span class="op">..</span> <span class="dv">5</span>] <span class="op">\</span><span class="fu">inp</span> <span class="ot">-&gt;</span></span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a>           <span class="fu">csum2</span> <span class="fu">inp</span> <span class="fu">out</span></span>
<span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a>          ) <span class="op">=</span><span class="ot">=</span> [<span class="dv">1</span>, <span class="dv">4</span>, <span class="dv">10</span>, <span class="dv">20</span>, <span class="dv">35</span>])</span></code></pre></div>
<h2 id="real-world">Interacting with the real world</h2>
<p>What sets <code>IO</code> apart from <code>ST</code> and <code>Mom</code> is that it can change the world.
We can define handlers to send output and receive input from the real world.
The result of these handlers must be in <code>IO</code>.</p>
<h3 id="printing-output">Printing output</h3>
<p>Text output can be printed to <code>stdout</code>.</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="fu">printOutput</span> <span class="ot">::</span> (<span class="dt">Out</span> <span class="dt">String</span> <span class="op">%</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()) <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a><span class="fu">printOutput</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">momToIO</span> <span class="kw">do</span></span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">tag</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">tag</span>)</span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">action</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">tag</span> <span class="fu">action</span></span>
<span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Output</span> <span class="fu">o</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="kw">do</span></span>
<span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a>        <span class="fu">putStrLn</span> <span class="fu">o</span></span>
<span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a>        <span class="fu">momToIO</span> (<span class="fu">handle</span> <span class="fu">tag</span> (<span class="fu">continue</span> (<span class="fu">pure</span> ())))</span>
<span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> () <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="fu">pure</span> ())</span>
<span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a>  <span class="fu">momToIO</span> <span class="ot">=</span> <span class="fu">fromMaybe</span> (<span class="fu">pure</span> ()) <span class="op">.</span> <span class="fu">run</span></span></code></pre></div>
<h3 id="reading-input">Reading input</h3>
<p>We can forward input from <code>stdin</code> into a
consumer computation.</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="fu">readInput</span> <span class="ot">::</span> (<span class="dt">In</span> <span class="dt">String</span> <span class="op">%</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()) <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a><span class="fu">readInput</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">momToIO</span> <span class="kw">do</span></span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">tag</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">tag</span>)</span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">action</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">tag</span> <span class="fu">action</span></span>
<span id="cb32-8"><a href="#cb32-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb32-9"><a href="#cb32-9" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Input</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="kw">do</span></span>
<span id="cb32-10"><a href="#cb32-10" aria-hidden="true" tabindex="-1"></a>        <span class="fu">i</span> <span class="ot">&lt;-</span> <span class="fu">getLine</span></span>
<span id="cb32-11"><a href="#cb32-11" aria-hidden="true" tabindex="-1"></a>        <span class="fu">momToIO</span> (<span class="fu">handle</span> <span class="fu">tag</span> (<span class="fu">continue</span> (<span class="fu">pure</span> <span class="fu">i</span>)))</span>
<span id="cb32-12"><a href="#cb32-12" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> () <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="fu">pure</span> ())</span>
<span id="cb32-13"><a href="#cb32-13" aria-hidden="true" tabindex="-1"></a>  <span class="fu">momToIO</span> <span class="ot">=</span> <span class="fu">fromMaybe</span> (<span class="fu">pure</span> ()) <span class="op">.</span> <span class="fu">run</span></span></code></pre></div>
<p>A drawback of this implementation is that for a computation that features both
input and output, these handlers are awkward to compose.
We can coerce <code>IO</code> to <code>Mom</code> so <code>readInput</code> can be composed with <code>printOutput</code>,
but that is a hacky solution that makes the type <code>Mom</code> a lie (it’s not supposed
to have side effects). A better solution may be to combine effects before
interpreting them in <code>IO</code> all at once.</p>
<h2 id="state">State</h2>
<p>No effect tutorial would be complete without the state effect.</p>
<h3 id="operations">Operations</h3>
<div class="sourceCode" id="cb33"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">State</span> <span class="fu">s</span> <span class="fu">a</span></span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Get</span> (<span class="dt">Mom</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>)</span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Put</span> <span class="fu">s</span> (<span class="dt">Mom</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>)</span></code></pre></div>
<div class="sourceCode" id="cb34"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="fu">get</span> <span class="ot">::</span> <span class="dt">State</span> <span class="fu">s</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">s</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a><span class="fu">get</span> <span class="fu">tag</span> <span class="ot">=</span> <span class="fu">control0</span> <span class="fu">tag</span> <span class="op">\</span><span class="fu">continue</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Op</span> (<span class="dt">Get</span> <span class="fu">continue</span>))</span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a><span class="fu">put</span> <span class="ot">::</span> <span class="dt">State</span> <span class="fu">s</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a><span class="fu">put</span> <span class="fu">tag</span> <span class="fu">s</span> <span class="ot">=</span> <span class="fu">control0</span> <span class="fu">tag</span> <span class="op">\</span><span class="fu">continue</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Op</span> (<span class="dt">Put</span> <span class="fu">s</span> <span class="fu">continue</span>))</span></code></pre></div>
<h3 id="handler-4">Handler</h3>
<p>State-passing, no mutation.</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="fu">runState</span> <span class="ot">::</span> <span class="fu">s</span> <span class="ot">-&gt;</span> (<span class="dt">State</span> <span class="fu">s</span> <span class="op">%</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Mom</span> (<span class="fu">s</span>, <span class="fu">a</span>)</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a><span class="fu">runState</span> <span class="fu">s0</span> <span class="fu">f</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">tag</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">s0</span> (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">tag</span>)</span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">s</span> <span class="fu">action</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">tag</span> <span class="fu">action</span></span>
<span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Get</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span> <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">s</span> (<span class="fu">continue</span> (<span class="fu">pure</span> <span class="fu">s</span>))</span>
<span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Put</span> <span class="fu">s&#39;</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span> <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">s&#39;</span> (<span class="fu">continue</span> (<span class="fu">pure</span> ()))</span>
<span id="cb35-11"><a href="#cb35-11" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="fu">s</span>, <span class="fu">a</span>)</span></code></pre></div>
<h3 id="example-3">Example</h3>
<div class="sourceCode" id="cb36"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="fu">incr</span> <span class="ot">::</span> <span class="dt">State</span> <span class="dt">Int</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a><span class="fu">incr</span> <span class="fu">st</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">n</span> <span class="ot">&lt;-</span> <span class="fu">get</span> <span class="fu">st</span></span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">put</span> <span class="fu">st</span> (<span class="fu">n</span> <span class="op">+</span> <span class="dv">1</span>)</span></code></pre></div>
<p>Again, combining state with logging is effortless, because
effects live in the same underlying monad.</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="fu">logState</span> <span class="ot">::</span> <span class="dt">Out</span> <span class="dt">String</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">State</span> <span class="dt">Int</span> <span class="op">%</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a><span class="fu">logState</span> <span class="fu">out</span> <span class="fu">st</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">n</span> <span class="ot">&lt;-</span> <span class="fu">get</span> <span class="fu">st</span></span>
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">log</span> <span class="fu">out</span> (<span class="fu">show</span> <span class="fu">n</span>)</span></code></pre></div>
<div class="sourceCode" id="cb38"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a><span class="fu">incr2</span> <span class="ot">::</span> <span class="dt">Out</span> <span class="dt">String</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">State</span> <span class="dt">Int</span> <span class="op">%</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a><span class="fu">incr2</span> <span class="fu">out</span> <span class="fu">st</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">incr</span> <span class="fu">st</span></span>
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">logState</span> <span class="fu">out</span> <span class="fu">st</span></span>
<span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">incr</span> <span class="fu">st</span></span>
<span id="cb38-6"><a href="#cb38-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">logState</span> <span class="fu">out</span> <span class="fu">st</span></span></code></pre></div>
<h3 id="test-5">Test</h3>
<div class="sourceCode" id="cb39"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testState</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testState</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> ((<span class="fu">collect</span> <span class="op">\</span><span class="fu">out</span> <span class="ot">-&gt;</span> <span class="fu">runState</span> <span class="dv">0</span> (<span class="fu">incr2</span> <span class="fu">out</span>) <span class="op">*&gt;</span> <span class="fu">pure</span> ()) <span class="op">=</span><span class="ot">=</span> [<span class="st">&quot;1&quot;</span>, <span class="st">&quot;2&quot;</span>])</span>
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> (<span class="fu">run</span> (<span class="fu">discardOutput</span> <span class="op">\</span><span class="fu">out</span> <span class="ot">-&gt;</span> <span class="fu">runState</span> <span class="dv">0</span> (<span class="fu">incr2</span> <span class="fu">out</span>)) <span class="op">=</span><span class="ot">=</span> <span class="dt">Just</span> (<span class="dv">2</span>, ()))</span></code></pre></div>
<h2 id="nondeterminism">Nondeterminism</h2>
<p>The examples above are quite sequential in nature.
<code>Mom</code> can also replace the list monad.</p>
<h3 id="operation-3">Operation</h3>
<p>Choose one element in a list.</p>
<div class="sourceCode" id="cb40"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Nondet</span> <span class="fu">a</span> <span class="kw">where</span></span>
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Choose</span> <span class="ot">::</span> [<span class="fu">x</span>] <span class="ot">-&gt;</span> (<span class="dt">Mom</span> <span class="fu">x</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Nondet</span> <span class="fu">a</span></span></code></pre></div>
<div class="sourceCode" id="cb41"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a><span class="fu">choose</span> <span class="ot">::</span> <span class="dt">Nondet</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> [<span class="fu">x</span>] <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">x</span></span>
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a><span class="fu">choose</span> <span class="fu">tag</span> <span class="fu">xs</span> <span class="ot">=</span> <span class="fu">control0</span> <span class="fu">tag</span> <span class="op">\</span><span class="fu">continue</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Op</span> (<span class="dt">Choose</span> <span class="fu">xs</span> <span class="fu">continue</span>))</span></code></pre></div>
<h3 id="example-4">Example</h3>
<div class="sourceCode" id="cb42"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a><span class="fu">nameTheorems</span> <span class="ot">::</span> <span class="dt">Nondet</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="dt">String</span></span>
<span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a><span class="fu">nameTheorems</span> <span class="fu">nd</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb42-3"><a href="#cb42-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">name1</span> <span class="ot">&lt;-</span> <span class="fu">choose</span> <span class="fu">nd</span> [<span class="st">&quot;Church&quot;</span>, <span class="st">&quot;Curry&quot;</span>]</span>
<span id="cb42-4"><a href="#cb42-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">name2</span> <span class="ot">&lt;-</span> <span class="fu">choose</span> <span class="fu">nd</span> [<span class="st">&quot;Turing&quot;</span>, <span class="st">&quot;Howard&quot;</span>]</span>
<span id="cb42-5"><a href="#cb42-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">result</span> <span class="ot">&lt;-</span> <span class="fu">choose</span> <span class="fu">nd</span> [<span class="st">&quot;thesis&quot;</span>, <span class="st">&quot;isomorphism&quot;</span>]</span>
<span id="cb42-6"><a href="#cb42-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">pure</span> (<span class="fu">name1</span> <span class="op">++</span> <span class="st">&quot;-&quot;</span> <span class="op">++</span> <span class="fu">name2</span> <span class="op">++</span> <span class="st">&quot; &quot;</span> <span class="op">++</span> <span class="fu">result</span>)</span></code></pre></div>
<h3 id="handler-5">Handler</h3>
<p>Use the output effect to stream all results of a nondeterministic computation.
Here, the continuation is not used linearly: it is called once for every
element in the given list.</p>
<div class="sourceCode" id="cb43"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a><span class="fu">enumerate</span> <span class="ot">::</span> (<span class="dt">Nondet</span> <span class="op">%</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>) <span class="ot">-&gt;</span> <span class="dt">Out</span> <span class="fu">a</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb43-2"><a href="#cb43-2" aria-hidden="true" tabindex="-1"></a><span class="fu">enumerate</span> <span class="fu">f</span> <span class="fu">out</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb43-3"><a href="#cb43-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">tag</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb43-4"><a href="#cb43-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> (<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">tag</span>)</span>
<span id="cb43-5"><a href="#cb43-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb43-6"><a href="#cb43-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">action</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb43-7"><a href="#cb43-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">tag</span> <span class="fu">action</span></span>
<span id="cb43-8"><a href="#cb43-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb43-9"><a href="#cb43-9" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Choose</span> <span class="fu">xs</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span> <span class="fu">for_</span> <span class="fu">xs</span> (<span class="fu">handle</span> <span class="fu">tag</span> <span class="op">.</span> <span class="fu">continue</span> <span class="op">.</span> <span class="fu">pure</span>)</span>
<span id="cb43-10"><a href="#cb43-10" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">output</span> <span class="fu">out</span> <span class="fu">a</span></span></code></pre></div>
<h3 id="test-6">Test</h3>
<div class="sourceCode" id="cb44"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testEnumerate</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testEnumerate</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb44-3"><a href="#cb44-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> (<span class="fu">collect</span> (<span class="fu">enumerate</span> <span class="fu">nameTheorems</span>) <span class="op">=</span><span class="ot">=</span></span>
<span id="cb44-4"><a href="#cb44-4" aria-hidden="true" tabindex="-1"></a>    [ <span class="st">&quot;Church-Turing thesis&quot;</span></span>
<span id="cb44-5"><a href="#cb44-5" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;Church-Turing isomorphism&quot;</span></span>
<span id="cb44-6"><a href="#cb44-6" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;Church-Howard thesis&quot;</span></span>
<span id="cb44-7"><a href="#cb44-7" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;Church-Howard isomorphism&quot;</span></span>
<span id="cb44-8"><a href="#cb44-8" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;Curry-Turing thesis&quot;</span></span>
<span id="cb44-9"><a href="#cb44-9" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;Curry-Turing isomorphism&quot;</span></span>
<span id="cb44-10"><a href="#cb44-10" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;Curry-Howard thesis&quot;</span></span>
<span id="cb44-11"><a href="#cb44-11" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;Curry-Howard isomorphism&quot;</span></span>
<span id="cb44-12"><a href="#cb44-12" aria-hidden="true" tabindex="-1"></a>    ])</span></code></pre></div>
<h2 id="concurrency">Concurrency</h2>
<p>Earlier, the streaming handler <code>connect</code> interleaved execution of one consumer
and one producer thread. Here is a cooperative concurrency effect that lets us
dynamically fork any number of threads and interleave them.</p>
<h3 id="operations-1">Operations</h3>
<div class="sourceCode" id="cb45"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Conc</span> <span class="fu">a</span></span>
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Fork</span> (<span class="dt">Mom</span> ()) (<span class="dt">Mom</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>)</span>
<span id="cb45-3"><a href="#cb45-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Yield</span> (<span class="dt">Mom</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> <span class="fu">a</span>)</span></code></pre></div>
<p>Fork a thread to run the given computation.</p>
<div class="sourceCode" id="cb46"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a><span class="fu">fork</span> <span class="ot">::</span> <span class="dt">Conc</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a><span class="fu">fork</span> <span class="fu">tag</span> <span class="fu">thread</span> <span class="ot">=</span> <span class="fu">control0</span> <span class="fu">tag</span> <span class="op">\</span><span class="fu">continue</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Op</span> (<span class="dt">Fork</span> <span class="fu">thread</span> <span class="fu">continue</span>))</span></code></pre></div>
<p>Cooperative concurrency: threads must yield explicitly.</p>
<div class="sourceCode" id="cb47"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a><span class="fu">yield</span> <span class="ot">::</span> <span class="dt">Conc</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a><span class="fu">yield</span> <span class="fu">tag</span> <span class="ot">=</span> <span class="fu">control0</span> <span class="fu">tag</span> <span class="op">\</span><span class="fu">continue</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> (<span class="dt">Op</span> (<span class="dt">Yield</span> <span class="fu">continue</span>))</span></code></pre></div>
<h3 id="example-5">Example</h3>
<p>A thread that repeats an output value three times.</p>
<div class="sourceCode" id="cb48"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a><span class="fu">simpleThread</span> <span class="ot">::</span> <span class="dt">Out</span> <span class="dt">String</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Conc</span> <span class="op">%</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a><span class="fu">simpleThread</span> <span class="fu">out</span> <span class="fu">conc</span> <span class="fu">n</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">log</span> <span class="fu">out</span> (<span class="fu">show</span> <span class="fu">n</span>)</span>
<span id="cb48-4"><a href="#cb48-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">yield</span> <span class="fu">conc</span></span>
<span id="cb48-5"><a href="#cb48-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">log</span> <span class="fu">out</span> (<span class="fu">show</span> <span class="fu">n</span>)</span>
<span id="cb48-6"><a href="#cb48-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">yield</span> <span class="fu">conc</span></span>
<span id="cb48-7"><a href="#cb48-7" aria-hidden="true" tabindex="-1"></a>  <span class="fu">log</span> <span class="fu">out</span> (<span class="fu">show</span> <span class="fu">n</span>)</span>
<span id="cb48-8"><a href="#cb48-8" aria-hidden="true" tabindex="-1"></a>  <span class="fu">yield</span> <span class="fu">conc</span></span></code></pre></div>
<p>Interleave <code>111</code>, <code>222</code>, <code>333</code>.</p>
<div class="sourceCode" id="cb49"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a><span class="fu">interleave123</span> <span class="ot">::</span> <span class="dt">Out</span> <span class="dt">String</span> <span class="op">%</span> <span class="fu">r</span> <span class="ot">-&gt;</span> <span class="dt">Conc</span> <span class="op">%</span> <span class="fu">s</span> <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a><span class="fu">interleave123</span> <span class="fu">out</span> <span class="fu">conc</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb49-3"><a href="#cb49-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">fork</span> <span class="fu">conc</span> (<span class="fu">simpleThread</span> <span class="fu">out</span> <span class="fu">conc</span> <span class="dv">1</span>)</span>
<span id="cb49-4"><a href="#cb49-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">fork</span> <span class="fu">conc</span> (<span class="fu">simpleThread</span> <span class="fu">out</span> <span class="fu">conc</span> <span class="dv">2</span>)</span>
<span id="cb49-5"><a href="#cb49-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">fork</span> <span class="fu">conc</span> (<span class="fu">simpleThread</span> <span class="fu">out</span> <span class="fu">conc</span> <span class="dv">3</span>)</span></code></pre></div>
<h3 id="handler-6">Handler</h3>
<p>A round-robin scheduler. <code>handle</code> keeps track of a queue of threads.
It runs the first thread until the next event. If the thread yields,
its continuation is pushed to the end of the queue. If the thread
forks another thread, the forked thread is pushed to the end of the queue,
and we continue in the main thread (forking does not yield).
If the thread terminates, we remove it from the queue.</p>
<div class="sourceCode" id="cb50"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a><span class="fu">runConc</span> <span class="ot">::</span> (<span class="dt">Conc</span> <span class="op">%</span> () <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()) <span class="ot">-&gt;</span> <span class="dt">Mom</span> ()</span>
<span id="cb50-2"><a href="#cb50-2" aria-hidden="true" tabindex="-1"></a><span class="fu">runConc</span> <span class="fu">f</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb50-3"><a href="#cb50-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">tag</span> <span class="ot">&lt;-</span> <span class="fu">newPromptTag</span></span>
<span id="cb50-4"><a href="#cb50-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> [<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">f</span> <span class="fu">tag</span>]</span>
<span id="cb50-5"><a href="#cb50-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb50-6"><a href="#cb50-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> [] <span class="ot">=</span> <span class="fu">pure</span> ()</span>
<span id="cb50-7"><a href="#cb50-7" aria-hidden="true" tabindex="-1"></a>  <span class="fu">handle</span> <span class="fu">tag</span> (<span class="fu">thread</span> <span class="op">:</span> <span class="fu">threads</span>) <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb50-8"><a href="#cb50-8" aria-hidden="true" tabindex="-1"></a>    <span class="fu">next</span> <span class="ot">&lt;-</span> <span class="fu">prompt</span> <span class="fu">tag</span> <span class="fu">thread</span></span>
<span id="cb50-9"><a href="#cb50-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> <span class="fu">next</span> <span class="kw">of</span></span>
<span id="cb50-10"><a href="#cb50-10" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Yield</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span> <span class="fu">handle</span> <span class="fu">tag</span> (<span class="fu">threads</span> <span class="op">++</span> [<span class="fu">continue</span> (<span class="fu">pure</span> ())])</span>
<span id="cb50-11"><a href="#cb50-11" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Op</span> (<span class="dt">Fork</span> <span class="fu">th</span> <span class="fu">continue</span>) <span class="ot">-&gt;</span> <span class="fu">handle</span> <span class="fu">tag</span> (<span class="fu">continue</span> (<span class="fu">pure</span> ()) <span class="op">:</span> <span class="fu">threads</span> <span class="op">++</span> [<span class="dt">Pure</span> <span class="op">&lt;$&gt;</span> <span class="fu">th</span>])</span>
<span id="cb50-12"><a href="#cb50-12" aria-hidden="true" tabindex="-1"></a>      <span class="dt">Pure</span> () <span class="ot">-&gt;</span> <span class="fu">handle</span> <span class="fu">tag</span> <span class="fu">threads</span></span></code></pre></div>
<h3 id="test-7">Test</h3>
<div class="sourceCode" id="cb51"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a><span class="fu">testInterleave</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb51-2"><a href="#cb51-2" aria-hidden="true" tabindex="-1"></a><span class="fu">testInterleave</span> <span class="ot">=</span></span>
<span id="cb51-3"><a href="#cb51-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">assert</span> ((<span class="fu">collect</span> <span class="op">\</span><span class="fu">out</span> <span class="ot">-&gt;</span> <span class="fu">runConc</span> <span class="op">\</span><span class="fu">conc</span> <span class="ot">-&gt;</span> <span class="fu">interleave123</span> <span class="fu">out</span> <span class="fu">conc</span>)</span>
<span id="cb51-4"><a href="#cb51-4" aria-hidden="true" tabindex="-1"></a>          <span class="op">=</span><span class="ot">=</span> [<span class="st">&quot;1&quot;</span>, <span class="st">&quot;2&quot;</span>, <span class="st">&quot;3&quot;</span>, <span class="st">&quot;1&quot;</span>, <span class="st">&quot;2&quot;</span>, <span class="st">&quot;3&quot;</span>, <span class="st">&quot;1&quot;</span>, <span class="st">&quot;2&quot;</span>, <span class="st">&quot;3&quot;</span>])</span></code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>Primitive delimited continuation in Haskell give us the power to jump around
the stack to implement many kinds of effects. Under the hood, those operations
live in the IO monad, grounding effectful code in a familiar execution model.</p>
<p>For those new to the topic, I hope that these examples may serve as a good
starting point to experiment with delimited continuations and algebraic effects
in Haskell.</p>
<p>The system implemented here is as rudimentary as it gets.
To define new effects and handlers, we use the new primitives directly, which
is dangerous. This was deliberate to provide material to familiarize oneself
with those primitives.
Moreover, on the one hand, a type system to keep track of the scope of
delimited continuations is a non-trivial ask. On the other hand, the examples
here all follow a regular structure, so there is probably a way to encapsulate
the primitives, trading off some expressiveness for a safe interface
to define new effects and handlers.</p>
<p>Named handlers—via prompt tags—occupy an interesting spot in the scale of
safety guarantees. It is imperfect, even very easy to circumvent. But if you’re
not working against it, it is still a neat way to prevent simple mistakes.
This system can be reinforced further using rank-2 polymorphism,
a technique described in:</p>
<ul>
<li><a href="https://dl.acm.org/doi/10.1145/3563289"><em>First-Class Names for Effect Handlers</em></a>,
Ningning Xie, Youyou Cong, Kazuki Ikemori, Daan Leijen (OOPSLA 2022).</li>
</ul>
<p>Interestingly, prompt tags were not part of the original proposal, and
they are not used by <a href="https://github.com/lexi-lambda/eff"><em>eff</em></a>, the effect
system which gave rise to Alexis King’s GHC proposal. Prompt tags were added during
the feedback process to make the primitives type-safe by default.</p>
<p>Now is an exciting time for algebraic effects/delimited continuations,
as they are making their way into industrial languages:
<a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0313-delimited-continuation-primops.rst">Haskell</a>,
<a href="https://discuss.ocaml.org/t/ocaml-5-0-0-is-out/10974">OCaml</a>,
<a href="https://wasmfx.dev/">WebAssembly</a>.</p>
<hr />
<h2 id="all-of-this-is-executable">All of this is executable</h2>
<div class="sourceCode" id="cb52"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a><span class="fu">main</span> <span class="ot">::</span> <span class="dt">IO</span> ()</span>
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a><span class="fu">main</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb52-3"><a href="#cb52-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">testThrow</span></span>
<span id="cb52-4"><a href="#cb52-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">testFibonacci</span></span>
<span id="cb52-5"><a href="#cb52-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">testTracedCatch</span></span>
<span id="cb52-6"><a href="#cb52-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">testDiscard</span></span>
<span id="cb52-7"><a href="#cb52-7" aria-hidden="true" tabindex="-1"></a>  <span class="fu">testCsum</span></span>
<span id="cb52-8"><a href="#cb52-8" aria-hidden="true" tabindex="-1"></a>  <span class="fu">testConnect</span></span>
<span id="cb52-9"><a href="#cb52-9" aria-hidden="true" tabindex="-1"></a>  <span class="fu">testState</span></span>
<span id="cb52-10"><a href="#cb52-10" aria-hidden="true" tabindex="-1"></a>  <span class="fu">testEnumerate</span></span>
<span id="cb52-11"><a href="#cb52-11" aria-hidden="true" tabindex="-1"></a>  <span class="fu">testInterleave</span></span>
<span id="cb52-12"><a href="#cb52-12" aria-hidden="true" tabindex="-1"></a>  <span class="fu">putStrLn</span> <span class="st">&quot;All tests passed!&quot;</span></span></code></pre></div>]]></summary>
</entry>
<entry>
    <title>The quantified constraint trick</title>
    <link href="https://blog.poisson.chat/posts/2022-09-21-quantified-constraint-trick.html" />
    <id>https://blog.poisson.chat/posts/2022-09-21-quantified-constraint-trick.html</id>
    <published>2022-09-21T00:00:00Z</published>
    <updated>2022-09-21T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>My favorite Haskell trick is how to use quantified constraints with type families.
<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/14860#note_188736">Kudos to <em>Iceland_jack</em> for coming up with it.</a></p>
<h2 id="quantified-constraints-and-type-families">Quantified constraints and type families</h2>
<p><code>QuantifiedConstraints</code> is an extension from GHC 8.6 that lets us
use <code>forall</code> in constraints.</p>
<p>It lets us express constraints for instances of higher-kinded types like <code>Fix</code>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Fix</span> <span class="fu">f</span> <span class="ot">=</span> <span class="dt">Fix</span> (<span class="fu">f</span> (<span class="dt">Fix</span> <span class="fu">f</span>))</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">deriving</span> <span class="kw">instance</span> (<span class="kw">forall</span> <span class="fu">a</span><span class="op">.</span> <span class="dt">Eq</span> <span class="fu">a</span> <span class="ot">=&gt;</span> <span class="dt">Eq</span> (<span class="fu">f</span> <span class="fu">a</span>)) <span class="ot">=&gt;</span> <span class="dt">Eq</span> (<span class="dt">Fix</span> <span class="fu">f</span>)</span></code></pre></div>
<p>Other solutions existed previously, but they’re less elegant:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">deriving</span> <span class="kw">instance</span> <span class="dt">Eq</span> (<span class="fu">f</span> (<span class="dt">Fix</span> <span class="fu">f</span>)) <span class="ot">=&gt;</span> <span class="dt">Eq</span> (<span class="dt">Fix</span> <span class="fu">f</span>)</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Eq1</span> <span class="fu">f</span> <span class="ot">=&gt;</span> <span class="dt">Eq</span> (<span class="dt">Fix</span> <span class="fu">f</span>) <span class="kw">where</span> <span class="op">...</span></span></code></pre></div>
<p>It also lets us say that a monad transformer indeed transforms monads:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> (<span class="kw">forall</span> <span class="fu">m</span><span class="op">.</span> <span class="dt">Monad</span> <span class="fu">m</span> <span class="ot">=&gt;</span> <span class="dt">Monad</span> (<span class="fu">t</span> <span class="fu">m</span>)) <span class="ot">=&gt;</span> <span class="dt">MonadTrans</span> <span class="fu">t</span> <span class="kw">where</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="fu">lift</span> <span class="ot">::</span> <span class="fu">m</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">t</span> <span class="fu">m</span> <span class="fu">a</span></span></code></pre></div>
<p>(Examples lifted from <a href="https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/quantified_constraints.html">the GHC User Guide on <code>QuantifiedConstraints</code></a>, section Motivation.)</p>
<p>One restriction is that the conclusion of a quantified constraint cannot
mention a type family.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="kw">family</span> <span class="dt">F</span> <span class="fu">a</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- (forall a. C (F a))  -- Illegal type family application in a quantified constraint</span></span></code></pre></div>
<p>A quantified constraint can be thought of as providing a local instance,
and they are subject to a similar restriction on the shape of instance heads
so that instance resolution may try to match required constraints with
the head of existing instances.</p>
<p>Type families are not matchable: we cannot determine whether an applied
type family <code>F a</code> matches a type constructor <code>T</code> in a manner satisfying the
properties required by instance resolution (“coherence”). So type families
can’t be in the conclusion of a type family.</p>
<h2 id="the-quantified-constraint-trick">The quantified constraint trick</h2>
<h3 id="step-1">Step 1</h3>
<p>To legalize type families in quantified constraints,
all we need is a <em>class synonym</em>:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span>    <span class="dt">C</span> (<span class="dt">F</span> <span class="fu">a</span>) <span class="ot">=&gt;</span> <span class="dt">CF</span> <span class="fu">a</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">C</span> (<span class="dt">F</span> <span class="fu">a</span>) <span class="ot">=&gt;</span> <span class="dt">CF</span> <span class="fu">a</span></span></code></pre></div>
<p>That <code>CF a</code> is equivalent to <code>C (F a)</code>, and <code>forall a. CF a</code> is legal.</p>
<h3 id="step-2">Step 2?</h3>
<p>Since GHC 9.2, Step 1 alone solves the problem. It Just Works™.
<a href="https://mail.haskell.org/pipermail/haskell-cafe/2022-September/135571.html">And I don’t know why.</a></p>
<p>Before that, for GHC 9.0 and prior,
we also needed to hold the compiler’s hand and tell it how
to instantiate the quantified constraint.</p>
<p>Indeed, now functions may have constraints of the form <code>forall a. CF a</code>,
which should imply <code>C (F x)</code> for any <code>x</code>.
Although <code>CF</code> and <code>C (F x)</code> are logically related, when <code>C (F x)</code> is required,
that triggers a search for instances of the class <code>C</code>, and not the <code>CF</code> which
is provided by the quantified constraint.
The search would fail unless some hint is provided to the compiler.</p>
<p>When you require a constraint <code>C (F x)</code>, insert a type annotation mentioning
the <code>CF x</code> constraint (using the <code>CF</code> class instead of <code>C</code>).</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="fu">_</span> <span class="co">{- C (F x) available here -}</span> <span class="ot">::</span> <span class="dt">CF</span> <span class="fu">x</span> <span class="ot">=&gt;</span> <span class="fu">_</span></span></code></pre></div>
<p>Inside the annotation (to the left of <code>::</code>), we are given <code>CF x</code>, from which <code>C (F x)</code> is inferred as a superclass. Outside the annotation, we are requiring <code>CF x</code>,
which is trivially solved by the quantified constraint <code>forall a. CF a</code>.</p>
<h3 id="recap">Recap</h3>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Mixing quantified constraints with type families --</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">C</span> <span class="fu">a</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="kw">family</span> <span class="dt">F</span> <span class="fu">a</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- forall a. C (F a)  -- Nope.</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span>    <span class="dt">C</span> (<span class="dt">F</span> <span class="fu">a</span>) <span class="ot">=&gt;</span> <span class="dt">CF</span> <span class="fu">a</span>  <span class="co">-- Class synonym</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">C</span> (<span class="dt">F</span> <span class="fu">a</span>) <span class="ot">=&gt;</span> <span class="dt">CF</span> <span class="fu">a</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="co">-- forall a. CF a     -- Yup.</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a><span class="co">-- Some provided function we want to call.</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a><span class="fu">f</span> <span class="ot">::</span> <span class="dt">C</span> (<span class="dt">F</span> <span class="fu">t</span>) <span class="ot">=&gt;</span> <span class="fu">t</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a><span class="co">-- A function we want to implement using f.</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a><span class="fu">g</span> <span class="ot">::</span> (<span class="kw">forall</span> <span class="fu">a</span><span class="op">.</span> <span class="dt">CF</span> <span class="fu">a</span>) <span class="ot">=&gt;</span> <span class="fu">t</span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a><span class="fu">g</span> <span class="ot">=</span> <span class="fu">f</span>               <span class="co">-- OK on GHC &gt;= 9.2</span></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a><span class="fu">g</span> <span class="ot">=</span> <span class="fu">f</span> <span class="ot">::</span> <span class="dt">CF</span> <span class="fu">t</span> <span class="ot">=&gt;</span> <span class="fu">t</span>  <span class="co">-- Annotation needed on GHC &lt;= 9.0</span></span></code></pre></div>
<p>The part of that type annotation that really matters
is the constraint. The rest of the type to the right of the arrow
is redundant. Another way to write only the constraint uses the following
identity function with a fancy type:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="fu">with</span> <span class="ot">::</span> <span class="kw">forall</span> <span class="fu">c</span> <span class="fu">r</span><span class="op">.</span> (<span class="fu">c</span> <span class="ot">=&gt;</span> <span class="fu">r</span>) <span class="ot">-&gt;</span> (<span class="fu">c</span> <span class="ot">=&gt;</span> <span class="fu">r</span>)</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="fu">with</span> <span class="fu">x</span> <span class="ot">=</span> <span class="fu">x</span></span></code></pre></div>
<p>So you can supply the hint like this instead:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">g</span> <span class="ot">::</span> <span class="kw">forall</span> <span class="fu">t</span><span class="op">.</span> (<span class="kw">forall</span> <span class="fu">a</span><span class="op">.</span> <span class="dt">CF</span> <span class="fu">a</span>) <span class="ot">=&gt;</span> <span class="fu">t</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="fu">g</span> <span class="ot">=</span> <span class="fu">with</span> <span class="op">@</span>(<span class="dt">CF</span> <span class="fu">t</span>) <span class="fu">f</span></span></code></pre></div>
<h2 id="application-generic-functor">Application: <em>generic-functor</em></h2>
<p>What do I need that trick for? It comes up in generic metaprogramming.</p>
<p>Imagine deriving <code>Functor</code> for <code>Generic</code> types (no <code>Generic1</code>, which is not as
general as you might hope). One way is to implement the following class on
generic representations:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">RepFmap</span> <span class="fu">a</span> <span class="fu">a&#39;</span> <span class="fu">rep</span> <span class="fu">rep&#39;</span> <span class="kw">where</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>  <span class="fu">repFmap</span> <span class="ot">::</span> (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a&#39;</span>) <span class="ot">-&gt;</span> <span class="fu">rep</span> <span class="ot">-&gt;</span> <span class="fu">rep&#39;</span></span></code></pre></div>
<p>A type constructor <code>f :: Type -&gt; Type</code> will be a <code>Functor</code> when its
generic representation (<code>Rep</code>) implements <code>RepFmap a a'</code>…
for all <code>a</code>, <code>a'</code>.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Class synonym for generically derivable functors</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span>    (<span class="kw">forall</span> <span class="fu">a</span><span class="op">.</span> <span class="dt">Generic</span> (<span class="fu">f</span> <span class="fu">a</span>), <span class="kw">forall</span> <span class="fu">a</span> <span class="fu">a&#39;</span><span class="op">.</span> <span class="dt">RepFmap</span> <span class="fu">a</span> <span class="fu">a&#39;</span> (<span class="dt">Rep</span> (<span class="fu">f</span> <span class="fu">a</span>) ()) (<span class="dt">Rep</span> (<span class="fu">f</span> <span class="fu">a&#39;</span>) ())) <span class="ot">=&gt;</span> <span class="dt">GFunctor</span> <span class="fu">f</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="op">...</span>   <span class="co">-- idem (class synonym)</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- Wait a second...</span></span></code></pre></div>
<p>But that is illegal, because the type family <code>Rep</code> occurs in the conclusion of
a quantified constraint.</p>
<p>Time for the trick! We give a new name to the conclusion:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span>    <span class="dt">RepFmap</span> <span class="fu">a</span> <span class="fu">a&#39;</span> (<span class="dt">Rep</span> (<span class="fu">f</span> <span class="fu">a</span>) ()) (<span class="dt">Rep</span> (<span class="fu">f</span> <span class="fu">a&#39;</span>) ()) <span class="ot">=&gt;</span> <span class="dt">RepFmapRep</span> <span class="fu">a</span> <span class="fu">a&#39;</span> <span class="fu">f</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="op">...</span>  <span class="co">-- idem (class synonym)</span></span></code></pre></div>
<p>And we can use it in a quantified constraint:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Now this works!</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span>    (<span class="kw">forall</span> <span class="fu">a</span><span class="op">.</span> <span class="dt">Generic</span> (<span class="fu">f</span> <span class="fu">a</span>), <span class="kw">forall</span> <span class="fu">a</span> <span class="fu">a&#39;</span><span class="op">.</span> <span class="dt">RepFmapRep</span> <span class="fu">a</span> <span class="fu">a&#39;</span> <span class="fu">f</span>) <span class="ot">=&gt;</span> <span class="dt">GFunctor</span> <span class="fu">f</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="op">...</span>   <span class="co">-- idem (class synonym)</span></span></code></pre></div>
<p>To obtain the final generic implementation of <code>fmap</code>, we wrap <code>repFmap</code> between <code>to</code> and <code>from</code>.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="fu">gfmap</span> <span class="ot">::</span> <span class="kw">forall</span> <span class="fu">f</span> <span class="fu">a</span> <span class="fu">a&#39;</span><span class="op">.</span> <span class="dt">GFunctor</span> <span class="fu">f</span> <span class="ot">=&gt;</span> (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a&#39;</span>) <span class="ot">-&gt;</span> <span class="fu">f</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">f</span> <span class="fu">a&#39;</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="fu">gfmap</span> <span class="fu">f</span> <span class="ot">=</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>  <span class="fu">with</span> <span class="op">@</span>(<span class="dt">RepFmapRep</span> <span class="fu">a</span> <span class="fu">a&#39;</span> <span class="fu">f</span>)             <span class="co">-- Hand-holding for GHC &lt;= 9.0</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>    (<span class="fu">to</span> <span class="op">@</span><span class="fu">_</span> <span class="op">@</span>() <span class="op">.</span> <span class="fu">repFmap</span> <span class="fu">f</span> <span class="op">.</span> <span class="fu">from</span> <span class="op">@</span><span class="fu">_</span> <span class="op">@</span>())</span></code></pre></div>
<p>Et voilà.</p>
<p><a href="https://gist.github.com/Lysxia/7714c19ef9c17b487a46c804694fc0f9">(Gist of this example)</a></p>
<hr />
<h2 id="appendix-couldnt-we-do-this-instead">Appendix: Couldn’t we do this instead?</h2>
<p>If you’ve followed all of that, there’s one other way you might try defining
<code>gfmap</code> without <code>QuantifiedConstraints</code>, by just listing the three constraints
actually needed in the body of the function.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Dangerous gfmap!</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="fu">gfmap</span> <span class="ot">::</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Generic</span> (<span class="fu">f</span> <span class="fu">a</span>) <span class="ot">=&gt;</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Generic</span> (<span class="fu">f</span> <span class="fu">a&#39;</span>) <span class="ot">=&gt;</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>  <span class="dt">RepFmap</span> <span class="fu">a</span> <span class="fu">a&#39;</span> (<span class="dt">Rep</span> (<span class="fu">f</span> <span class="fu">a</span>) ()) (<span class="dt">Rep</span> (<span class="fu">f</span> <span class="fu">a&#39;</span>) ()) <span class="ot">=&gt;</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>  (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a&#39;</span>) <span class="ot">-&gt;</span> <span class="fu">f</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">f</span> <span class="fu">a&#39;</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a><span class="fu">gfmap</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">to</span> <span class="op">@</span><span class="fu">_</span> <span class="op">@</span>() <span class="op">.</span> <span class="fu">repFmap</span> <span class="fu">f</span> <span class="op">.</span> <span class="fu">from</span> <span class="op">@</span><span class="fu">_</span> <span class="op">@</span>()</span></code></pre></div>
<p>This is okay as long as it is only ever used to implement <code>fmap</code> as in:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="fu">fmap</span> <span class="ot">=</span> <span class="fu">gfmap</span></span></code></pre></div>
<p>Any other use voids a guarantee you didn’t know you expected.</p>
<p>The thing I haven’t told you is that <code>RepFmap</code> is implemented with…
incoherent instances!<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> In fact, this <code>gfmap</code> may behave differently
depending on how it is instantiated <em>at compile time</em>.</p>
<p>For example, for a functor with a field of constant type:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">T</span> <span class="fu">a</span> <span class="fu">b</span> <span class="ot">=</span> <span class="dt">C</span> <span class="dt">Int</span> <span class="fu">a</span> <span class="fu">b</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> <span class="dt">Generic</span></span></code></pre></div>
<p><code>gfmap @(T a) @b @b'</code> where <code>a</code>, <code>b</code> and <code>b'</code> are distinct type variables
behaves like <code>fmap</code> should. But <code>gfmap @(T Int) @Int @Int</code>
will unexpectedly apply its argument function to every field.
They all have type <code>Int</code>, so a function <code>Int -&gt; Int</code> can and will be applied to
all fields.</p>
<p>I could demonstrate this if I had implemented <code>RepFmap</code>…
Luckily, there is a more general version of this “dangerous <code>gfmap</code>” readily
available in my library
<a href="https://hackage.haskell.org/package/generic-functor"><em>generic-functor</em></a>.
It can be very incoherent, but if you follow some rules, it can also be very
fun to use.</p>
<h3 id="playing-with-fire">Playing with fire</h3>
<p><code>gsolomap</code><a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> is a function from <em>generic-functor</em> that can implement
<code>fmap</code>, and much more.</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="fu">fmapT</span> <span class="ot">::</span> (<span class="fu">b</span> <span class="ot">-&gt;</span> <span class="fu">b&#39;</span>) <span class="ot">-&gt;</span> <span class="dt">T</span> <span class="fu">a</span> <span class="fu">b</span> <span class="ot">-&gt;</span> <span class="dt">T</span> <span class="fu">a</span> <span class="fu">b&#39;</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="fu">fmapT</span> <span class="ot">=</span> <span class="fu">gsolomap</span></span></code></pre></div>
<p>Map over the first parameter if you prefer:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="fu">firstT</span> <span class="ot">::</span> (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a&#39;</span>) <span class="ot">-&gt;</span> <span class="dt">T</span> <span class="fu">a</span> <span class="fu">b</span> <span class="ot">-&gt;</span> <span class="dt">T</span> <span class="fu">a&#39;</span> <span class="fu">b</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="fu">firstT</span> <span class="ot">=</span> <span class="fu">gsolomap</span></span></code></pre></div>
<p>Or map over both type parameters at once:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bothT</span> <span class="ot">::</span> (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a&#39;</span>) <span class="ot">-&gt;</span> <span class="dt">T</span> <span class="fu">a</span> <span class="fu">a</span> <span class="ot">-&gt;</span> <span class="dt">T</span> <span class="fu">a&#39;</span> <span class="fu">a&#39;</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="fu">bothT</span> <span class="ot">=</span> <span class="fu">gsolomap</span></span></code></pre></div>
<p>I don’t know what to call this, but <code>gsolomap</code> also does what you might guess
from this type:</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="fu">watT</span> <span class="ot">::</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>  (<span class="fu">a</span> <span class="ot">-&gt;</span> <span class="fu">a&#39;</span>) <span class="ot">-&gt;</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">T</span> (<span class="fu">a</span> , <span class="fu">a</span> ) ((<span class="fu">a</span>  <span class="ot">-&gt;</span> <span class="fu">a&#39;</span>) <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="fu">a</span> ) <span class="ot">-&gt;</span></span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">T</span> (<span class="fu">a&#39;</span>, <span class="fu">a&#39;</span>) ((<span class="fu">a&#39;</span> <span class="ot">-&gt;</span> <span class="fu">a</span> ) <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="fu">a&#39;</span>) </span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a><span class="fu">watT</span> <span class="ot">=</span> <span class="fu">gsolomap</span></span></code></pre></div>
<p>It’s important to specialize <code>gsolomap</code> with <em>distinct type variables</em>
(<code>a</code> and <code>a'</code>).
You cannot refactor code by inlining a function if its body uses <code>gsolomap</code>,
as it risks breaking that requirement.</p>
<h3 id="witnessing-incoherence">Witnessing incoherence</h3>
<p>For an example of surprising result caused by incoherence, apply the <code>fmapT</code>
defined above to some concrete arguments. See how the result changes then you
replace <code>fmapT</code> with its definition, <code>gsolomap</code>.</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="fu">fmapT</span>    ((<span class="op">+</span><span class="dv">1</span>) <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span>) (<span class="dt">C</span> <span class="dv">0</span> <span class="dv">0</span> <span class="dv">0</span>) <span class="op">=</span><span class="ot">=</span> <span class="dt">C</span> <span class="dv">0</span> <span class="dv">0</span> <span class="dv">1</span> <span class="ot">::</span> <span class="dt">T</span> <span class="dt">Int</span> <span class="dt">Int</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="fu">gsolomap</span> ((<span class="op">+</span><span class="dv">1</span>) <span class="ot">::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span>) (<span class="dt">C</span> <span class="dv">0</span> <span class="dv">0</span> <span class="dv">0</span>) <span class="op">=</span><span class="ot">=</span> <span class="dt">C</span> <span class="dv">1</span> <span class="dv">1</span> <span class="dv">1</span> <span class="ot">::</span> <span class="dt">T</span> <span class="dt">Int</span> <span class="dt">Int</span>  <span class="co">-- Noooooo...</span></span></code></pre></div>
<p><a href="https://gist.github.com/Lysxia/a83b16c992d9945576fbff3611ab8f3a">(Gist of those <code>gsolomap</code> (counter)examples)</a></p>
<p>This is why <code>gfmap</code>’s signature should use quantified constraints:
this guarantees that when the <code>RepFmap</code> constraint is solved,
the first two parameters are going to be <em>distinct type variables</em>,
thanks to the universal quantification (<code>forall a a'</code>).
Thus, incoherence is hidden away.</p>
<p>Following that recipe, <em>generic-functor</em> contains <em>safe</em> implementations of
<code>Functor</code>, <code>Foldable</code>, <code>Traversable</code>, <code>Bifunctor</code>, and <code>Bitraversable</code>.</p>
<p>In particular, the type of <code>gfmap</code> guarantees that it has a unique
inhabitant satisfying <code>gfmap id = id</code>, and this property is quite
straightforward to check by visual inspection of the implementation.</p>
<p>After all, <code>gfmap</code> will essentially do one of three things:
(1) it will be <code>id</code> on types that don’t mention the type parameters
in its function argument <code>a -&gt; a'</code>, (2) it will apply the provided function
<code>f</code>, or (3) it will <code>fmap</code> (or <code>bimap</code>, or <code>dimap</code>) itself through a type
constructor. In all cases (with some inductive reasoning for (3)),
if <code>f = id</code>, then <code>gfmap f = id</code>.</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="fu">gfmap</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">id</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="fu">gfmap</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">f</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a><span class="fu">gfmap</span> <span class="fu">f</span> <span class="ot">=</span> <span class="fu">fmap</span> (<span class="fu">gfmap</span> <span class="fu">f</span>)</span></code></pre></div>
<p>The dangerous <code>gfmap</code> (without <code>QuantifiedConstraints</code>) or <code>gsolomap</code> fail this
property, because the extra occurrences of <code>a</code> and <code>a'</code> in its constraint make
their signatures have a different “shape” from <code>fmap</code>.</p>
<p>The trade-off is that those safe functions can’t do the same crazy things
as <code>gsolomap</code> above.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>AFAICT there is no way around that with <code>GHC.Generics</code>.
Incoherent instances can be avoided with <em>kind-generics</em>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p><code>gsolomap</code> accepts one function parameter. There is also
<code>gmultimap</code> which accepts arbitrarily many functions.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></summary>
</entry>
<entry>
    <title>Formalizing finite sets</title>
    <link href="https://blog.poisson.chat/posts/2022-05-27-formalizing-finite-sets.html" />
    <id>https://blog.poisson.chat/posts/2022-05-27-formalizing-finite-sets.html</id>
    <published>2022-05-27T00:00:00Z</published>
    <updated>2022-05-27T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>Combinatorics studies mathematical structures by counting. Counting may seem like
a benign activity, but the same rigor necessary to prevent double- or under-counting
mistakes arguably underpins all of mathematics.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<p>Combining my two favorite topics, I’ve always wanted to mechanize combinatorics
in Coq.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>
An immediate challenge is to formalize the idea of “set”.<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> We have
to be able to define the set of things we want to count. It turns out that
there are at least two ways of encoding sets in type theory: sets as
types, and sets as predicates. They are suitable for defining different classes
of operations: sums (disjoint union) are a natural operation on types, while
unions and intersections are naturally defined on predicates.</p>
<p>The interplay between these two notions of sets, and finiteness, will then let
us prove the standard formula for the cardinality of unions, aka. the binary
inclusion-exclusion formula:</p>
<pre><code>#|X ∪ Y| = #|X| + #|Y| - #|X ∩ Y|</code></pre>
<details>
<summary>
Imports and options
</summary>
<pre class="alectryon"><code>From Coq Require Import ssreflect ssrbool.

Set Implicit Arguments.</code></pre>
</details>
<h2 id="sets-as-types">Sets as types</h2>
<p>The obvious starting point is to view a type as the set of its inhabitants.</p>
<p>How do we count its inhabitants?
We will say that a set <code>A</code> has cardinality <code>n</code> if there is a bijection between
<code>A</code> and the set <code>{0 .. n-1}</code> of natural numbers between <code>0</code> and <code>n-1</code>.</p>
<h3 id="bijections">Bijections</h3>
<p>A bijection is a standard way to represent a one-to-one correspondence
between two sets, with a pair of inverse functions.
We define the type <code>bijection A B</code> as a record containing the two functions
and a proof of their inverse relationship.</p>
<pre class="alectryon"><code>Record is_bijection {A B} (to : A -&gt; B) (from : B -&gt; A) : Prop :=
  { from_to : forall a, from (to a) = a
  ; to_from : forall b, to (from b) = b }.

Record bijection (A B : Type) : Type :=
  { bij_to : A -&gt; B
  ; bij_from : B -&gt; A
  ; bij_is_bijection :&gt; is_bijection bij_to bij_from }.

Infix &quot;&lt;--&gt;&quot; := bijection (at level 90) : type_scope.</code></pre>
<p>We say that <code>A</code> and <code>B</code> are isomorphic when there exists a bijection between
<code>A</code> and <code>B</code>. Isomorphism is an equivalence relation: reflexive, symmetric,
transitive.<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a></p>
<pre class="alectryon"><code>Definition bijection_refl {A} : A &lt;--&gt; A.
Admitted. (* Easy exercise *)

Definition bijection_sym {A B} : (A &lt;--&gt; B) -&gt; (B &lt;--&gt; A).
Admitted. (* Easy exercise *)

Definition bijection_trans {A B C} : (A &lt;--&gt; B) -&gt; (B &lt;--&gt; C) -&gt; (A &lt;--&gt; C).
Admitted. (* Easy exercise *)

Infix &quot;&gt;&gt;&gt;&quot; := bijection_trans (at level 40).</code></pre>
<h3 id="finite-sets">Finite sets</h3>
<p>Our “bijective” definition of cardinality shall rely on a primitive,
canonical family of finite types <code>{0 .. n-1}</code> that is taken for granted.
We can define them as the following sigma type, using the familiar set
comprehension notation, also known as <a href="https://math-comp.github.io/htmldoc/mathcomp.ssreflect.fintype.html#ordinal"><code>ordinal</code> in <em>math-comp</em></a>:</p>
<pre class="alectryon"><code>Definition fin (n : nat) : Type := { p | p &lt; n }.</code></pre>
<p>An inhabitant of <code>fin n</code> is a pair of a <code>p : nat</code> and
a proof object of <code>p &lt; n</code>. Such proofs objects are unique for a given
<code>p</code> and <code>n</code>, so the first component uniquely determines the second component,
and <code>fin n</code> does have exactly <code>n</code> inhabitants.<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a></p>
<h4 id="finiteness">Finiteness</h4>
<p>We can now say that a type <code>A</code> has cardinality <code>n</code> if there is a bijection
between <code>A</code> and <code>fin n</code>, <em>i.e.</em>, there is an inhabitant of <code>A &lt;--&gt; fin n</code>.
Note that this only defines finite cardinalities, which is fine for doing
finite combinatorics. Infinity is really weird so let’s not think about it.</p>
<p>As a sanity check, you can verify the cardinalities of the usual suspects,
<code>bool</code>, <code>unit</code>, and <code>Empty_set</code>.</p>
<pre class="alectryon"><code>Definition bijection_bool : bool &lt;--&gt; fin 2.
Admitted. (* Easy exercise *)

Definition bijection_unit : unit &lt;--&gt; fin 1.
Admitted. (* Easy exercise *)

Definition bijection_Empty_set : Empty_set &lt;--&gt; fin 0.
Admitted. (* Easy exercise *)</code></pre>
<p>A type <code>A</code> is finite when it has some cardinality <code>n : nat</code>.
When speaking informally, it’s common to view finiteness as a property,
a thing that a set either <em>is</em> or <em>is not</em>. To prove finiteness
is merely to exhibit the relevant data: a number to be the cardinality,
and an associated bijection (which we call an <em>enumeration</em> of <code>A</code>,
<code>enum</code> for short).
Hence we formalize “finiteness” as the type of that data.</p>
<pre class="alectryon"><code>Record is_finite (A : Type) : Type :=
  { card : nat
  ; enum : A &lt;--&gt; fin card }.</code></pre>
<p>Further bundling <code>is_finite A</code> proofs with their associated set <code>A</code>, we obtain
a concept aptly named “finite type”.<a href="#fn6" class="footnote-ref" id="fnref6" role="doc-noteref"><sup>6</sup></a> A finite type is a type <code>A</code> paired with
a proof of <code>is_finite A</code>.</p>
<pre class="alectryon"><code>Record finite_type : Type :=
  { ft_type :&gt; Type
  ; ft_is_finite :&gt; is_finite ft_type }.</code></pre>
<p>We leverage coercions (indicated by <code>:&gt;</code>) to lighten the notation of
expressions involving <code>finite_type</code>.</p>
<p>The first coercion <code>ft_type</code> lets us use a <code>finite_type</code> as a <code>Type</code>.
So if <code>E : finite_type</code>, we can write the judgement that
“<code>e</code> is an element of <code>E</code>” as <code>e : E</code>, which implicitly expands to
the more cumbersome <code>e : ft_type E</code>.</p>
<p>Similarly, the second coercion <code>ft_is_finite</code> lets us access
the evidence of finiteness without naming that field. In particular,
we can write the cardinality of <code>E : finite_type</code> as <code>card E</code>,
as if <code>card</code> were a proper field of <code>E</code> rather than the nested record it
actually belongs to. This is a convenient mechanism for overloading,
letting us reuse the name <code>card</code>(inality) even though records technically
cannot have fields with the same name.
With that, we define <code>#|A|</code> as sugar for <code>card A</code>:</p>
<pre class="alectryon"><code>Notation &quot;&#39;#|&#39; A &#39;|&#39;&quot; := (card A).</code></pre>
<details>
<summary>
Some notation boilerplate
</summary>
<pre class="alectryon"><code>Declare Scope fintype_scope.
Delimit Scope fintype_scope with fintype.
Bind Scope fintype_scope with finite_type.</code></pre>
</details>
<h4 id="uniqueness-of-cardinality">Uniqueness of cardinality</h4>
<p>The phrase “cardinality of a set” suggests that cardinality is an inherent
property of sets. But now we’ve defined “finite type” essentially as a tuple
where the cardinality is just one component. What’s to prevent us from
putting a different number there, for the same underlying type?</p>
<p>We can prove that this cannot happen. Cardinality is unique: any two finiteness
proofs for the same type must yield the same cardinality.</p>
<p>(The proof is a little tedious and technical.)</p>
<pre class="alectryon"><code>Theorem card_unique {A} (F1 F2 : is_finite A) : card F1 = card F2.
Admitted. (* Intermediate exercise *)</code></pre>
<p>A slightly more general result is that isomorphic types (<em>i.e.</em>, related by
a bijection) have the same cardinality. It can first be proved
in terms of <code>is_finite</code>, from which a corollary in terms of <code>finite_type</code>
follows.</p>
<pre class="alectryon"><code>Theorem card_bijection {A B} (FA : is_finite A) (FB : is_finite B)
  : (A &lt;--&gt; B) -&gt; card FA = card FB.
Admitted. (* Like card_unique *)

Theorem card_bijection_finite_type {A B : finite_type}
  : (A &lt;--&gt; B) -&gt; #|A| = #|B|.
Proof.
  apply card_bijection.
Qed.</code></pre>
<p>The converse is also true and useful: two types with the same cardinality are
isomorphic.</p>
<pre class="alectryon"><code>Theorem bijection_card {A B} (FA : is_finite A) (FB : is_finite B)
  : card FA = card FB -&gt; (A &lt;--&gt; B).
Admitted. (* Easy exercise *)

Theorem bijection_card_finite_type {A B : finite_type}
  : #|A| = #|B| -&gt; (A &lt;--&gt; B).
Proof.
  apply bijection_card.
Qed.</code></pre>
<h3 id="operations-on-finite-sets">Operations on finite sets</h3>
<h4 id="sum">Sum</h4>
<p>The sum of sets is also known as the disjoint union.</p>
<pre class="coq"><code>Inductive sum (A B : Type) : Type :=
| inl : A -&gt; A + B
| inr : B -&gt; A + B
where &quot;A + B&quot; := (sum A B) : type_scope.</code></pre>
<p><code>sum</code> is a binary operation on types. We must work to
make it an operation on finite types.</p>
<p>There is a bijection between <code>fin n + fin m</code> (sum of sets)
and <code>fin (n + m)</code> (sum of nats).</p>
<pre class="alectryon"><code>Definition bijection_sum_fin {n m} : fin n + fin m &lt;--&gt; fin (n + m).
Admitted. (* Intermediate exercise *)</code></pre>
<p>The sum is a bifunctor.</p>
<pre class="alectryon"><code>Definition bijection_sum {A A&#39; B B&#39;}
  : (A &lt;--&gt; B) -&gt; (A&#39; &lt;--&gt; B&#39;) -&gt; (A + A&#39; &lt;--&gt; B + B&#39;).
Admitted. (* Easy exercise *)</code></pre>
<p>Combining those facts, we can prove that the sum of two finite sets is finite (<code>finite_sum</code>),
and the cardinality of the sum is the sum of the cardinalities (<code>card_sum</code>).</p>
<pre class="alectryon"><code>Definition is_finite_sum {A B} (FA : is_finite A) (FB : is_finite B)
  : is_finite (A + B) :=
  {| card := #|FA| + #|FB|
  ;  enum := bijection_sum (enum FA) (enum FB) &gt;&gt;&gt; bijection_sum_fin |}.

Definition finite_sum (A B : finite_type) : finite_type :=
  {| ft_type := A + B ; ft_is_finite := is_finite_sum A B |}.

Infix &quot;+&quot; := finite_sum : fintype_scope.</code></pre>
<pre class="alectryon"><code>Theorem card_sum {A B : finite_type} : #|(A + B)%fintype| = #|A| + #|B|.
Proof.
  reflexivity.
Qed.</code></pre>
<h4 id="product">Product</h4>
<p>The cartesian product has structure dual to the sum.</p>
<pre class="coq"><code>Inductive prod (A B : Type) : Type :=
| pair : A -&gt; B -&gt; A * B
where &quot;A * B&quot; := (prod A B) : type_scope.</code></pre>
<ul>
<li>There is a bijection <code>fin n * fin m &lt;--&gt; fin (n * m)</code>.</li>
<li>The product is a bifunctor.</li>
<li>The product of finite sets is finite.</li>
<li>The cardinality of the product is the product of the cardinalities.</li>
</ul>
<details>
<summary>
Coq code
</summary>
<pre class="alectryon"><code>Definition bijection_prod_fin {n m} : fin n * fin m &lt;--&gt; fin (n * m).
Admitted. (* Intermediate exercise *)

Definition bijection_prod {A A&#39; B B&#39;}
  : (A &lt;--&gt; B) -&gt; (A&#39; &lt;--&gt; B&#39;) -&gt; (A * A&#39; &lt;--&gt; B * B&#39;).
Admitted. (* Easy exercise *)

Definition is_finite_prod {A B} (FA : is_finite A) (FB : is_finite B)
  : is_finite (A * B) :=
  {| card := #|FA| * #|FB|
  ;  enum := bijection_prod (enum FA) (enum FB) &gt;&gt;&gt; bijection_prod_fin |}.

Definition finite_prod (A B : finite_type) : finite_type :=
  {| ft_type := A * B ; ft_is_finite := is_finite_prod A B |}.

Infix &quot;*&quot; := finite_prod : fintype_scope.

Theorem card_prod {A B : finite_type} : #|(A * B)%fintype| = #|A| * #|B|.
Proof.
  reflexivity.
Qed.</code></pre>
</details>
<h2 id="sets-as-predicates">Sets as predicates</h2>
<p>Two other common operations on sets are union and intersection.
However, those operations don’t fit in the view of sets as types.
While set membership <code>x ∈ X</code> is a proposition, type inhabitation <code>x : X</code> is
a judgement, which is a completely different thing,<a href="#fn7" class="footnote-ref" id="fnref7" role="doc-noteref"><sup>7</sup></a> so we need a different
approach.</p>
<p>The idea of set membership <code>x ∈ X</code> as a proposition presumes that <code>x</code>
and <code>X</code> are entities that exist independently of each other. This suggests
that there is some “universe” that elements <code>x</code> live in, and the
sets <code>X</code> under consideration are subsets of that same universe.
We represent the universe by a type <code>A</code>, and sets (<em>i.e.</em>, “subsets of the universe”)
by predicates on <code>A</code>.</p>
<pre class="alectryon"><code>Definition set_of (A : Type) := (A -&gt; bool).</code></pre>
<p>Hence, if <code>x : A</code> is an element of the universe, and <code>X : set A</code> is a set
(subset of the universe), we will denote set membership <code>x ∈ X</code> simply as <code>X x</code>
(<code>x</code> satisfies the predicate <code>X</code>).</p>
<p>Those predicates are boolean, <em>i.e.</em>, decidable. This is necessary in several
constructions and proofs here, notably to prove that the union or intersection
of finite sets is finite. We rely on a coercion to implicitly convert booleans
to <code>Prop</code>: <code>is_true : bool &gt;-&gt; Prop</code>, which is exported by <code>ssreflect</code>.</p>
<h3 id="union-intersection-complement">Union, intersection, complement</h3>
<p>Those common set operations correspond to the usual logical connectives.</p>
<pre class="alectryon"><code>Section Operations.

Context {A : Type}.

Definition union&#39; (X Y : set_of A) : set_of A := fun a =&gt; X a || Y a.
Definition intersection&#39; (X Y : set_of A) : set_of A := fun a =&gt; X a &amp;&amp; Y a.
Definition complement&#39; (X : set_of A) : set_of A := fun a =&gt; negb (X a).

End Operations.</code></pre>
<p>Define the familiar infix notation for union and intersection.</p>
<pre class="alectryon"><code>Declare Scope set_of_scope.
Delimit Scope set_of_scope with set_of.
Bind Scope set_of_scope with set_of.

Infix &quot;∪&quot; := union&#39; (at level 40) : set_of_scope.
Infix &quot;∩&quot; := intersection&#39; (at level 40) : set_of_scope.</code></pre>
<h3 id="finiteness-1">Finiteness</h3>
<p>Again, we will characterize finite sets using bijections to <code>fin n</code>.
We first transform the set <code>X</code> into a type <code>to_type X</code>, so we can form
the type of bijections <code>to_type X &lt;--&gt; fin n</code>. Like <code>fin</code>, we define
<code>to_type A</code> as a sigma type. Thanks to the predicate <code>X</code> being boolean,
there is at most one proof <code>p : X a</code> for each <code>a</code>, so the type
<code>{ a : A | X a }</code> has exactly one inhabitant for each inhabitant <code>a : A</code>
satisfying <code>X a</code>.</p>
<pre class="alectryon"><code>Definition to_type {A : Type} (X : set_of A) : Type := { a : A | X a }.

Coercion to_type : set_of &gt;-&gt; Sortclass.</code></pre>
<p>We obtain a notion of finite set by imitating the structure of <code>finite_type</code>.
The set-as-predicate <code>X</code> is finite if the set-as-type <code>to_type X</code> is finite.</p>
<pre class="alectryon"><code>Record finite_set_of (A : Type) : Type :=
  { elem_of :&gt; set_of A
  ; fso_is_finite :&gt; is_finite (to_type elem_of)
  }.</code></pre>
<p>Similarly, a <code>finite_type_of</code> can be coerced to a <code>finite_type</code>.</p>
<pre class="alectryon"><code>Definition to_finite_type {A} (X : finite_set_of A) : finite_type :=
  {| ft_type := elem_of X
  ;  ft_is_finite := X |}.

Coercion to_finite_type : finite_set_of &gt;-&gt; finite_type.</code></pre>
<h3 id="finite-unions-and-intersections">Finite unions and intersections</h3>
<p>We then prove that the union and intersection of finite sets are finite.
This is actually fairly challenging, since proving finiteness means to
calculate the cardinality of the set and to construct the associated
bijection. Unlike sum and product, there is no simple formula for the
cardinality of union and intersection. One candidate may seem to be the binary
inclusion-exclusion formula:</p>
<pre><code>#|X ∪ Y| = #|X| + #|Y| - #|X ∩ Y|</code></pre>
<p>But that only gives the cardinality of the union in terms of the intersection,
or vice versa, and we don’t know either yet.</p>
<p>Rather than constructing the bijections directly, we decompose the proof.
The intuition is that <code>X ∪ Y</code> and <code>X ∩ Y</code> can easily be “bounded” by known
finite sets, namely <code>X + Y</code> and <code>X</code> respectively. By “bounded”, we mean that
there is an injection from one set to the other.</p>
<p>The standard definition of injectivity is via an implication
<code>f x = f y -&gt; x = y</code>. However, a better definition for our purposes
comes from a one-sided inverse property: a function <code>f : A -&gt; B</code> is
a section if there exists another function <code>g : B -&gt; A</code> (called a retraction)
such that <code>g (f x) = x</code>.
Every section is an injection, but the converse requires the law of excluded
middle.</p>
<pre class="alectryon"><code>Record is_section {A B} (to : A -&gt; B) (from : B -&gt; A) : Prop :=
  { s_from_to : forall a, from (to a) = a }.

Record section (A B : Type) : Type :=
  { s_from : A -&gt; B
  ; s_to : B -&gt; A
  ; s_is_section : is_section s_from s_to }.</code></pre>
<p>The point is that, given a section to a finite set, <code>section A (fin n)</code>,
we can construct a bijection <code>A &lt;--&gt; fin m</code> for some <code>m</code>, that is smaller than
<code>n</code>. We formalize this result with a proof-relevant sigma type.</p>
<pre class="alectryon"><code>Definition section_bijection (A : Type) (n : nat)
  : section A (fin n) -&gt; { m &amp; A &lt;--&gt; fin m }.
Admitted. (* Hard exercise *)</code></pre>
<p>This construction is rather involved. It is much more general than when we
were looking specifically at union and intersection, but at the same time it
is easier to come up with as it abstracts away the distracting details of those
operations.</p>
<p>Now there is a section from <code>X ∪ Y</code> to <code>X + Y</code>,
and from <code>X ∩ Y</code> to <code>X</code>.</p>
<pre class="alectryon"><code>Definition section_union {A} (X Y : set_of A)
  : section (X ∪ Y)%set_of (X + Y).
Admitted. (* Easy exercise *)

Definition section_intersection {A} (X Y : set_of A)
  : section (X ∩ Y)%set_of X.
Admitted. (* Easy exercise *)</code></pre>
<p>We can then rely on the finiteness of <code>X</code> and <code>X + Y</code> to extend those
sections to <code>fin n</code> for some <code>n</code> via the following theorem:</p>
<pre class="alectryon"><code>Theorem section_extend (A B C : Type)
  : section A B -&gt; (B &lt;--&gt; C) -&gt; section A C.
Admitted. (* Easy exercise *)

Definition section_union&#39; {A} (X Y : finite_set_of A)
  : section (X ∪ Y)%set_of (fin (#|X| + #|Y|)).
Proof.
  eapply section_extend.
  - apply section_union.
  - apply is_finite_sum.
Qed.

Definition section_intersection&#39; {A} (X Y : finite_set_of A)
  : section (X ∩ Y)%set_of (fin #|X|).
Proof.
  eapply section_extend.
  - apply section_intersection.
  - apply enum.
Qed.</code></pre>
<p>Finally, by <code>section_bijection</code>, we obtain finiteness proofs of <code>union'</code> and
<code>intersection'</code>, which let us define <code>union</code> and <code>intersection</code> properly as operations
on finite sets.</p>
<pre class="alectryon"><code>Theorem is_finite_union {A} {X Y : set_of A}
    (FX : is_finite X) (FY : is_finite Y)
  : is_finite (X ∪ Y)%set_of.
Proof.
  refine {| enum := projT2 (section_bijection _) |}.
  eapply (section_extend (B := X + Y)%type).
  - apply section_union.
  - apply (is_finite_sum FX FY).
Qed.

Theorem is_finite_intersection {A} {X Y : set_of A}
    (FX : is_finite X) (FY : is_finite Y)
  : is_finite (X ∩ Y)%set_of.
Proof.
  refine {| enum := projT2 (section_bijection _) |}.
  eapply section_extend.
  - apply section_intersection.
  - apply (enum FX).
Qed.

Definition union {A} (X Y : finite_set_of A) : finite_set_of A :=
  {| fso_is_finite := is_finite_union X Y |}.

Definition intersection {A} (X Y : finite_set_of A) : finite_set_of A :=
  {| fso_is_finite := is_finite_intersection X Y |}.</code></pre>
<pre class="alectryon"><code>Declare Scope fso_scope.
Delimit Scope fso_scope with fso.
Bind Scope fso_scope with finite_set_of.

Infix &quot;∪&quot; := union (at level 40) : fso_scope.
Infix &quot;∩&quot; := intersection (at level 40) : fso_scope.</code></pre>
<p>Hereafter, <code>∪</code> and <code>∩</code> will denote finite unions and intersections.</p>
<pre class="alectryon"><code>#[local] Open Scope fso_scope.</code></pre>
<h2 id="inclusion-exclusion">Inclusion-exclusion</h2>
<pre><code>#|X ∪ Y| = #|X| + #|Y| - #|X ∩ Y|</code></pre>
<p>To prove that formula, it’s probably not a good idea to look at how <code>∪</code> and <code>∩</code>
compute their cardinalities. A better idea is to construct a bijection, which
implies an equality of cardinalities by <code>card_bijection</code>.</p>
<p>To start, subtractions are bad, so we rewrite the goal:</p>
<pre><code>#|X ∪ Y| + #|X ∩ Y| = #|X| + #|Y|</code></pre>
<p>Now we look for a bijection <code>(X ∪ Y) + (X ∩ Y) &lt;--&gt; X + Y</code>.
It gets a bit tricky because of the dependent types.</p>
<pre class="alectryon"><code>Definition inclusion_exclusion_bijection {A} (X Y : finite_set_of A)
  : (X ∪ Y)%set_of + (X ∩ Y)%set_of &lt;--&gt; X + Y.
Admitted. (* Hard exercise *)</code></pre>
<p>Isomorphic sets have the same cardinality (by theorem <code>card_bijection_finite_type</code>).
The resulting equation simplifies to the binary inclusion-exclusion identity,
because <code>#|A + B|</code> equals <code>#|A| + #|B|</code> definitionally.
So the proof consists simply of applying that theorem with the above bijection.</p>
<pre class="alectryon"><code>Theorem inclusion_exclusion {A} (X Y : finite_set_of A)
  : #|X ∪ Y| + #|X ∩ Y| = #|X| + #|Y|.
Proof.
  apply (@card_bijection_finite_type ((X ∪ Y) + (X ∩ Y)) (X + Y)).
  apply inclusion_exclusion_bijection.
Qed.</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>To formalize mathematics, it’s often useful to revisit our preconceptions about
fundamental concepts. To carry out even basic combinatorics in type theory, it’s
useful to distinguish two views of the naive notion of set.</p>
<p>For example, when we say “union”, we really mean one of two things depending on
the context. Either the sets are obviously disjoint, so we really mean “sum”:
this corresponds to viewing sets as types. Or we implicitly know that the two
sets contain the same “type” of elements a priori, so the overlap is something
we have to worry about explicitly: this corresponds to viewing sets as
predicates on a given universe.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Ironically, when making restaurant reservations, I still
occasionally forget to count myself.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>The code from this post is part of this project I’ve started
<a href="https://gitlab.com/lysxia/bijou">here</a>. Also check out Brent Yorgey’s thesis:
<a href="http://ozark.hendrix.edu/~yorgey/pub/thesis.pdf"><em>Combinatorial Species and Labelled Structures</em></a> (2014).<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>Speaking of sets, it’s important to distinguish <em>naive set theory</em> from <em>axiomatic
set theory</em>.
Naive set theory is arguably what most people think of when they hear “set”.
It is a semi-formal system for organizing mathematics: there are sets,
they have elements, and there are various operations to construct and analyze
sets, but overall we don’t think too hard about what sets <em>are</em> (hence,
“semi-formal”). When this blog post talks about sets, it is in the context of
naive set theory. Axiomatic set theory is formal, with rules that are clear
enough to be encoded in a computer. The name “axiomatic set theory” is
a stroke of marketing genius, establishing it as the “standard” way of
formalizing naive set theory, and thus, all of mathematics, as can be seen
in most introductory courses on formal logic. Historically, Zermelo’s set
theory was formulated at around the same time as Russell’s type theory,
and type theory is at the root of currently very active areas of programming
language theory and formal methods.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4"><p>Bijections actually form a groupoid (a “proof-relevant equivalence relation”).<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5"><p>We could also have defined <code>fin</code> as the inductive type of bounded naturals,
which is named <code>Fin.t</code> in the standard library. Anecdotal experience suggests
that the sigma type is more beginner-friendly. But past a certain level
of familiarity, I think they are completely interchangeable.</p>
<pre class="alectryon"><code>Inductive fin&#39; : nat -&gt; Type :=
| F0 : fin&#39; 1
| FS : forall n, fin&#39; n -&gt; fin&#39; (S n).</code></pre>
<p>The definition of <code>fin</code> as a sigma type relies on details of the definition of
the order relation <code>_ &lt; _</code>. Other definitions may allow the proposition <code>p &lt; n</code>
to be inhabited by multiple proof objects, causing <code>fin n</code> to have “more” than
<code>n</code> inhabitants unless they are collapsed by proof irrelevance.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn6"><p><em>math-comp</em> has a different but equivalent definition of <a href="https://math-comp.github.io/htmldoc/mathcomp.ssreflect.fintype.html"><code>fintype</code></a>.<a href="#fnref6" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn7"><p>… if you know what those words mean.<a href="#fnref7" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></summary>
</entry>

</feed>
