<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Vaibhav Sagar's blog</title>
    <subtitle>A blog with posts in it.</subtitle>
    <link href="https://vaibhavsagar.com/atom.xml" rel="self" />
    <link href="https://vaibhavsagar.com" />
    <id>https://vaibhavsagar.com/atom.xml</id>
    <author>
        <name>Vaibhav Sagar</name>
        
        <email>vaibhavsagar@gmail.com</email>
        
    </author>
    <updated>2025-10-22T00:00:00Z</updated>
    <entry>
    <title>SATisfying Solutions to Difficult Problems!</title>
    <link href="https://vaibhavsagar.com/blog/2025/10/22/satisfying-solutions/" />
    <id>https://vaibhavsagar.com/blog/2025/10/22/satisfying-solutions/index.html</id>
    <published>2025-10-22</published>
    <updated>2025-10-22T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on 22 October 2025
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>
    
</div>

<p><em>This post covers the same material as <a href="https://www.youtube.com/watch?v=J0ZLCHHku6U">my !!Con 2024
talk</a>, for which the slides are
<a href="https://vaibhavsagar.com/presentations/sat-solvers/">here</a>.</em></p>
<p>What are SAT solvers, and how are they useful? Let’s start by briefly touching on
<a href="https://en.wikipedia.org/wiki/NP-completeness">NP-complete problems</a>!</p>
<h1 id="np-complete-problems">NP-complete problems</h1>
<p>NP-complete problems are <a href="https://en.wikipedia.org/wiki/Decision_problem">decision
problems</a>, i.e. the solution to
them is “yes” or “no”. When these solutions exist, they can be verified in
<a href="https://en.wikipedia.org/wiki/Time_complexity#Polynomial_time">polynomial
time</a>, but we
don’t know how to find solutions in polynomial time, or even if this is
possible at all (this is the <a href="https://en.wikipedia.org/wiki/P_versus_NP_problem">P versus NP
problem</a>). An important
characteristic of NP-complete problems is that any NP-complete problem can be
reduced to any other NP-complete problem. Examples of NP-complete problems include:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Knapsack_problem">Knapsack problem</a></li>
<li><a href="https://en.wikipedia.org/wiki/Travelling_salesman_problem">Travelling salesman problem</a></li>
<li><a href="https://en.wikipedia.org/wiki/Subset_sum_problem">Subset sum problem</a></li>
<li><a href="https://en.wikipedia.org/wiki/Graph_coloring">Graph colouring problem</a></li>
<li><a href="https://en.wikipedia.org/wiki/Sudoku">Sudoku</a></li>
<li><a href="https://en.wikipedia.org/wiki/Boolean_satisfiability_problem">Boolean satisfiability problem</a></li>
</ul>
<p>We’re specifically interested in the boolean satisfiability problem.</p>
<h2 id="boolean-satisfiability-problem">Boolean satisfiability problem</h2>
<p>One definition of the boolean satisfiability problem is</p>
<blockquote>
<p>Given a propositional logic formula, can we assign truth values to each
variable such that the formula is satisfied?</p>
</blockquote>
<p>When working with these formulas, we commonly express them in <a href="https://en.wikipedia.org/wiki/Conjunctive_normal_form">Conjunctive
normal form</a> as
a conjunction (ANDed together) of clauses that consist of a series of
disjunctions (ORed together) of literals, e.g.</p>
<p><span class="math display">\[(x \vee y \vee z) \wedge (x \vee \neg y) \wedge (\neg y \vee \neg x) \wedge (\neg z)\]</span></p>
<p>With this context, I can finally tell you what SAT solvers are!</p>
<h1 id="sat-solvers">SAT solvers</h1>
<p>SAT solvers are programs that solve boolean satisfiability problems by
providing satisfying assignments (when they exist)! In other words, they are
programs for solving NP-complete problems expressed as instances of the boolean
satisfiability problem(!!)</p>
<h1 id="sudoku">Sudoku</h1>
<p>To demonstrate how this works in practice, let’s look at how to <em>reduce</em> Sudoku
to a Boolean satisfiability problem.</p>
<p>The rules of Sudoku are as follows:</p>
<ul>
<li>Each cell contains exactly one digit</li>
<li>Each digit occurs once per row</li>
<li>Each digit occurs once per column</li>
<li>Each digit occurs once per sub-grid</li>
<li>The solution must use the filled-in cells</li>
</ul>
<p>A useful insight is that we can use <span class="math inline">\(n\)</span> boolean variables to represent each
digit when we add the constraint that at most one of those variables can be
true.</p>
<p>Now we can express those rules in propositional logic:</p>
<h2 id="each-cell-has-at-least-one-value">Each cell has at least one value</h2>
<p><span class="math display">\[\displaylines{
\definecolor{comment}{RGB}{161,161,180}
{\color{comment}\textit{\small{row 1, column 1 is one of 1,2,...9}}} \\
(x_{1,1,1} \vee x_{1,1,2} \vee \dots \vee x_{1,1,9}) \wedge \\
{\color{comment}\textit{\small{row 1, column 2 is one of 1,2,...9}}} \\
(x_{1,2,1} \vee x_{1,2,2} \vee \dots \vee x_{1,2,9}) \wedge \\
\dots \\
{\color{comment}\textit{\small{row 9, column 9 is one of 1,2,...9}}} \\
(x_{9,9,1} \vee x_{9,9,2} \vee \dots \vee x_{9,9,9})
}\]</span></p>
<h2 id="each-cell-has-at-most-one-value">Each cell has at most one value</h2>
<p><span class="math display">\[\displaylines{
\definecolor{comment}{RGB}{161,161,180}
{\color{comment}\textit{\small{row 1, column 1 is not both 1 and 2}}} \\
(\neg x_{1,1,1} \vee \neg x_{1,1,2}) \wedge \\
{\color{comment}\textit{\small{row 1, column 1 is not both 1 and 3}}} \\
(\neg x_{1,1,1} \vee \neg x_{1,1,3}) \wedge \\
 \dots \\
{\color{comment}\textit{\small{row 9, column 9 is not both 8 and 9}}} \\
(\neg x_{9,9,8} \vee \neg x_{9,9,9})}\]</span></p>
<h2 id="each-row-has-all-values">Each row has all values</h2>
<p><span class="math display">\[\displaylines{
\definecolor{comment}{RGB}{161,161,180}
{\color{comment}\textit{\small{row 1 has a 1}}} \\
(x_{1,1,1} \vee x_{1,2,1} \vee \dots \vee x_{1,9,1}) \wedge \\
{\color{comment}\textit{\small{row 1 has a 2}}} \\
(x_{1,1,2} \vee x_{1,2,2} \vee \dots \vee x_{1,9,2}) \wedge \\
\dots \\
{\color{comment}\textit{\small{row 9 has a 9}}} \\
(x_{9,1,9} \vee x_{9,2,9} \vee \dots \vee x_{9,9,9})}\]</span></p>
<h2 id="each-column-has-all-values">Each column has all values</h2>
<p><span class="math display">\[\displaylines{
\definecolor{comment}{RGB}{161,161,180}
{\color{comment}\textit{\small{column 1 has a 1}}} \\
(x_{1,1,1} \vee x_{2,1,1} \vee \dots \vee x_{9,1,1}) \wedge \\
{\color{comment}\textit{\small{column 1 has a 2}}} \\
(x_{1,1,2} \vee x_{2,1,2} \vee \dots \vee x_{9,1,2}) \wedge \\
\dots \\
{\color{comment}\textit{\small{column 9 has a 9}}} \\
(x_{1,9,9} \vee x_{2,9,9} \vee \dots \vee x_{9,9,9})}\]</span></p>
<h2 id="each-sub-grid-has-all-values">Each sub-grid has all values</h2>
<p><span class="math display">\[\displaylines{
\definecolor{comment}{RGB}{161,161,180}
{\color{comment}\textit{\small{sub-grid 1 has a 1}}} \\
(x_{1,1,1} \vee x_{1,2,1} \vee \dots \vee x_{3,3,1}) \wedge \\
{\color{comment}\textit{\small{sub-grid 1 has a 2}}} \\
(x_{1,1,2} \vee x_{1,2,2} \vee \dots \vee x_{3,3,2}) \wedge \\
\dots \\
{\color{comment}\textit{\small{sub-grid 9 has a 9}}} \\
(x_{7,7,9} \vee x_{7,8,9} \vee \dots \vee x_{9,9,9})}\]</span></p>
<h2 id="the-solution-must-use-the-filled-in-cells">The solution must use the filled-in cells</h2>
<p>For a puzzle such as</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/e/e0/Sudoku_Puzzle_by_L2G-20050714_standardized_layout.svg" /></p>
<p><em>Tim Stellmach, CC0, via Wikimedia Commons</em></p>
<p>This looks like</p>
<p><span class="math display">\[\displaylines{
x_{1,1,5} \wedge x_{1,2,3} \wedge x_{1,5,7} \wedge \\
x_{2,1,6} \wedge x_{2,4,1} \wedge x_{2,5,9} \wedge x_{2,6,5} \wedge \\
\dots \\
x_{9,5,8} \wedge x_{9,8,7} \wedge x_{9,9,9}}\]</span></p>
<h2 id="solving">Solving</h2>
<p>To solve this Sudoku (or indeed any NP-complete problem that we have expressed
as a Boolean satisfiability problem), all we need to do is provide the
resulting propositional logic formula as input to a SAT solver!</p>
<p>How do these marvellous programs work?</p>
<h1 id="dpll">DPLL</h1>
<p>One algorithm is known as DPLL. To explain how it works, let’s look at an
example. Not so coincidentally, this is the same propositional logic formula
from earlier! At the beginning, we don’t know the values of <span class="math inline">\(x\)</span>, <span class="math inline">\(y\)</span>, or <span class="math inline">\(z\)</span>.</p>
<p><span class="math display">\[(x \vee y \vee z) \wedge (x \vee \neg y) \wedge (\neg y \vee \neg x) \wedge (\neg z)\]</span></p>
<p><span class="math inline">\(x\)</span>: 🤷<br/>
<span class="math inline">\(y\)</span>: 🤷<br/>
<span class="math inline">\(z\)</span>: 🤷<br/></p>
<p>We begin by picking a variable and assigning it a truth value, preferring unit
clauses (clauses with a single literal). In this case, let’s set <span class="math inline">\(z\)</span> to <span class="math inline">\(False\)</span>.
Now we can perform <a href="https://en.wikipedia.org/wiki/Unit_propagation">unit
propagation</a>.</p>
<h2 id="unit-propagation">Unit propagation</h2>
<p>When performing unit propagation, we assign the appropriate truth value to
a literal, which is obvious when it occurs in a unit clause. Then we remove all
clauses that are satisfied, since we don’t need to consider them going forward.
Next we remove the literal where it is <span class="math inline">\(False\)</span>, since it cannot contribute to
that clause being satisfied.</p>
<p><span class="math display">\[(x \vee y \vee \cancel{{\color{red} z}}) \wedge (x \vee \neg y) \wedge (\neg y \vee \neg x) \wedge \cancel{{\color{green} (\neg z)}}\]</span></p>
<p><span class="math inline">\(x\)</span>: 🤷<br/>
<span class="math inline">\(y\)</span>: 🤷<br/>
<span class="math inline">\(z\)</span>: <code>False</code><br/></p>
<p>Next we pick another variable and continue. Let’s set <span class="math inline">\(y\)</span> to <span class="math inline">\(True\)</span>.</p>
<p><span class="math display">\[\cancel{{\color{green}(x \vee y \vee z)}} \wedge (x \vee \cancel{{\color{red} \neg y}}) \wedge (\cancel{{\color{red} \neg y}} \vee \neg x) \wedge \cancel{{\color{green}(\neg z)}}\]</span></p>
<p><span class="math inline">\(x\)</span>: 🤷<br/>
<span class="math inline">\(y\)</span>: <code>True</code><br/>
<span class="math inline">\(z\)</span>: <code>False</code><br/></p>
<p>Unfortunately, we now have a conflict, since two of the remaining clauses are <span class="math inline">\(x\)</span> and <span class="math inline">\(\neg x\)</span>.</p>
<p><span class="math display">\[\cancel{{\color{green}(x \vee y \vee z)}} \wedge ({\color{blue} x} \vee \cancel{{\color{red} \neg y}}) \wedge (\cancel{{\color{red} \neg y}} \vee {\color{blue}\neg x}) \wedge \cancel{{\color{green}(\neg z)}}\]</span></p>
<p><span class="math inline">\(x\)</span>: 🤷<br/>
<span class="math inline">\(y\)</span>: <code>True</code><br/>
<span class="math inline">\(z\)</span>: <code>False</code><br/></p>
<p>The only appropriate thing to do here is backtrack, so we undo our previous assignment and try the other truth value, setting <span class="math inline">\(y\)</span> to <span class="math inline">\(False\)</span>.</p>
<p><span class="math display">\[(x \vee \cancel{{\color{red} y}} \vee \cancel{{\color{red} z}}) \wedge \cancel{{\color{green}(x \vee \neg y)}} \wedge \cancel{{\color{green} (\neg y \vee \neg x)}} \wedge \cancel{{\color{green} (\neg z)}}\]</span></p>
<p><span class="math inline">\(x\)</span>: 🤷<br/>
<span class="math inline">\(y\)</span>: <code>False</code><br/>
<span class="math inline">\(z\)</span>: <code>False</code><br/></p>
<p>Now we can perform <a href="https://en.wikipedia.org/wiki/DPLL_algorithm#The_algorithm">pure literal elimination</a>.</p>
<h2 id="pure-literal-elimination">Pure literal elimination</h2>
<p>When literals involving a variable in a propositional logic formula are either
always <span class="math inline">\(True\)</span> (<span class="math inline">\(x\)</span>) or always <span class="math inline">\(False\)</span> (<span class="math inline">\(\neg x\)</span>), then these are called <em>pure</em>
literals, and it’s obvious what truth value to assign to them. In this case we set <span class="math inline">\(x\)</span> to <span class="math inline">\(True\)</span>.</p>
<p><span class="math display">\[\cancel{{\color{green}(x \vee y \vee z)}} \wedge \cancel{{\color{green}(x \vee \neg y)}} \wedge \cancel{{\color{green} (\neg y \vee \neg x)}} \wedge \cancel{{\color{green} (\neg z)}}\]</span></p>
<p><span class="math inline">\(x\)</span>: <code>True</code><br/>
<span class="math inline">\(y\)</span>: <code>False</code><br/>
<span class="math inline">\(z\)</span>: <code>False</code><br/></p>
<p>And that’s DPLL (Davis-Putnam-Logemann-Loveland)!</p>
<h2 id="davis-putnam-logemann-loveland">Davis-Putnam-Logemann-Loveland</h2>
<p>Davis-Putnam-Logemann-Loveland is exhaustive backtracking search with unit
propagation and pure literal elimination. Although it works reasonably well for
small numbers of clauses, it has a tendency to repeatedly run into the same
conflicts, and when it does it only backtracks one level at a time. It would be
great to somehow remember and learn from these conflicts when we encounter
them, so we can scale up to more complex problems. Does such an algorithm
exist? It does.</p>
<h1 id="cdcl">CDCL</h1>
<p>CDCL starts out very similarly to DPLL in that it still features unit
propagation and pure literal elimination. However it also involves additional
bookkeeping, distinguishing between decisions (when we choose a truth value for
a variable) and implications (truth values determined through unit propagation
and pure literal elimination). It also keeps track of the <a href="https://en.wikipedia.org/wiki/Implication_graph">implication
graph</a> created by decisions
and implications.</p>
<p>Let’s look at a more complex example.</p>
<p><span class="math display">\[\begin{align}
&amp; (a \vee d) \wedge \\
&amp; (a \vee \neg c \vee \neg f) \wedge \\
&amp; (a \vee f \vee j) \wedge \\
&amp; (b \vee i) \wedge \\
&amp; (\neg e \vee \neg c \vee g) \wedge \\
&amp; (\neg e \vee f \vee \neg g) \wedge \\
&amp; (e \vee f \vee \neg h) \wedge \\
&amp; (e \vee h \vee \neg j)
\end{align}\]</span></p>
<p>We start by setting <span class="math inline">\(a\)</span> to <span class="math inline">\(False\)</span>, which implies <span class="math inline">\(d\)</span> through the clause <span class="math inline">\(a \vee d\)</span>.</p>
<div style="display: flex">
<div style="flex: 33%">
<img src="/images/tree1x1.svg" />
</div>
<div style="flex: 33%">
<img src="/images/graph1x1.svg" />
</div>
<div style="flex: 33%">
<p><span class="math display">\[\begin{align}
&amp; {\color{red} a} \vee {\color{green} d} \\
&amp; {\color{red} a} \vee \neg c \vee \neg f \\
&amp; {\color{red} a} \vee f \vee j \\
&amp; b \vee i \\
&amp; \neg e \vee \neg c \vee g \\
&amp; \neg e \vee f \vee \neg g \\
&amp; e \vee f \vee \neg h \\
&amp; e \vee h \vee \neg j
\end{align}\]</span></p>
</div>
</div>
<p>Next we set <span class="math inline">\(c\)</span> to <span class="math inline">\(True\)</span>, which implies <span class="math inline">\(\neg f\)</span> through the clause <span class="math inline">\(a \vee
\neg c \vee \neg f\)</span> and <span class="math inline">\(j\)</span> through the clause <span class="math inline">\(a \vee f \vee j\)</span>.</p>
<div style="display: flex">
<div style="flex: 33%">
<img src="/images/tree1x3.svg" />
</div>
<div style="flex: 33%">
<img src="/images/graph1x3.svg" />
</div>
<div style="flex: 33%">
<p><span class="math display">\[\begin{align}
&amp; {\color{red} a} \vee {\color{green} d} \\
&amp; {\color{red} a} \vee {\color{red}\neg c} \vee {\color{green} \neg f} \\
&amp; {\color{red} a} \vee {\color{red} f} \vee {\color{green} j} \\
&amp; b \vee i \\
&amp; \neg e \vee {\color{red}\neg c} \vee g \\
&amp; \neg e \vee {\color{red} f} \vee \neg g \\
&amp; e \vee {\color{red} f} \vee \neg h \\
&amp; e \vee h \vee {\color{red}\neg j}
\end{align}\]</span></p>
</div>
</div>
<p>We continue by setting <span class="math inline">\(b\)</span> to <span class="math inline">\(False\)</span>, which implies <span class="math inline">\(i\)</span> through the clause <span class="math inline">\(b
\vee i\)</span>.</p>
<div style="display: flex">
<div style="flex: 33%">
<img src="/images/tree1x2.svg" />
</div>
<div style="flex: 33%">
<img src="/images/graph1x2.svg" />
</div>
<div style="flex: 33%">
<p><span class="math display">\[\begin{align}
&amp; {\color{red} a} \vee {\color{green} d} \\
&amp; {\color{red} a} \vee {\color{red}\neg c} \vee {\color{green} \neg f} \\
&amp; {\color{red} a} \vee {\color{red} f} \vee {\color{green} j} \\
&amp; {\color{red} b} \vee {\color{green} i} \\
&amp; \neg e \vee {\color{red}\neg c} \vee g \\
&amp; \neg e \vee {\color{red} f} \vee \neg g \\
&amp; e \vee {\color{red} f} \vee \neg h \\
&amp; e \vee h \vee {\color{red}\neg j}
\end{align}\]</span></p>
</div>
</div>
<p>Then we set <span class="math inline">\(e\)</span> to <span class="math inline">\(True\)</span>, which causes a conflict because <span class="math inline">\(g\)</span> is implied to be
both <span class="math inline">\(True\)</span> (through the clause <span class="math inline">\(\neg e \vee \neg c \vee g\)</span>) and <span class="math inline">\(False\)</span>
(through the clause <span class="math inline">\(\neg e \vee f \vee \neg g\)</span>).</p>
<div style="display: flex">
<div style="flex: 33%">
<img src="/images/tree1.svg" />
</div>
<div style="flex: 33%">
<img src="/images/graph1.svg" />
</div>
<div style="flex: 33%">
<p><span class="math display">\[\begin{align}
&amp; {\color{red} a} \vee {\color{green} d} \\
&amp; {\color{red} a} \vee {\color{red}\neg c} \vee {\color{green} \neg f} \\
&amp; {\color{red} a} \vee {\color{red} f} \vee {\color{green} j} \\
&amp; {\color{red} b} \vee {\color{green} i} \\
&amp; {\color{red}\neg e} \vee {\color{red}\neg c} \vee {\color{blue} g} \\
&amp; {\color{red}\neg e} \vee {\color{red} f} \vee {\color{blue}\neg g} \\
&amp; {\color{green} e} \vee {\color{red} f} \vee \neg h \\
&amp; {\color{green} e} \vee h \vee {\color{red}\neg j}
\end{align}\]</span></p>
</div>
</div>
<h2 id="clause-learning">Clause learning</h2>
<p>Fortunately we can analyse the implication graph to determine a <a href="https://users.aalto.fi/~tjunttil/2020-DP-AUT/notes-sat/cdcl.html#implication-graphs-learned-clauses-and-backjumping">Unique
Implication
Point</a>
that all edges from the latest decision node to the conflict node pass through,
and the corresponding <em>UIP cut</em> corresponding to a clause. In this case the
UIP is the decision node <span class="math inline">\(e\)</span> and the clause is <span class="math inline">\(\neg f \vee c \vee e\)</span>. We want
to remove the possibility of reaching this state again, so we negate this
clause (by De Morgan’s theorem).</p>
<div style="display: flex">
<div style="flex: 50%">
<img src="/images/graph2.svg" />
</div>
<div style="flex: 50%">
<p><span class="math display">\[\displaylines{\neg (\neg f \wedge c \wedge e) \\
\iff \\
(f \vee \neg c \vee \neg e)}\]</span></p>
</div>
</div>
<p>And this gives us our <em>learned clause</em>!</p>
<p><span class="math display">\[(f \vee \neg c \vee \neg e)\]</span></p>
<p>We can then add it to our formula.</p>
<p><span class="math display">\[\begin{align}
\definecolor{comment}{RGB}{161,161,180}
\definecolor{emphasis}{RGB}{88,110,117}
&amp; {\color{comment}(a \vee d) \wedge} \\
&amp; {\color{comment}(a \vee \neg c \vee \neg f) \wedge} \\
&amp; {\color{comment}(a \vee f \vee j) \wedge} \\
&amp; {\color{comment}(b \vee i) \wedge} \\
&amp; {\color{comment}(\neg e \vee \neg c \vee g) \wedge} \\
&amp; {\color{comment}(\neg e \vee f \vee \neg g) \wedge} \\
&amp; {\color{comment}(e \vee f \vee \neg h) \wedge} \\
&amp; {\color{comment}(e \vee h \vee \neg j) \wedge} \\\
&amp; (f \vee \neg c \vee \neg e)
\end{align}\]</span></p>
<h2 id="non-chronological-backjumping">Non-chronological backjumping</h2>
<p>Next we <em>backjump non-chronologically</em> to the second-highest decision level of
the literals in our clause, which in this case is <span class="math inline">\(2\)</span>, and repeat.</p>
<p><img src="/images/tree2.svg" /></p>
<p>That’s CDCL (Conflict-driven clause learning)!</p>
<h2 id="conflict-driven-clause-learning">Conflict-driven Clause Learning</h2>
<p>Conflict-driven clause learning is an extension of DPLL with learned clauses
and non-chronological backtracking, effectively addressing most of DPLL’s
downsides. It forms the basis of most modern SAT solvers.</p>
<h1 id="sls">SLS</h1>
<p>In contrast to the rigorous and structured approaches we’ve seen already, what
if we tried something more ad-hoc? We could generate a random assignment of
<span class="math inline">\(True\)</span> and <span class="math inline">\(False\)</span> values for each of our variables, pick a clause at random,
and flip either the “best” variable (whose negation causes the fewest
conflicts) or some other variable. We could loop this selection and flipping
a number of times, and restart the whole assignment upon getting stuck,
finishing after either finding a solution or after a predetermined number of
tries.</p>
<h2 id="stochastic-local-search">Stochastic Local Search</h2>
<p>This is known as stochastic local search and it’s surprisingly effective! The
specific algorithm I described above is called
<a href="https://en.wikipedia.org/wiki/WalkSAT">WalkSAT</a> and it’s possible to <a href="https://www.ml.cmu.edu/research/dap-papers/dap_mcdonald.pdf">do it in
parallel and use a form of clause
learning</a>.
Unfortunately, this approach cannot conclusively determine unsatisfiability
because an inconclusive result might be due to the solver running out of
attempts. A more general technique that this family of algorithms reminds me of
is called <a href="https://en.wikipedia.org/wiki/Simulated_annealing">simulated
annealing</a>.</p>
<h1 id="smt">SMT</h1>
<p>SAT solvers are great when it’s straightforward to express your problem as
a boolean satisfiability problem, but what if we want to solve more complex
problems where we might not have the ability or desire to do so?</p>
<p><span class="math display">\[
\begin{align}
  SEND &amp;\\
+ MORE &amp;\\
\hline
MONEY
\end{align}
\]</span></p>
<p>To solve this puzzle, we need to assign digit values to each of the letters
such that the sum of the 4-digit number represented by <span class="math inline">\(SEND\)</span> and the 4-digit
number represented by <span class="math inline">\(MORE\)</span> equals the 5-digit number <span class="math inline">\(MONEY\)</span>.</p>
<p>We’d have to essentially teach our SAT solver how to do enough arithmetic for
it to solve the equation</p>
<p><span class="math display">\[
\begin{align}
  (1000 \cdot (S+M)) + (100 \cdot (E+O)) + (10 \cdot (N+R)) + (D+E) &amp;\\
= (10000 \cdot M) + (1000 \cdot O) + (100 \cdot N) + (10 \cdot E) + Y
\end{align}
\]</span></p>
<h2 id="satisfiability-modulo-theories">Satisfiability Modulo Theories</h2>
<p>This is what an SMT (Satisfiability Modulo Theories) solver is! A SAT solver
can be extended to reason over bitvectors, fixed-length arrays, Presburger
arithmetic, algebraic datatypes, and more. One way of doing this would be to
pre-process a problem into a series of CNF clauses that can be fed to a normal
SAT solver; this is known as
<a href="https://www.cs.cmu.edu/~15414/s23/lectures/21-bitblasting.pdf">bit-blasting</a>,
but deeper integrations are often more practical. Examples of SMT solvers
include <a href="https://github.com/Z3Prover/z3">Z3</a>, <a href="https://cvc5.github.io/">CVC5</a>,
<a href="https://yices.csl.sri.com/">Yices</a>, and
<a href="https://bitwuzla.github.io/">Bitwuzla</a>.</p>
<h1 id="thats-all">That’s all!</h1>
<p>I hope you have a basic understanding of what a SAT/SMT solver is, how they
(broadly) work, and when it might be a good idea to use them!</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=6K6HFl7UhQk">Lindsey Kuper - Reasoning Under Uncertainty in SMT Solving, Research, and
Life</a></li>
<li><a href="https://cacm.acm.org/research/the-science-of-brute-force/">The Science of Brute
Force</a></li>
<li><a href="https://www.iospress.com/catalog/books/handbook-of-satisfiability-2">Handbook of
Satisfiability</a></li>
<li><a href="https://www.cs.cmu.edu/~emc/15-820A/reading/grasp_iccad96.pdf">GRASP - A New Search Algorithm for Satisfiability (1996)</a></li>
<li><a href="http://minisat.se/downloads/MiniSat.pdf">An Extensible SAT-solver (2003)</a></li>
<li><a href="https://users.aalto.fi/~tjunttil/2020-DP-AUT/notes-sat/overview.html">CS-E3220: Propositional satisfiability and SAT
solvers</a></li>
<li><a href="https://codingnest.com/tag/sat/">Modern SAT solvers: fast, neat and underused</a></li>
</ul>
<p><em>Thanks to <a href="https://gitlab.com/awjchen">Alex Chen</a> for multiple rounds of
excellent feedback on my presentation.</em></p>
</summary>
</entry>
<entry>
    <title>GHCi in the Browser</title>
    <link href="https://vaibhavsagar.com/blog/2024/07/03/ghci-in-the-browser/" />
    <id>https://vaibhavsagar.com/blog/2024/07/03/ghci-in-the-browser/index.html</id>
    <published>2024-07-03</published>
    <updated>2024-07-03T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on  3 July 2024
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;haskell&#39;." href="/blog/tags/haskell/index.html" rel="tag">haskell</a>, <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>
    
</div>

<p>I’m happy to announce that you can now run GHCi entirely in your browser (if
your browser supports WebAssembly and you’re willing to download approximately
220MB of compressed WASM).</p>
<h2 id="where">Where?</h2>
<p><a href="https://vaibhavsagar.com/amd64-ghc-wasi-demo">Here</a> or
<a href="https://vaibhavsagar.com/webvm/">here</a>.</p>
<h2 id="how">How?</h2>
<p>I used <a href="https://github.com/ktock/container2wasm"><code>container2wasm</code></a> to convert
an OCI image containing GHC to a WASM blob that I could serve using a lightly
modified <a href="https://github.com/ktock/container2wasm-demo"><code>container2wasm-demo</code></a>.
If you’re curious, the website repo is
<a href="https://github.com/vaibhavsagar/amd64-ghc-wasi-demo">here</a> and the chunks of
WASM are <a href="https://github.com/vaibhavsagar/amd64-ghc-wasi-container">here</a>.</p>
<p>As of this writing, only images with an uncompressed size below 2GB can be used
with <code>container2wasm</code> (tracked
<a href="https://github.com/ktock/container2wasm/issues/230">here</a>) and my initial
attempts using an OCI image generated by Nix were unsuccessful because of
duplicate filenames (tracked
<a href="https://github.com/ktock/container2wasm/issues/263">here</a>).</p>
<p>I also separately used <a href="https://webvm.io/">WebVM</a> which has the same file size
limitation as <code>container2wasm</code>, only works on <code>x86</code> binaries (which is why
I used the <code>i386</code> build of GHC), and is closed-source 😢, but potentially offers
better performance depending on how well the JIT compiler performs on this
workload.</p>
<h2 id="why">Why?</h2>
<p>I’ve wanted to do something like this for a long time. In my capacity as
a maintainer of <a href="https://github.com/IHaskell/IHaskell">IHaskell</a>, installation
issues are the most common category of support request I receive. Wouldn’t it
be great if a user could simply navigate to a webpage and have a correctly
configured Jupyter notebook waiting for them? The Jupyter folks also seem to be
thinking the same thing, based on the existence of
<a href="https://jupyterlite.readthedocs.io/en/stable/">JupyterLite</a>. Unfortunately
we’re a long way off from Haskell support<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>, but I hope my proof-of-concept
shows that this is possible.</p>
<p>Even outside Jupyter-land, a fully-functional GHCi REPL in the browser would be
generally useful. For example, currently
<a href="https://www.haskell.org/">Haskell.org</a> has a “Try it!” section where you can
enter expressions, which are currently passed to a backend server to execute.
A client-side GHCi could provide a better experience and allow us to get rid of
the backend entirely. Another wild idea: the Hackage documentation for
a package could provide a REPL with that package pre-installed for users to try
out immediately. Wouldn’t that be amazing?</p>
<h2 id="why-not-compile-ghci-directly-to-javascriptwasm-using-the-new-backends">Why not compile GHCi directly to JavaScript/WASM using the new backends?</h2>
<p>I don’t think that would work/result in a usable Haskell interpreter with
access to <code>base</code> or other GHC boot packages. As of this writing it is on <a href="https://gitlab.haskell.org/ghc/ghc/-/wikis/javascript-backend?version_id=bff087ec5b0231e12b3a8d902522f3d41aed530b">the
roadmap for GHC
9.12+</a>
so hopefully that will eventually be possible (tracked
<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25067">here</a>). If you get this
working I’d love to know about it!</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>It’s not something I’m working on and I don’t know how to go from this
Goldbergian blob of WASM to a kernel that would work with JupyterLite. If you
have ideas, please get in touch!<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
</summary>
</entry>
<entry>
    <title>The Real Hash Was the Friends We Made along the Way</title>
    <link href="https://vaibhavsagar.com/blog/2024/02/14/minimal-perfect-hashing/" />
    <id>https://vaibhavsagar.com/blog/2024/02/14/minimal-perfect-hashing/index.html</id>
    <published>2024-02-14</published>
    <updated>2024-02-14T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on 14 February 2024
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>
    
</div>

<p>When I lived in Singapore, I attended a fascinating talk at FOSSASIA 2018 about
<a href="https://www.youtube.com/watch?v=8Zu-EVjN24s">Indeed’s fast and compact immutable key-value
stores</a> that went almost
completely over my head. In fact, if you listen carefully during the Q&amp;A
session at the end, you can hear me ask some not-very-good questions in an
ill-advised and ultimately futile attempt to relate to the speaker.</p>
<p>This was my first encounter with the concept of minimal perfect hashing.
Unfortunately for me, I found most of the existing literature so impenetrable
that I gave up on learning more. <a href="https://cmph.sourceforge.net/papers/esa09.pdf">Hash, displace, and
compress</a>? <a href="https://cmph.sourceforge.net/papers/wads07.pdf">Hypergraph
peeling</a>?
<a href="https://arxiv.org/abs/1910.06416">RecSplit</a>? Eventually I found a suitable
entry point: <a href="https://arxiv.org/abs/1702.03154">Fast and scalable minimal perfect hashing for massive key
sets</a>.</p>
<h2 id="minimal-perfect-hashing">Minimal perfect hashing</h2>
<p>Let’s start with what minimal perfect hashing is:</p>
<h3 id="hashing">Hashing</h3>
<p>One definition of hashing is a process that converts some key to a value of
some fixed size (e.g. an integer). We can think of this in terms of a hash
<em>function</em> that takes some input and produces an integer as output.</p>
<h3 id="perfect">Perfect</h3>
<p>In practice, sometimes these hash functions produce the same output for
different inputs, known as a <em>hash collision</em>. This is pretty annoying and
causes lots of problems, and it would be nice if we could guarantee that
distinct inputs always hash to different values, i.e. that the function is
<a href="https://en.wikipedia.org/wiki/Injective_function"><em>injective</em></a>. Hash functions
with this useful property are known as <em>perfect hash functions</em>. This requires
all possible inputs to be known in advance.</p>
<figure>
<img src="//upload.wikimedia.org/wikipedia/commons/5/5c/Gen_injection_not_surjection.svg" alt="Injective function" />
<figcaption aria-hidden="true">Injective function</figcaption>
</figure>
<h3 id="minimal-perfect-hashing-1"><em>Minimal</em> perfect hashing</h3>
<p>Bringing it all together, a <em>minimal perfect hash</em> function is one that has no
gaps in its outputs, i.e. it
<a href="https://en.wikipedia.org/wiki/Bijection">bijectively</a> maps <span class="math inline">\(n\)</span> different
inputs to <span class="math inline">\(n\)</span> consecutive integers, e.g. <span class="math inline">\([0..n)\)</span> or <span class="math inline">\([1..n]\)</span>. It’s important
to note that <em>minimal</em> does not imply anything about the space or time
complexity of these functions, e.g. it would be totally valid to have an
internal hashtable that maps each input to a distinct integer without gaps and
then use that to implement our hash function. In practice, however, we want
these functions to be as efficient as possible to construct, store, and use,
and this is an active area of research.</p>
<figure>
<img src="//upload.wikimedia.org/wikipedia/commons/a/a5/Bijection.svg" alt="Bijective function" />
<figcaption aria-hidden="true">Bijective function</figcaption>
</figure>
<p>You’d probably want to use a minimal perfect hash when</p>
<ul>
<li>all possible keys are known in advance</li>
<li>the set of keys doesn’t change</li>
<li>space is at a premium</li>
</ul>
<p>One attractive property of a minimal perfect hash function is that you can use
it to create a minimal perfect hash <em>table</em> by associating it with an array
where each value’s index is the hash of the corresponding key.</p>
<h2 id="how-it-works">How it works</h2>
<p>The approach used in the paper is based on cascading collisionless bitarrays,
as illustrated below. I have a more detailed example later so if you aren’t
able to follow this one that’s totally okay! It exists to give you a quick
taste of the algorithm.</p>
<figure>
<img src="/images/cascading-collisionless-arrays.svg" alt="Cascading Collisionless Bitarrays" />
<figcaption aria-hidden="true"><em>Cascading Collisionless Bitarrays</em></figcaption>
</figure>
<p>In the example, keys <span class="math inline">\(k_1\)</span> to <span class="math inline">\(k_6\)</span> are hashed and positions where there are no
collisions are recorded. The keys that collide at each level are removed and
retried at the next level until all the keys are used. For the first bitarray
<span class="math inline">\(A_0\)</span>, <span class="math inline">\(k_3\)</span> and <span class="math inline">\(k_6\)</span> do not collide when using the hash function <span class="math inline">\(h_0\)</span>. For
the next bitarray <span class="math inline">\(A_1\)</span>, <span class="math inline">\(k_1\)</span> and <span class="math inline">\(k_5\)</span> do not collide when using <span class="math inline">\(h_1\)</span>.
Finally for <span class="math inline">\(A_2\)</span>, <span class="math inline">\(k_2\)</span> and <span class="math inline">\(k_4\)</span> do not collide using <span class="math inline">\(h_2\)</span> and we have no
more keys left. To compute the hash for a key, in this example <span class="math inline">\(k_2\)</span>, we find
the position where <span class="math inline">\(A_n[h_n(k_2)] \equiv 1\)</span> and count the number of <code>1</code>s at or
preceding this position, also known as the <em>rank</em>, which will always give us
a number <span class="math inline">\([1..n]\)</span>. For <span class="math inline">\(k2\)</span>, the hash is <span class="math inline">\(5\)</span>.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p>To implement this, we’ll need</p>
<ul>
<li>a family of hash functions</li>
<li>bitvectors supporting <a href="https://en.wikipedia.org/wiki/Succinct_data_structure#Succinct_indexable_dictionaries"><span class="math inline">\(rank\)</span> (and
<span class="math inline">\(select\)</span>)</a>
operations</li>
</ul>
<p>For the hash functions, I used
<a href="https://hackage.haskell.org/package/hashable-1.4.3.0/docs/Data-Hashable.html#v:hashWithSalt"><code>hashWithSalt</code></a>
from the <a href="https://hackage.haskell.org/package/hashable"><code>hashable</code></a> package,
and for the bitvectors I used the
<a href="https://hackage.haskell.org/package/bv-little"><code>bv-little</code></a> package because
<a href="https://github.com/recursion-ninja/bv-little/issues/3">past Vaibhav asked for <code>rank</code> and <code>select</code>
support</a>.</p>
<h3 id="construction">Construction</h3>
<p>At a high level, this is what the construction algorithm looks like:</p>
<ol type="1">
<li>Repeat the following steps until the maximum level is reached or we have no more keys:
<ol type="1">
<li>Hash each key to a number <span class="math inline">\(i \in [0..n)\)</span></li>
<li>If <span class="math inline">\(bitvector[i]\)</span> has not been set this iteration, set it to <span class="math inline">\(1\)</span>, otherwise unset it</li>
<li>Remove all keys that have been set successfully</li>
</ol></li>
<li>If there are any leftover keys, store them separately</li>
</ol>
<h4 id="hashing-1">Hashing</h4>
<p>As I mentioned previously, I used <code>hashWithSalt</code>:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">value</span><span> </span><span class="ot">=</span><span> </span><span class="va">hashWithSalt</span><span> </span><span class="va">currentLevel</span><span> </span><span class="va">key</span><span> </span><span class="ot">`</span><span class="va">mod</span><span class="ot">`</span><span> </span><span class="ot">(</span><span class="va">gamma</span><span> </span><span class="ot">*</span><span> </span><span class="va">currentLength</span><span class="ot">)</span></code></pre></div>
<p>The role of <code>gamma</code> is to control the amount of “slack” in the bitvector, since
sometimes making it larger than strictly necessary can reduce the probability
of collisions. More on this later.</p>
<h4 id="populating-the-bitvector">Populating the bitvector</h4>
<p>The approach described in the paper involves using an auxiliary bitvector <span class="math inline">\(C\)</span>
to keep track of collisions:</p>
<ol type="1">
<li>Initialise two bitvectors <span class="math inline">\(B\)</span> and <span class="math inline">\(C\)</span> with <span class="math inline">\(0\)</span>s</li>
<li>When setting an index <span class="math inline">\(i\)</span>:
<ol type="1">
<li>If <span class="math inline">\(B[i] \equiv 0\)</span> and <span class="math inline">\(C[i] \equiv 0\)</span> then set <span class="math inline">\(B[i] = 1\)</span></li>
<li>If <span class="math inline">\(B[i] \equiv 1\)</span> then set <span class="math inline">\(B[i] = 0\)</span> and <span class="math inline">\(C[i] = 1\)</span></li>
<li>If <span class="math inline">\(B[i] \equiv 0\)</span> and <span class="math inline">\(C[i] \equiv 1\)</span> then do nothing</li>
</ol></li>
</ol>
<h3 id="lookup">Lookup</h3>
<p>To actually use our hash function, we can do the following:</p>
<ol type="1">
<li>For each level:
<ol type="1">
<li>Hash the key and check if the corresponding index is set</li>
<li>If so, find the rank</li>
<li>If not, increment the level count and repeat</li>
</ol></li>
<li>Otherwise check the leftovers</li>
</ol>
<h2 id="example">Example</h2>
<p>Let’s look at a small example. The <a href="https://www.bonditocoogeewalk.com/">Bondi to Coogee
walk</a> here in Sydney passes through the
following beaches:</p>
<ul>
<li>Bondi</li>
<li>Tamarama</li>
<li>Bronte</li>
<li>Clovelly</li>
<li>Gordons Bay</li>
<li>Coogee</li>
</ul>
<p>and we can use these as keys for a minimal perfect hash function.</p>
<h3 id="construction-1">Construction</h3>
<p>The results of the first iteration are</p>
<details open="1">
<summary style="cursor: pointer">
Level 0
</summary>
<pre><code>┌─┐
│0│ &lt;- [&quot;Clovelly&quot;,&quot;Bronte&quot;]
├─┤
│1│ &lt;- [&quot;Gordons Bay&quot;]
├─┤
│0│
├─┤
│0│
├─┤
│0│ &lt;- [&quot;Coogee&quot;,&quot;Tamarama&quot;]
├─┤
│1│ &lt;- [&quot;Bondi&quot;]
└─┘</code></pre>
</details>
<p>So far, so good.</p>
<details open="1">
<summary style="cursor: pointer">
Level 1
</summary>
<pre><code>┌─┐
│0│
├─┤
│0│
├─┤
│0│
├─┤
│0│ &lt;- [&quot;Coogee&quot;,&quot;Clovelly&quot;,&quot;Bronte&quot;,&quot;Tamarama&quot;]
└─┘</code></pre>
</details>
<p>Hmm, that’s a little concerning.</p>
<details open="1">
<summary style="cursor: pointer">
Level 2
</summary>
<pre><code>┌─┐
│0│ &lt;- [&quot;Coogee&quot;,&quot;Clovelly&quot;,&quot;Bronte&quot;,&quot;Tamarama&quot;]
├─┤
│0│
├─┤
│0│
├─┤
│0│
└─┘</code></pre>
</details>
<p>This is not going well.</p>
<details open="1">
<summary style="cursor: pointer">
Level 3
</summary>
<pre><code>┌─┐
│0│
├─┤
│0│ &lt;- [&quot;Coogee&quot;,&quot;Clovelly&quot;,&quot;Bronte&quot;,&quot;Tamarama&quot;]
├─┤
│0│
├─┤
│0│
└─┘</code></pre>
</details>
<p>It’s like the algorithm is taunting me.</p>
<details open="1">
<summary style="cursor: pointer">
Level 4
</summary>
<pre><code>┌─┐
│0│
├─┤
│0│
├─┤
│0│ &lt;- [&quot;Coogee&quot;,&quot;Clovelly&quot;,&quot;Bronte&quot;,&quot;Tamarama&quot;]
├─┤
│0│
└─┘</code></pre>
</details>
<p>I tried this for another 20 levels, and all 4 keys keep colliding.</p>
<p>If we take a step back, an easily-identifiable problem is that there are only
4 possible slots for each key to fit into, which increases the likelihood of
a collision. This is where the <code>gamma</code> parameter from earlier comes into play.
We can try again with a <code>gamma</code> of <code>1.5</code>:</p>
<details open="1">
<summary style="cursor: pointer">
Level 0
</summary>
<pre><code>┌─┐
│1│ &lt;- [&quot;Bronte&quot;]
├─┤
│1│ &lt;- [&quot;Gordons Bay&quot;]
├─┤
│0│
├─┤
│0│
├─┤
│0│ &lt;- [&quot;Coogee&quot;,&quot;Tamarama&quot;]
├─┤
│0│
├─┤
│1│ &lt;- [&quot;Clovelly&quot;]
├─┤
│0│
├─┤
│1│ &lt;- [&quot;Bondi&quot;]
└─┘</code></pre>
</details>
<p>Okay, this is already looking better.</p>
<details open="1">
<summary style="cursor: pointer">
Level 1
</summary>
<pre><code>┌─┐
│0│ &lt;- [&quot;Coogee&quot;,&quot;Tamarama&quot;]
├─┤
│0│
├─┤
│0│
└─┘</code></pre>
</details>
<p>Maybe I spoke too soon?</p>
<details open="1">
<summary style="cursor: pointer">
Level 2
</summary>
<pre><code>┌─┐
│1│ &lt;- [&quot;Tamarama&quot;]
├─┤
│1│ &lt;- [&quot;Coogee&quot;]
├─┤
│0│
└─┘</code></pre>
</details>
<p>Phew.</p>
<h3 id="lookup-1">Lookup</h3>
<p>Suppose we wanted to hash <code>Coogee</code>. This is what the final bitarrays look like:</p>
<details open="1">
<summary style="cursor: pointer">
Bitarrays
</summary>
<pre><code> 0 1 2 3 4 5 6 7 8
┌─┬─┬─┬─┬─┬─┬─┬─┬─┐
│1│1│0│0│0│0│1│0│1│ b0
└─┴─┴─┴─┴─┴─┴─┴─┴─┘
         └──────────── hashWithSalt 0 &quot;Coogee&quot; `mod` 9
┌─┬─┬─┐
│0│0│0│ b1
└─┴─┴─┘
 └──────────────────── hashWithSalt 1 &quot;Coogee&quot; `mod` 3
┌─┬─┬─┐
│1│1│0│ b2
└─┴─┴─┘
   └────────────────── hashWithSalt 2 &quot;Coogee&quot; `mod` 3</code></pre>
</details>
<p>We try each bitarray in sequence until we find a <span class="math inline">\(1\)</span> at our index, and we find the <span class="math inline">\(rank\)</span> of that index:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="op">&gt;</span><span> </span><span class="va">hashWithSalt</span><span> </span><span class="dv">0</span><span> </span><span class="st">&quot;Coogee&quot;</span><span> </span><span class="ot">`</span><span class="va">mod</span><span class="ot">`</span><span> </span><span class="dv">9</span><span>
</span><span class="dv">4</span><span>
</span><span class="op">&gt;</span><span> </span><span class="va">b0</span><span> </span><span class="op">!</span><span> </span><span class="dv">4</span><span> </span><span class="co">-- collision</span><span>
</span><span class="dv">0</span><span>
</span><span class="op">&gt;</span><span> </span><span class="va">hashWithSalt</span><span> </span><span class="dv">1</span><span> </span><span class="st">&quot;Coogee&quot;</span><span> </span><span class="ot">`</span><span class="va">mod</span><span class="ot">`</span><span> </span><span class="dv">3</span><span>
</span><span class="dv">0</span><span>
</span><span class="op">&gt;</span><span> </span><span class="va">b1</span><span> </span><span class="op">!</span><span> </span><span class="dv">0</span><span> </span><span class="co">-- collision</span><span>
</span><span class="dv">0</span><span>
</span><span class="op">&gt;</span><span> </span><span class="va">hashWithSalt</span><span> </span><span class="dv">2</span><span> </span><span class="st">&quot;Coogee&quot;</span><span> </span><span class="ot">`</span><span class="va">mod</span><span class="ot">`</span><span> </span><span class="dv">3</span><span>
</span><span class="dv">1</span><span>
</span><span class="op">&gt;</span><span> </span><span class="va">b2</span><span> </span><span class="op">!</span><span> </span><span class="dv">1</span><span> </span><span class="co">-- hit</span><span>
</span><span class="dv">1</span><span>
</span><span class="op">&gt;</span><span> </span><span class="va">popCount</span><span> </span><span class="va">b0</span><span> </span><span class="op">+</span><span> </span><span class="va">popCount</span><span> </span><span class="va">b1</span><span> </span><span class="op">+</span><span> </span><span class="va">rank</span><span> </span><span class="va">b2</span><span> </span><span class="dv">1</span><span>
</span><span class="dv">6</span></code></pre></div>
<p>Our hash is <span class="math inline">\(6\)</span>.</p>
<h4 id="false-positive">False positive</h4>
<p>Unfortunately, we also get seemingly-valid output for a key that wasn’t in our
input set, e.g.
<a href="https://www.sydney.com/destinations/sydney/sydney-north/manly/attractions/shelly-beach-manly"><code>Shelly</code></a>:</p>
<details open="1">
<summary style="cursor: pointer">
Bitarrays
</summary>
<pre><code> 0 1 2 3 4 5 6 7 8
┌─┬─┬─┬─┬─┬─┬─┬─┬─┐
│1│1│0│0│0│0│1│0│1│ b0
└─┴─┴─┴─┴─┴─┴─┴─┴─┘
   └─────────────────  hashWithSalt 0 &quot;Shelly&quot; `mod` 9
┌─┬─┬─┐
│0│0│0│ b1
└─┴─┴─┘
┌─┬─┬─┐
│1│1│0│ b2
└─┴─┴─┘</code></pre>
</details>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="op">&gt;</span><span> </span><span class="va">hashWithSalt</span><span> </span><span class="dv">0</span><span> </span><span class="st">&quot;Shelly&quot;</span><span> </span><span class="ot">`</span><span class="va">mod</span><span class="ot">`</span><span> </span><span class="dv">9</span><span>
</span><span class="dv">1</span><span>
</span><span class="op">&gt;</span><span> </span><span class="va">rank</span><span> </span><span class="va">b0</span><span> </span><span class="dv">1</span><span>
</span><span class="dv">2</span></code></pre></div>
<p>This is a limitation of minimal perfect hash functions in general, and
something to keep in mind while using them.</p>
<h3 id="minimal-perfect-hash-table">Minimal perfect hash <em>table</em></h3>
<p>All we have to do is create an array <span class="math inline">\(A\)</span> such that <span class="math inline">\(A[hash(k_n)-1] = v_n\)</span>!</p>
<details open="1">
<summary style="cursor: pointer">
Values
</summary>
<pre><code> ╭──────────── Bronte
 │ ╭────────── Gordons Bay
 │ │ ╭──────── Clovelly
 │ │ │ ╭────── Bondi
 │ │ │ │ ╭──── Tamarama
 │ │ │ │ │ ╭── Coogee
 0 1 2 3 4 5
┌─┬─┬─┬─┬─┬─┐
│ │ │ │ │ │ │
└─┴─┴─┴─┴─┴─┘</code></pre>
</details>
<p>The authors point out that trying to save a few bits per key by tweaking
<span class="math inline">\(gamma\)</span> doesn’t make much sense in this case, since the values account for the
vast majority of the space usage.</p>
<h3 id="code">Code</h3>
<p>The authors provide a library called
<a href="https://github.com/rizkg/BBHash"><code>BBHash</code></a>, and I have <a href="https://github.com/vaibhavsagar/notebooks/blob/master/mph/MPH.ipynb">a small implementation
here</a>.</p>
<h2 id="thats-all">That’s all!</h2>
<p>An interesting thing I noticed was that after I was able to make sense of this
implementation of minimal perfect hashing, the other approaches were easier to
grasp. I wouldn’t go so far as to say I magically <em>understood</em> them when
I didn’t before, but I definitely feel less lost now. Maybe you’ll have
a similar experience?</p>
<h2 id="more-links">More links</h2>
<ul>
<li><a href="http://stevehanov.ca/blog/?id=119">Throw away the keys: Easy, Minimal Perfect Hashing</a></li>
<li><a href="http://iswsa.acm.org/mphf/index.html">Minimal Perfect Hashing Resources</a></li>
</ul>
</summary>
</entry>
<entry>
    <title>Low-effort Dependency Pinning with Nix Flakes</title>
    <link href="https://vaibhavsagar.com/blog/2024/02/09/low-effort-dependency-pinning-flakes/" />
    <id>https://vaibhavsagar.com/blog/2024/02/09/low-effort-dependency-pinning-flakes/index.html</id>
    <published>2024-02-09</published>
    <updated>2024-02-09T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on  9 February 2024
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>, <a title="All pages tagged &#39;nix&#39;." href="/blog/tags/nix/index.html" rel="tag">nix</a>
    
</div>

<p>Back in 2018 I wrote <a href="/blog/2018/05/27/quick-easy-nixpkgs-pinning/">a blog post about pinning
<code>nixpkgs</code></a> which describes an
approach I’ve used happily and successfully since then to manage dependencies
(and not just <code>nixpkgs</code>) for small projects. In short, it involves</p>
<ol type="1">
<li><code>versions.json</code>, a JSON file storing dependency information</li>
<li><code>updater</code>, an updater script</li>
<li><code>pkgs.nix</code>, a Nix expression that makes each dependency available</li>
</ol>
<p>Here’s what each of those files might look like:</p>
<details>
<summary style="cursor: pointer">
<code>versions.json</code>
</summary>
<div class="sourceCode" id="cb1"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;ihaskell&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;owner&quot;</span><span class="fu">:</span> <span class="st">&quot;gibiansky&quot;</span><span class="fu">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;repo&quot;</span><span class="fu">:</span> <span class="st">&quot;IHaskell&quot;</span><span class="fu">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;branch&quot;</span><span class="fu">:</span> <span class="st">&quot;master&quot;</span><span class="fu">,</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;rev&quot;</span><span class="fu">:</span> <span class="st">&quot;575b2be1c25e8e7c5ed5048c8d7ead51bb9c67f0&quot;</span><span class="fu">,</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;sha256&quot;</span><span class="fu">:</span> <span class="st">&quot;148sdawqln2ys0s1rapwj2bwjzfq027dz5h49pa034nmyizyqs4a&quot;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>  <span class="fu">},</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;nixpkgs&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;owner&quot;</span><span class="fu">:</span> <span class="st">&quot;NixOS&quot;</span><span class="fu">,</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;repo&quot;</span><span class="fu">:</span> <span class="st">&quot;nixpkgs&quot;</span><span class="fu">,</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;branch&quot;</span><span class="fu">:</span> <span class="st">&quot;nixos-23.11&quot;</span><span class="fu">,</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;rev&quot;</span><span class="fu">:</span> <span class="st">&quot;9dd7699928e26c3c00d5d46811f1358524081062&quot;</span><span class="fu">,</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;sha256&quot;</span><span class="fu">:</span> <span class="st">&quot;0hmsw3qd3i13dp8jhr1d96xlpkmd78m8g6shw086f6sqhn2rrvv6&quot;</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>  <span class="fu">}</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
</details>
<details>
<summary style="cursor: pointer">
<code>updater</code>
</summary>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">#! /usr/bin/env nix-shell</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="co">#! nix-shell -i bash</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="co">#! nix-shell -p curl jq nix</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="bu">set</span> <span class="at">-eufo</span> pipefail</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="va">FILE</span><span class="op">=</span><span class="va">$1</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="va">PROJECT</span><span class="op">=</span><span class="va">$2</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="va">OWNER</span><span class="op">=</span><span class="va">$(</span><span class="ex">jq</span> <span class="at">-r</span> <span class="st">&#39;.[$project].owner&#39;</span> <span class="at">--arg</span> project <span class="st">&quot;</span><span class="va">$PROJECT</span><span class="st">&quot;</span> <span class="op">&lt;</span> <span class="st">&quot;</span><span class="va">$FILE</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="va">REPO</span><span class="op">=</span><span class="va">$(</span><span class="ex">jq</span> <span class="at">-r</span> <span class="st">&#39;.[$project].repo&#39;</span> <span class="at">--arg</span> project <span class="st">&quot;</span><span class="va">$PROJECT</span><span class="st">&quot;</span> <span class="op">&lt;</span> <span class="st">&quot;</span><span class="va">$FILE</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="va">DEFAULT_BRANCH</span><span class="op">=</span><span class="va">$(</span><span class="ex">jq</span> <span class="at">-r</span> <span class="st">&#39;.[$project].branch // &quot;master&quot;&#39;</span> <span class="at">--arg</span> project <span class="st">&quot;</span><span class="va">$PROJECT</span><span class="st">&quot;</span> <span class="op">&lt;</span> <span class="st">&quot;</span><span class="va">$FILE</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="va">BRANCH</span><span class="op">=</span><span class="va">${3</span><span class="op">:-</span><span class="va">$DEFAULT_BRANCH}</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="va">REV</span><span class="op">=</span><span class="va">$(</span><span class="ex">curl</span> <span class="st">&quot;https://api.github.com/repos/</span><span class="va">$OWNER</span><span class="st">/</span><span class="va">$REPO</span><span class="st">/branches/</span><span class="va">$BRANCH</span><span class="st">&quot;</span> <span class="kw">|</span> <span class="ex">jq</span> <span class="at">-r</span> <span class="st">&#39;.commit.sha&#39;</span><span class="va">)</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="va">SHA256</span><span class="op">=</span><span class="va">$(</span><span class="ex">nix-prefetch-url</span> <span class="at">--unpack</span> <span class="st">&quot;https://github.com/</span><span class="va">$OWNER</span><span class="st">/</span><span class="va">$REPO</span><span class="st">/archive/</span><span class="va">$REV</span><span class="st">.tar.gz&quot;</span><span class="va">)</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="va">TJQ</span><span class="op">=</span><span class="va">$(</span><span class="ex">jq</span> <span class="st">&#39;.[$project] = {owner: $owner, repo: $repo, branch: $branch, rev: $rev, sha256: $sha256}&#39;</span> <span class="dt">\</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>  <span class="at">--arg</span> project <span class="st">&quot;</span><span class="va">$PROJECT</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>  <span class="at">--arg</span> owner <span class="st">&quot;</span><span class="va">$OWNER</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a>  <span class="at">--arg</span> repo <span class="st">&quot;</span><span class="va">$REPO</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a>  <span class="at">--arg</span> branch <span class="st">&quot;</span><span class="va">$BRANCH</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>  <span class="at">--arg</span> rev <span class="st">&quot;</span><span class="va">$REV</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a>  <span class="at">--arg</span> sha256 <span class="st">&quot;</span><span class="va">$SHA256</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a>  <span class="op">&lt;</span> <span class="st">&quot;</span><span class="va">$FILE</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a><span class="kw">[[</span> <span class="va">$?</span> <span class="ot">==</span> 0 <span class="kw">]]</span> <span class="kw">&amp;&amp;</span> <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">${TJQ}</span><span class="st">&quot;</span> <span class="op">&gt;|</span> <span class="st">&quot;</span><span class="va">$FILE</span><span class="st">&quot;</span></span></code></pre></div>
</details>
<details>
<summary style="cursor: pointer">
<code>pkgs.nix</code>
</summary>
<div class="sourceCode" id="cb3"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="va">fetcher</span> <span class="op">=</span> <span class="op">{</span> <span class="va">owner</span><span class="op">,</span> <span class="va">repo</span><span class="op">,</span> <span class="va">rev</span><span class="op">,</span> <span class="va">sha256</span><span class="op">,</span> <span class="op">...</span> <span class="op">}</span>: <span class="bu">builtins</span><span class="op">.</span>fetchTarball <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">inherit</span> <span class="va">sha256</span><span class="op">;</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="va">url</span> <span class="op">=</span> <span class="st">&quot;https://github.com/</span><span class="sc">${</span>owner<span class="sc">}</span><span class="st">/</span><span class="sc">${</span>repo<span class="sc">}</span><span class="st">/tarball/</span><span class="sc">${</span>rev<span class="sc">}</span><span class="st">&quot;</span><span class="op">;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  <span class="va">versions</span> <span class="op">=</span> <span class="bu">builtins</span><span class="op">.</span>mapAttrs</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">(</span><span class="va">_</span><span class="op">:</span> fetcher<span class="op">)</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>fromJSON <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>readFile <span class="ss">./versions.json</span><span class="op">));</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> versions</span></code></pre></div>
</details>
<p>This approach still works, but in the meantime <a href="https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake">Nix
flakes</a>
have become the primary way to manage dependencies in Nix projects. Although
they’re still listed as an experimental feature, the same is also true of the
<code>nix</code> command, and I don’t think either is going away in the foreseeable
future.</p>
<h2 id="the-fundamental-insight">The fundamental insight</h2>
<p>It turns out that you can replace <code>pkgs.nix</code>:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="va">fetcher</span> <span class="op">=</span> <span class="op">{</span> <span class="va">owner</span><span class="op">,</span> <span class="va">repo</span><span class="op">,</span> <span class="va">rev</span><span class="op">,</span> <span class="va">sha256</span><span class="op">,</span> <span class="op">...</span> <span class="op">}</span>: <span class="bu">builtins</span><span class="op">.</span>fetchTarball <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">inherit</span> <span class="va">sha256</span><span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="va">url</span> <span class="op">=</span> <span class="st">&quot;https://github.com/</span><span class="sc">${</span>owner<span class="sc">}</span><span class="st">/</span><span class="sc">${</span>repo<span class="sc">}</span><span class="st">/tarball/</span><span class="sc">${</span>rev<span class="sc">}</span><span class="st">&quot;</span><span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>  <span class="va">versions</span> <span class="op">=</span> <span class="bu">builtins</span><span class="op">.</span>mapAttrs</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">(</span><span class="va">_</span><span class="op">:</span> fetcher<span class="op">)</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>fromJSON <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>readFile <span class="ss">./versions.json</span><span class="op">));</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> versions</span></code></pre></div>
<p>using the relatively new <code>fetchTree</code> builtin:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>  <span class="va">lock</span> <span class="op">=</span> <span class="bu">builtins</span><span class="op">.</span>fromJSON <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>readFile <span class="ss">./flake.lock</span><span class="op">);</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>  <span class="va">versions</span> <span class="op">=</span> <span class="bu">builtins</span><span class="op">.</span>mapAttrs</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">(</span><span class="va">_</span><span class="op">:</span> <span class="va">node</span><span class="op">:</span> <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>fetchTree node<span class="op">.</span>locked<span class="op">).</span>outPath<span class="op">)</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    lock<span class="op">.</span>nodes<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> versions</span></code></pre></div>
<p>following which you can replace <code>updater</code> with <a href="https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake-update"><code>nix flake update</code></a>
and <code>versions.json</code> with <code>flake.lock</code>.</p>
<h2 id="flakes-griping">Flakes griping</h2>
<p>I’ve done my best to avoid flakes for as long as possible, since there are
a couple of UI/UX issues that bother me:</p>
<h3 id="a-reliance-on-new-style-nix-commands">A reliance on new-style <code>nix</code> commands</h3>
<p>I’m pretty comfortable with <code>nix-build</code> and <code>nix-shell</code>, and it’s an adjustment
to use the newer <code>nix build</code> and <code>nix develop</code> commands since they don’t work
exactly the same (e.g. not printing build logs by default, having to use <code>.#</code>
for packages).</p>
<h3 id="coupling-dependency-and-systems-concerns">Coupling dependency and systems concerns</h3>
<p>The flakes position is that <code>system</code> is an impurity (which is reasonable
enough) and so each output is parametrised by the system and there’s no
built-in way to ignore or work around this. In practice I’ve seen most people
use <a href="https://github.com/numtide/flake-utils"><code>flake-utils</code></a> and its provided
<code>eachSystem</code> or <code>eachDefaultSystem</code> functions. For my purposes I haven’t run
into any issues with <code>eachDefaultSystem</code> and if you are shaking your head at
the screen thinking of all the ways this can go wrong then you probably don’t
need to read this blog post. Unfortunately <code>eachDefaultSystem</code> doesn’t save you
from having to supply <code>system</code> to <code>nixpkgs</code> everywhere you import it, which
makes adapting existing non-flakes projects with multiple imports of <code>nixpkgs</code>
tedious to migrate.</p>
<h3 id="surprising-interactions-with-git">Surprising interactions with <code>git</code></h3>
<p>Strange and confusing things can happen when you try to use a file that’s
currently untracked by <code>git</code>. Often it will tell you it can’t find a particular
file, even though it’s <em>right there</em>, but at other times things will appear to
work but your language-specific build tool will complain. The obvious solution
is to always <code>git add</code> everything you care about, but that has the same energy
as “I would simply write code with no bugs at all times” and is equally
non-actionable advice. The only hint you get is the message</p>
<p><code>warning: Git tree '&lt;project root&gt;' is dirty</code></p>
<p>as your build commences which is more often than not innocuous. I foresee
myself running into this issue over and over again when using flakes.</p>
<h2 id="why-bother-with-flakes">Why bother with flakes?</h2>
<p>Although I’m still critical of certain aspects of flakes, they do provide one
feature I was missing: the ability to manage and update dependencies without
the use of
<a href="https://nixos.org/manual/nix/stable/language/import-from-derivation">IFD</a>.
I also get the impression that the vast majority of effort being put into Nix
now is in and around the flakes ecosystem, e.g.
<a href="https://flakehub.com/">FlakeHub</a> and <a href="https://github.com/DeterminateSystems/update-flake-lock">the <code>update-flake-lock</code> GitHub
Action</a>. Keeping all
this in mind, I think there is a way to ignore most of the stuff I don’t care
about for now while getting rid of my primitive shell script in favour of
robust and better-supported dependency management code that’s built into Nix.
That way I can gradually integrate flakes more deeply, and if I’m wrong about
it being the future I still have the option to go back to what I was using
before (or adopt whatever the new hotness is).</p>
<h2 id="a-minimal-flake">A minimal flake</h2>
<p>The first hurdle to overcome is replacing <code>default.nix</code> with <code>flake.nix</code>. I’ve
found that this is a good starting point for me:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="va">inputs</span>.<span class="va">nixpkgs</span>.<span class="va">url</span> <span class="op">=</span> <span class="st">&quot;github:NixOS/nixpkgs/release-23.11&quot;</span><span class="op">;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>  <span class="va">inputs</span>.<span class="va">flake-utils</span>.<span class="va">url</span> <span class="op">=</span> <span class="st">&quot;github:numtide/flake-utils&quot;</span><span class="op">;</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  <span class="va">inputs</span>.<span class="va">flake-compat</span>.<span class="va">url</span> <span class="op">=</span> <span class="st">&quot;github:edolstra/flake-compat&quot;</span><span class="op">;</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>  <span class="va">outputs</span> <span class="op">=</span> <span class="op">{</span><span class="va">nixpkgs</span><span class="op">,</span> <span class="va">flake-utils</span><span class="op">,</span> <span class="op">...}</span>:</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    flake<span class="op">-</span>utils<span class="op">.</span>lib<span class="op">.</span>eachDefaultSystem <span class="op">(</span><span class="va">system</span><span class="op">:</span> <span class="kw">let</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>      <span class="va">pkgs</span> <span class="op">=</span> <span class="bu">import</span> nixpkgs <span class="op">{</span> <span class="kw">inherit</span> <span class="va">system</span><span class="op">;</span> <span class="op">};</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>      <span class="co"># ...</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">in</span> <span class="op">{</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>      <span class="va">defaultPackage</span> <span class="op">=</span> <span class="cn">null</span><span class="op">;</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>      <span class="va">devShell</span> <span class="op">=</span> <span class="cn">null</span><span class="op">;</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">});</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>combined with this snippet in <code>default.nix</code> taken from the <a href="https://github.com/edolstra/flake-compat/blob/0f9255e01c2351cc7d116c072cb317785dd33b33/README.md#usage"><code>flake-compat</code>
README</a>:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="op">(</span><span class="bu">import</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>  <span class="op">(</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="va">lock</span> <span class="op">=</span> <span class="bu">builtins</span><span class="op">.</span>fromJSON <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>readFile <span class="ss">./flake.lock</span><span class="op">);</span> <span class="kw">in</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    <span class="bu">fetchTarball</span> <span class="op">{</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>      <span class="va">url</span> <span class="op">=</span> lock<span class="op">.</span>nodes<span class="op">.</span>flake<span class="op">-</span>compat<span class="op">.</span>locked<span class="op">.</span>url <span class="kw">or</span> <span class="st">&quot;https://github.com/edolstra/flake-compat/archive/</span><span class="sc">${</span>lock<span class="op">.</span>nodes<span class="op">.</span>flake<span class="op">-</span>compat<span class="op">.</span>locked<span class="op">.</span>rev<span class="sc">}</span><span class="st">.tar.gz&quot;</span><span class="op">;</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>      <span class="va">sha256</span> <span class="op">=</span> lock<span class="op">.</span>nodes<span class="op">.</span>flake<span class="op">-</span>compat<span class="op">.</span>locked<span class="op">.</span>narHash<span class="op">;</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">)</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">{</span> <span class="va">src</span> <span class="op">=</span> <span class="ss">./.</span><span class="op">;</span> <span class="op">}</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a><span class="op">).</span>defaultNix</span></code></pre></div>
<p>A couple of things are worth pointing out:</p>
<ul>
<li>I include <code>flake-compat</code> in my inputs but I don’t actually use it in
<code>flake.nix</code>, it is declared solely so that it can be tracked in <code>flake.lock</code>.</li>
<li>I could include more dependencies here, as long as <code>nix flake update</code> knows
how to fetch them, which is already a huge improvement over my
GitHub-specific <code>updater</code> script.</li>
<li>If your input is a flake but you’re not using it in <code>flake.nix</code>, you probably
want to set <code>inpugs.&lt;input&gt;.flake = false</code> so that it doesn’t pull in that
flake’s dependencies too.</li>
<li>The <code>default.nix</code> snippet doesn’t have the old Nix behaviour of doing the
right thing when used with <code>nix-shell</code>, but I could probably recover this by
including (or reimplementing)
<a href="https://github.com/NixOS/nixpkgs/blob/9b5d456802b2322b36b69dca65b04095877495ad/lib/trivial.nix#L235-L240"><code>lib.inNixShell</code></a>
and using it.</li>
</ul>
<h2 id="migrating-all-the-things">Migrating all the things</h2>
<p>I recently went on a tear, moving a bunch of my repositories over to this workflow:</p>
<ul>
<li><a href="https://github.com/vaibhavsagar/resume/commit/a698b2df6e37c67bc05b57547d345883e76eb491"><code>resume</code></a></li>
<li><a href="https://github.com/vaibhavsagar/notebooks/commit/daa4f593f2bcacdb318f57525887c853db838304"><code>notebooks</code></a></li>
<li><a href="https://github.com/vaibhavsagar/website/commit/760a4b4c8f44347fbe3bb39203383c4a1b42178d"><code>website</code></a></li>
</ul>
<p>It was reasonably straightforward, except in the case of <code>notebooks</code>, where
I have a bunch of expressions that each have their own overlays etc. that
I wasn’t ready to unify just yet. This meant a lot of <code>{ system ? builtins.currentSystem }</code> which I could have done without. It’s an
anti-pattern to import <code>nixpkgs</code> in multiple places anyway, so this is probably
a sign that there is a better way to organise my expressions.</p>
<h2 id="further-reading">Further reading</h2>
<p>I was partly inspired to try this after reading Jade Lovelace’s <a href="https://jade.fyi/blog/flakes-arent-real/">excellent blog
post</a> about Nix flakes. Thank you
Jade!</p>
</summary>
</entry>
<entry>
    <title>Binary Trees To Hash Array Mapped Tries, Step by Step</title>
    <link href="https://vaibhavsagar.com/blog/2023/10/07/binary-trees-to-hamts/" />
    <id>https://vaibhavsagar.com/blog/2023/10/07/binary-trees-to-hamts/index.html</id>
    <published>2023-10-07</published>
    <updated>2023-10-07T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on  7 October 2023
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>, <a title="All pages tagged &#39;haskell&#39;." href="/blog/tags/haskell/index.html" rel="tag">haskell</a>
    
</div>

<p>Hash Array Mapped Tries (HAMTs) are a persistent data structure used to implement hashmaps. They’re heavily used <a href="https://github.com/clojure/clojure/blob/2a058814e5fa3e8fb630ae507c3fa7dc865138c6/src/jvm/clojure/lang/PersistentHashMap.java">in Clojure</a> and used to be the backbone of Haskell’s <a href="https://hackage.haskell.org/package/aeson"><code>aeson</code></a> library until <a href="https://hackage.haskell.org/package/aeson-2.0.1.0/changelog">relatively recently</a>. I’ve <a href="/blog/2018/07/29/hamts-from-scratch/">written about HAMTs before</a> but wanted to try a different approach: starting with a binary tree (or something close to it) and then making a series of straightforward modifications until we end up with the implementation detailed there.</p>
<p>Let’s start with some language extensions and imports:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="pp">{-# LANGUAGE GeneralizedNewtypeDeriving #-}</span><span>
</span><span class="pp">{-# LANGUAGE TypeSynonymInstances #-}</span><span>
</span><span class="pp">{-# LANGUAGE FlexibleInstances #-}</span><span>
</span><span class="pp">{-# LANGUAGE RankNTypes #-}</span><span>


</span><span class="kw">import</span><span> </span><span class="dt">Data.Bits</span><span>             </span><span class="ot">(</span><span class="dt">Bits</span><span> </span><span class="ot">(</span><span class="va">bit</span><span class="ot">,</span><span> </span><span class="va">complement</span><span class="ot">,</span><span> </span><span class="va">popCount</span><span class="ot">,</span><span> </span><span class="va">shiftR</span><span class="ot">,</span><span> </span><span class="ot">(</span><span class="op">.&amp;.</span><span class="ot">)</span><span class="ot">,</span><span> </span><span class="ot">(</span><span class="op">.|.</span><span class="ot">)</span><span class="ot">,</span><span> </span><span class="va">testBit</span><span class="ot">)</span><span class="ot">,</span><span>
                              </span><span class="dt">FiniteBits</span><span> </span><span class="ot">(</span><span class="va">finiteBitSize</span><span class="ot">)</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Data.ByteArray.Hash</span><span>   </span><span class="ot">(</span><span class="dt">FnvHash32</span><span> </span><span class="ot">(</span><span class="ot">..</span><span class="ot">)</span><span class="ot">,</span><span> </span><span class="va">fnv1Hash</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Data.ByteString.Char8</span><span> </span><span class="ot">(</span><span class="va">pack</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Data.Char</span><span>             </span><span class="ot">(</span><span class="va">intToDigit</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Data.Semigroup</span><span>        </span><span class="ot">(</span><span class="ot">(</span><span class="op">&lt;&gt;</span><span class="ot">)</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Data.Vector</span><span>           </span><span class="ot">(</span><span class="dt">Vector</span><span class="ot">,</span><span> </span><span class="va">drop</span><span class="ot">,</span><span> </span><span class="va">singleton</span><span class="ot">,</span><span> </span><span class="va">take</span><span class="ot">,</span><span> </span><span class="va">replicate</span><span class="ot">,</span><span> </span><span class="ot">(</span><span class="op">!</span><span class="ot">)</span><span class="ot">,</span><span> </span><span class="ot">(</span><span class="op">//</span><span class="ot">)</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Data.Word</span><span>             </span><span class="ot">(</span><span class="dt">Word16</span><span class="ot">,</span><span> </span><span class="dt">Word32</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Numeric</span><span>               </span><span class="ot">(</span><span class="va">showIntAtBase</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Prelude</span><span>               </span><span class="kw">hiding</span><span> </span><span class="ot">(</span><span class="va">drop</span><span class="ot">,</span><span> </span><span class="va">lookup</span><span class="ot">,</span><span> </span><span class="va">take</span><span class="ot">,</span><span> </span><span class="va">replicate</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="kw">qualified</span><span>             </span><span class="dt">Prelude</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">System.TimeIt</span><span>         </span><span class="ot">(</span><span class="va">timeIt</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Text.Show.Pretty</span><span>      </span><span class="ot">(</span><span class="va">pPrint</span><span class="ot">)</span></code></pre></div>
<p>I think it’s useful to be able to visualise these structures, for which we need some more imports:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import</span><span> </span><span class="dt">IHaskell.Display.Graphviz</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Control.Monad.Trans.State.Strict</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Control.Monad.Trans.Writer.CPS</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Control.Monad.Trans.Class</span><span>
</span><span class="kw">import</span><span> </span><span class="kw">qualified</span><span> </span><span class="dt">Data.Vector</span><span> </span><span class="kw">as</span><span> </span><span class="dt">Vector</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Data.List</span><span> </span><span class="ot">(</span><span class="va">intercalate</span><span class="ot">,</span><span> </span><span class="va">intersperse</span><span class="ot">,</span><span> </span><span class="va">foldl&#39;</span><span class="ot">)</span></code></pre></div>
<p>I’m going to define some instances for pretty-printing hashes:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">newtype</span><span> </span><span class="dt">Binary</span><span> </span><span class="va">a</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Binary</span><span> </span><span class="va">a</span><span>
    </span><span class="kw">deriving</span><span> </span><span class="ot">(</span><span class="dt">Enum</span><span class="ot">,</span><span> </span><span class="dt">Ord</span><span class="ot">,</span><span> </span><span class="dt">Real</span><span class="ot">,</span><span> </span><span class="dt">Integral</span><span class="ot">,</span><span> </span><span class="dt">Eq</span><span class="ot">,</span><span> </span><span class="dt">Num</span><span class="ot">,</span><span> </span><span class="dt">Bits</span><span class="ot">,</span><span> </span><span class="dt">FiniteBits</span><span class="ot">)</span><span>

</span><span class="kw">instance</span><span> </span><span class="ot">(</span><span class="dt">FiniteBits</span><span> </span><span class="va">a</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span> </span><span class="va">a</span><span class="ot">,</span><span> </span><span class="dt">Integral</span><span> </span><span class="va">a</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="dt">Show</span><span> </span><span class="ot">(</span><span class="dt">Binary</span><span> </span><span class="va">a</span><span class="ot">)</span><span> </span><span class="kw">where</span><span>
    </span><span class="va">show</span><span> </span><span class="ot">(</span><span class="dt">Binary</span><span> </span><span class="va">n</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
        </span><span class="va">str</span><span> </span><span class="ot">=</span><span> </span><span class="va">showIntAtBase</span><span> </span><span class="dv">2</span><span> </span><span class="va">intToDigit</span><span> </span><span class="va">n</span><span> </span><span class="st">&quot;&quot;</span><span>
        </span><span class="va">size</span><span> </span><span class="ot">=</span><span> </span><span class="va">finiteBitSize</span><span> </span><span class="va">n</span><span>
        </span><span class="kw">in</span><span> </span><span class="va">Prelude.replicate</span><span> </span><span class="ot">(</span><span class="va">size</span><span> </span><span class="op">-</span><span> </span><span class="va">length</span><span> </span><span class="va">str</span><span class="ot">)</span><span> </span><span class="ch">&#39;0&#39;</span><span> </span><span class="op">&lt;&gt;</span><span> </span><span class="va">str</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">type</span><span> </span><span class="dt">Hash</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Binary</span><span> </span><span class="dt">Word32</span><span>

</span><span class="kw">class</span><span> </span><span class="dt">Hashable</span><span> </span><span class="va">a</span><span> </span><span class="kw">where</span><span>
    </span><span class="va">hash</span><span> </span><span class="ot">::</span><span> </span><span class="va">a</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash</span></code></pre></div>
<p>One can think of hashing as mapping values of some type to fixed-size values of another type, and in this case I’ve decided to hash <code>Int</code>s to themselves for demonstration purposes. I would strongly recommend against doing this in production, but when explaining how these trees are constructed it’s handy to be able to immediately tell what the hash of some <code>Int</code> will be.</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">instance</span><span> </span><span class="dt">Hashable</span><span> </span><span class="dt">String</span><span> </span><span class="kw">where</span><span>
    </span><span class="va">hash</span><span> </span><span class="va">s</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
        </span><span class="dt">FnvHash32</span><span> </span><span class="va">h</span><span> </span><span class="ot">=</span><span> </span><span class="va">fnv1Hash</span><span> </span><span class="ot">(</span><span class="va">pack</span><span> </span><span class="va">s</span><span class="ot">)</span><span>
        </span><span class="kw">in</span><span> </span><span class="dt">Binary</span><span> </span><span class="va">h</span><span>

</span><span class="kw">instance</span><span> </span><span class="dt">Hashable</span><span> </span><span class="dt">Int</span><span> </span><span class="kw">where</span><span>
    </span><span class="va">hash</span><span> </span><span class="va">int</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Binary</span><span> </span><span class="ot">(</span><span class="va">fromIntegral</span><span> </span><span class="va">int</span><span class="ot">)</span></code></pre></div>
I’m also defining some helpers so that we can generate DOT representations and use <code>ihaskell-graphviz</code> to display each of the structures defined here:
<details>
<summary>
Graphviz helper functions
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">getFreshId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">State</span><span> </span><span class="dt">Int</span><span> </span><span class="dt">Int</span><span>
</span><span class="va">getFreshId</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">currentId</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">get</span><span>
    </span><span class="va">put</span><span> </span><span class="ot">(</span><span class="va">currentId</span><span class="op">+</span><span class="dv">1</span><span class="ot">)</span><span>
    </span><span class="va">pure</span><span> </span><span class="va">currentId</span><span>

</span><span class="va">escape</span><span> </span><span class="ot">=</span><span> </span><span class="va">concatMap</span><span> </span><span class="va">escaper</span><span>
    </span><span class="kw">where</span><span>
        </span><span class="va">escaper</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Char</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">String</span><span>
        </span><span class="va">escaper</span><span> </span><span class="va">c</span><span> </span><span class="ot">=</span><span> </span><span class="kw">case</span><span> </span><span class="va">c</span><span> </span><span class="kw">of</span><span>
            </span><span class="ch">&#39;&quot;&#39;</span><span>  </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;\\\&quot;&quot;</span><span>
            </span><span class="ch">&#39;\\&#39;</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;\\\\&quot;</span><span>
            </span><span class="ot">_</span><span>    </span><span class="ot">-&gt;</span><span> </span><span class="ot">[</span><span class="va">c</span><span class="ot">]</span><span>

</span><span class="va">makeDotLines</span><span> </span><span class="ot">::</span><span> </span><span class="ot">[</span><span class="dt">String</span><span class="ot">]</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">String</span><span>
</span><span class="va">makeDotLines</span><span> </span><span class="ot">=</span><span> </span><span class="va">concatMap</span><span> </span><span class="ot">(</span><span class="op">++</span><span> </span><span class="st">&quot;;\n&quot;</span><span class="ot">)</span><span>

</span><span class="va">preamble</span><span> </span><span class="ot">=</span><span> </span><span class="va">unlines</span><span>
    </span><span class="ot">[</span><span> </span><span class="st">&quot;digraph {&quot;</span><span>
    </span><span class="ot">,</span><span> </span><span class="st">&quot;node [shape=record];&quot;</span><span>
    </span><span class="ot">,</span><span> </span><span class="st">&quot;splines=false;&quot;</span><span>
    </span><span class="ot">,</span><span> </span><span class="st">&quot;ranksep=2;&quot;</span><span>
    </span><span class="ot">,</span><span> </span><span class="st">&quot;nodesep=1;&quot;</span><span>
    </span><span class="ot">]</span><span>
</span><span class="va">postamble</span><span> </span><span class="ot">=</span><span> </span><span class="va">unlines</span><span> </span><span class="ot">[</span><span class="st">&quot;}&quot;</span><span class="ot">]</span><span>

</span><span class="va">makeDot</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">String</span><span>
</span><span class="va">makeDot</span><span> </span><span class="va">str</span><span> </span><span class="ot">=</span><span> </span><span class="va">preamble</span><span> </span><span class="op">++</span><span> </span><span class="va">str</span><span> </span><span class="op">++</span><span> </span><span class="va">postamble</span></code></pre></div>
</details>
<p>Let’s define a typeclass to abstract over the details of our data structures. For our purposes we only care that for some <code>Mapping</code>, we can have an empty value, a way to <code>insert</code> key-value pairs, and a way to <code>lookup</code> a particular key:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">class</span><span> </span><span class="dt">Mapping</span><span> </span><span class="va">mapping</span><span> </span><span class="kw">where</span><span>
    </span><span class="va">empty</span><span> </span><span class="ot">::</span><span> </span><span class="kw">forall</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="op">.</span><span> </span><span class="va">mapping</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span>
    </span><span class="va">lookup</span><span> </span><span class="ot">::</span><span> </span><span class="kw">forall</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="op">.</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">k</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">k</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">mapping</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Maybe</span><span> </span><span class="va">v</span><span>
    </span><span class="va">insert</span><span> </span><span class="ot">::</span><span> </span><span class="kw">forall</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="op">.</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">k</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">k</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">mapping</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">mapping</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span></code></pre></div>
<p>As a way of exercising these <code>Mappings</code>, I’ve chosen to implement a simple memoised <code>fib'</code> function that stores intermediate results:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">fib&#39;</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Mapping</span><span> </span><span class="va">m</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">m</span><span> </span><span class="dt">Int</span><span> </span><span class="dt">Integer</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Int</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="ot">(</span><span class="dt">Integer</span><span class="ot">,</span><span> </span><span class="va">m</span><span> </span><span class="dt">Int</span><span> </span><span class="dt">Integer</span><span class="ot">)</span><span>
</span><span class="va">fib&#39;</span><span> </span><span class="va">table</span><span> </span><span class="dv">0</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="dv">1</span><span class="ot">,</span><span> </span><span class="va">insert</span><span> </span><span class="dv">0</span><span> </span><span class="dv">1</span><span> </span><span class="va">table</span><span class="ot">)</span><span>
</span><span class="va">fib&#39;</span><span> </span><span class="va">table</span><span> </span><span class="dv">1</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="dv">1</span><span class="ot">,</span><span> </span><span class="va">insert</span><span> </span><span class="dv">1</span><span> </span><span class="dv">1</span><span> </span><span class="va">table</span><span class="ot">)</span><span>
</span><span class="va">fib&#39;</span><span> </span><span class="va">table</span><span> </span><span class="va">n</span><span> </span><span class="ot">=</span><span> </span><span class="kw">case</span><span> </span><span class="va">lookup</span><span> </span><span class="va">n</span><span> </span><span class="va">table</span><span> </span><span class="kw">of</span><span>
    </span><span class="dt">Just</span><span> </span><span class="va">i</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="ot">(</span><span class="va">i</span><span class="ot">,</span><span> </span><span class="va">table</span><span class="ot">)</span><span>
    </span><span class="dt">Nothing</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="kw">let</span><span>
        </span><span class="ot">(</span><span class="va">i1</span><span class="ot">,</span><span> </span><span class="va">table&#39;</span><span class="ot">)</span><span>  </span><span class="ot">=</span><span> </span><span class="va">fib&#39;</span><span> </span><span class="va">table</span><span>  </span><span class="ot">(</span><span class="va">n</span><span class="op">-</span><span class="dv">1</span><span class="ot">)</span><span>
        </span><span class="ot">(</span><span class="va">i2</span><span class="ot">,</span><span> </span><span class="va">table&#39;&#39;</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="va">fib&#39;</span><span> </span><span class="va">table&#39;</span><span> </span><span class="ot">(</span><span class="va">n</span><span class="op">-</span><span class="dv">2</span><span class="ot">)</span><span>
        </span><span class="kw">in</span><span> </span><span class="ot">(</span><span class="va">i1</span><span> </span><span class="op">+</span><span> </span><span class="va">i2</span><span class="ot">,</span><span> </span><span class="va">insert</span><span> </span><span class="va">n</span><span> </span><span class="ot">(</span><span class="va">i1</span><span> </span><span class="op">+</span><span> </span><span class="va">i2</span><span class="ot">)</span><span> </span><span class="va">table&#39;&#39;</span><span class="ot">)</span></code></pre></div>
<p>After that housekeeping, we can begin with our first data structure:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">=</span><span> </span><span class="dt">HashBinaryMappedTrieNone</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">HashBinaryMappedTrieLeaf</span><span> </span><span class="dt">Hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">HashBinaryMappedTrieNode</span><span>
        </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span class="ot">)</span><span>
        </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span class="ot">)</span><span>
    </span><span class="kw">deriving</span><span> </span><span class="ot">(</span><span class="dt">Eq</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span class="ot">)</span></code></pre></div>
<p>This is a binary tree with key-value pairs stored at the leaves. It is also a bitwise <a href="https://en.wikipedia.org/wiki/Trie">trie</a> because I plan to insert into it as follows:</p>
<ol type="1">
<li>First, hash the key.</li>
<li>If we find a <code>HashBinaryMappedTrieNone</code>, replace it with a <code>HashBinaryMappedTrieLeaf</code> and our hash, key, and value and stop. If we find a <code>HashBinaryMappedTrieLeaf</code> and it’s not the key-value pair we are inserting, replace it with a <code>HashBinaryMappedTrieNode</code> and insert both the old value and the new value into this node.</li>
<li>Branch on the rightmost bit of the hash. If it is a <code>0</code>, go left, otherwise go right.</li>
<li>Remove the rightmost bit from the hash for the purposes of considering whether we go left or right.</li>
<li>Repeat steps 2-5.</li>
</ol>
<p>I’ve chosen to call it a Hash Binary Mapped Trie, since it is a binary (bitwise) trie storing a mapping based on hashes.</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">insertHashBinaryMappedTrie</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHashBinaryMappedTrieHelper</span><span> </span><span class="dv">0</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="va">key</span><span>

</span><span class="va">insertHashBinaryMappedTrieHelper</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="dt">HashBinaryMappedTrieNone</span><span> </span><span class="ot">=</span><span>
    </span><span class="dt">HashBinaryMappedTrieLeaf</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrieLeaf</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span class="ot">)</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">hash</span><span> </span><span class="op">==</span><span> </span><span class="va">leafHash</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashBinaryMappedTrieLeaf</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">otherwise</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
        </span><span class="va">emptyNode</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashBinaryMappedTrieNode</span><span> </span><span class="dt">HashBinaryMappedTrieNone</span><span> </span><span class="dt">HashBinaryMappedTrieNone</span><span>
        </span><span class="va">leafInsertedNode</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span> </span><span class="va">emptyNode</span><span>
        </span><span class="kw">in</span><span> </span><span class="va">insertHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="va">leafInsertedNode</span><span>
</span><span class="va">insertHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrieNode</span><span> </span><span class="va">left</span><span> </span><span class="va">right</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">goRight</span><span> </span><span class="ot">=</span><span> </span><span class="va">testBit</span><span> </span><span class="va">hash</span><span> </span><span class="va">depth</span><span>
    </span><span class="va">depth&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">depth</span><span> </span><span class="op">+</span><span> </span><span class="dv">1</span><span>
    </span><span class="kw">in</span><span> </span><span class="kw">if</span><span> </span><span class="va">goRight</span><span>
        </span><span class="kw">then</span><span> </span><span class="dt">HashBinaryMappedTrieNode</span><span> </span><span class="va">left</span><span> </span><span class="ot">(</span><span class="va">insertHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="va">right</span><span class="ot">)</span><span>
        </span><span class="kw">else</span><span> </span><span class="dt">HashBinaryMappedTrieNode</span><span> </span><span class="ot">(</span><span class="va">insertHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="va">left</span><span class="ot">)</span><span> </span><span class="va">right</span></code></pre></div>
<p>To look up a particular key, the process is similar:</p>
<ol type="1">
<li>Hash the key.</li>
<li>If we find a <code>HashBinaryMappedTrieNone</code>, return <code>Nothing</code>. If we find a <code>HashBinaryMappedTrieLeaf</code>, check that the hashes match (this ignores the possibility of hash collisions) and if so return the pair otherwise return <code>Nothing</code>.</li>
<li>Branch on the rightmost bit of the hash, going left if it is <code>0</code> and right otherwise.</li>
<li>Remove the rightmost bit from the hash for the purposes of considering whether to go left or right.</li>
<li>Repeat steps 2-5.</li>
</ol>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">lookupHashBinaryMappedTrie</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Maybe</span><span> </span><span class="va">value</span><span>
</span><span class="va">lookupHashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="ot">=</span><span> </span><span class="va">lookupHashBinaryMappedTrieHelper</span><span> </span><span class="dv">0</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="va">key</span><span>

</span><span class="va">lookupHashBinaryMappedTrieHelper</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Maybe</span><span> </span><span class="va">value</span><span>
</span><span class="va">lookupHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="dt">HashBinaryMappedTrieNone</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Nothing</span><span>
</span><span class="va">lookupHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrieLeaf</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span class="ot">)</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">hash</span><span> </span><span class="op">==</span><span> </span><span class="va">leafHash</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Just</span><span> </span><span class="va">leafValue</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">otherwise</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Nothing</span><span>
</span><span class="va">lookupHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrieNode</span><span> </span><span class="va">left</span><span> </span><span class="va">right</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">goRight</span><span> </span><span class="ot">=</span><span> </span><span class="va">testBit</span><span> </span><span class="va">hash</span><span> </span><span class="va">depth</span><span>
    </span><span class="va">depth&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">depth</span><span> </span><span class="op">+</span><span> </span><span class="dv">1</span><span>
    </span><span class="kw">in</span><span> </span><span class="kw">if</span><span> </span><span class="va">goRight</span><span>
        </span><span class="kw">then</span><span> </span><span class="va">lookupHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">right</span><span>
        </span><span class="kw">else</span><span> </span><span class="va">lookupHashBinaryMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">left</span></code></pre></div>
<p>An empty <code>HashBinaryMappedTrie</code> is <code>HashBinaryMappedTrieNone</code>:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">emptyHashBinaryMappedTrie</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashBinaryMappedTrieNone</span></code></pre></div>
<p>We can easily implement an instance of <code>Mapping</code> for <code>HashBinaryMappedTrie</code>:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">instance</span><span> </span><span class="dt">Mapping</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="kw">where</span><span>
    </span><span class="va">empty</span><span> </span><span class="ot">=</span><span> </span><span class="va">emptyHashBinaryMappedTrie</span><span>
    </span><span class="va">insert</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHashBinaryMappedTrie</span><span>
    </span><span class="va">lookup</span><span> </span><span class="ot">=</span><span> </span><span class="va">lookupHashBinaryMappedTrie</span></code></pre></div>
<p>Now we can build a tree to look at using <code>fib'</code>, but before we can visualise it we need to convert it into DOT files for <code>ihaskell-graphviz</code>:</p>
<details>
<summary>
Graphviz helper functions for HashBinaryMappedTrie
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span><span> </span><span class="dt">HashBinaryMappedTrieGraphvizNode</span><span>
    </span><span class="ot">=</span><span> </span><span class="dt">HashBinaryMappedTrieGraphvizNode</span><span>
        </span><span class="ot">{</span><span> </span><span class="va">hashBinaryMappedTrieGraphvizNodeId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashBinaryMappedTrieGraphvizLeftChildId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashBinaryMappedTrieGraphvizRightChildId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">}</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">HashBinaryMappedTrieGraphvizLeafNode</span><span>
        </span><span class="ot">{</span><span> </span><span class="va">hashBinaryMappedTrieGraphvizLeafNodeId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashBinaryMappedTriGraphvizeLeafHash</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashBinaryMappedTrieGraphvizLeafKey</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashBinaryMappedTrieGraphvizLeafNodeValue</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">}</span><span>
    </span><span class="kw">deriving</span><span> </span><span class="ot">(</span><span class="dt">Eq</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span class="ot">)</span><span>

</span><span class="va">numberHBMT</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Show</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">WriterT</span><span> </span><span class="ot">[</span><span class="dt">HashBinaryMappedTrieGraphvizNode</span><span class="ot">]</span><span> </span><span class="ot">(</span><span class="dt">State</span><span> </span><span class="dt">Int</span><span class="ot">)</span><span> </span><span class="dt">Int</span><span>
</span><span class="va">numberHBMT</span><span> </span><span class="dt">HashBinaryMappedTrieNone</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">tell</span><span> </span><span class="va">mempty</span><span>
    </span><span class="va">pure</span><span> </span><span class="dv">0</span><span>
</span><span class="va">numberHBMT</span><span> </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrieLeaf</span><span> </span><span class="va">h</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">i</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">lift</span><span> </span><span class="va">getFreshId</span><span>
    </span><span class="va">tell</span><span> </span><span class="ot">[</span><span class="dt">HashBinaryMappedTrieGraphvizLeafNode</span><span> </span><span class="va">i</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">h</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">k</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">v</span><span class="ot">)</span><span class="ot">]</span><span>
    </span><span class="va">pure</span><span> </span><span class="va">i</span><span>
</span><span class="va">numberHBMT</span><span> </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrieNode</span><span> </span><span class="va">l</span><span> </span><span class="va">r</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">i</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">lift</span><span> </span><span class="va">getFreshId</span><span>
    </span><span class="va">leftChildId</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">numberHBMT</span><span> </span><span class="va">l</span><span>
    </span><span class="va">rightChildId</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">numberHBMT</span><span> </span><span class="va">r</span><span>
    </span><span class="va">tell</span><span> </span><span class="ot">[</span><span class="dt">HashBinaryMappedTrieGraphvizNode</span><span> </span><span class="va">i</span><span> </span><span class="va">leftChildId</span><span> </span><span class="va">rightChildId</span><span class="ot">]</span><span>
    </span><span class="va">pure</span><span> </span><span class="va">i</span><span>

</span><span class="va">nodeLinesHBMT</span><span> </span><span class="ot">::</span><span> </span><span class="dt">HashBinaryMappedTrieGraphvizNode</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="ot">[</span><span class="dt">String</span><span class="ot">]</span><span>
</span><span class="va">nodeLinesHBMT</span><span> </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrieGraphvizLeafNode</span><span> </span><span class="va">i</span><span> </span><span class="va">h</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="kw">label</span><span> </span><span class="ot">=</span><span> </span><span class="va">intercalate</span><span> </span><span class="st">&quot;|&quot;</span><span> </span><span class="ot">[</span><span class="va">h</span><span class="ot">,</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="va">v</span><span class="ot">]</span><span>
    </span><span class="va">line</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span class="ot">)</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;[label=\&quot;&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">escape</span><span> </span><span class="kw">label</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;\&quot;]&quot;</span><span>
    </span><span class="kw">in</span><span> </span><span class="ot">[</span><span class="va">line</span><span class="ot">]</span><span>
</span><span class="va">nodeLinesHBMT</span><span> </span><span class="ot">(</span><span class="dt">HashBinaryMappedTrieGraphvizNode</span><span> </span><span class="va">i</span><span> </span><span class="va">l</span><span> </span><span class="va">r</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">edges</span><span> </span><span class="ot">=</span><span> </span><span class="va">map</span><span> </span><span class="ot">(</span><span class="ot">\</span><span class="va">index</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; -&gt; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">index</span><span class="ot">)</span><span> </span><span class="ot">[</span><span class="va">l</span><span class="ot">,</span><span> </span><span class="va">r</span><span class="ot">]</span><span>
    </span><span class="kw">label</span><span> </span><span class="ot">=</span><span> </span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;[label=\&quot;\&quot;]&quot;</span><span>
    </span><span class="kw">in</span><span> </span><span class="kw">label</span><span class="ot">:</span><span class="va">edges</span><span>

</span><span class="va">dotFromHBMT</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Show</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="dt">HashBinaryMappedTrie</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">String</span><span>
</span><span class="va">dotFromHBMT</span><span> </span><span class="ot">=</span><span> </span><span class="va">makeDot</span><span> </span><span class="op">.</span><span> </span><span class="va">makeDotLines</span><span class="op">.</span><span> </span><span class="va">concatMap</span><span> </span><span class="va">nodeLinesHBMT</span><span> </span><span class="op">.</span><span> </span><span class="va">flip</span><span> </span><span class="va">evalState</span><span> </span><span class="dv">0</span><span> </span><span class="op">.</span><span> </span><span class="va">execWriterT</span><span> </span><span class="op">.</span><span> </span><span class="va">numberHBMT</span></code></pre></div>
</details>
Here’s a visualisation of the tree created by <code>fib' 8</code>:
<details>
<summary>
Hash Binary Mapped Trie
</summary>
<div style="overflow: scroll">
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">dot</span><span> </span><span class="op">$</span><span> </span><span class="va">dotFromHBMT</span><span> </span><span class="op">$</span><span> </span><span class="va">snd</span><span> </span><span class="op">$</span><span> </span><span class="va">fib&#39;</span><span> </span><span class="va">emptyHashBinaryMappedTrie</span><span> </span><span class="dv">8</span></code></pre></div>
<svg width="3454pt" height="769pt" viewBox="0.00 0.00 3453.50 769.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 765)">
<polygon fill="white" stroke="none" points="-4,4 -4,-765 3449.5,-765 3449.5,4 -4,4"/>
<!-- n4 -->
<g id="node1" class="node">
<title>
n4
</title>
<polygon fill="none" stroke="black" points="0,-0.5 0,-36.5 357,-36.5 357,-0.5 0,-0.5"/>
<text text-anchor="middle" x="152.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000000</text>
<polyline fill="none" stroke="black" points="305,-0.5 305,-36.5"/>
<text text-anchor="middle" x="318" y="-14.8" font-family="Times,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="331,-0.5 331,-36.5"/>
<text text-anchor="middle" x="344" y="-14.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- n5 -->
<g id="node2" class="node">
<title>
n5
</title>
<polygon fill="none" stroke="black" points="429.5,-0.5 429.5,-36.5 795.5,-36.5 795.5,-0.5 429.5,-0.5"/>
<text text-anchor="middle" x="582" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000001000</text>
<polyline fill="none" stroke="black" points="734.5,-0.5 734.5,-36.5"/>
<text text-anchor="middle" x="747.5" y="-14.8" font-family="Times,serif" font-size="14.00">8</text>
<polyline fill="none" stroke="black" points="760.5,-0.5 760.5,-36.5"/>
<text text-anchor="middle" x="778" y="-14.8" font-family="Times,serif" font-size="14.00">34</text>
</g>
<!-- n3 -->
<g id="node3" class="node">
<title>
n3
</title>
<polygon fill="none" stroke="black" points="368.5,-181.5 368.5,-217.5 422.5,-217.5 422.5,-181.5 368.5,-181.5"/>
<text text-anchor="middle" x="395.5" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n3&#45;&gt;n4 -->
<g id="edge1" class="edge">
<title>
n3-&gt;n4
</title>
<path fill="none" stroke="black" d="M375.09,-181.66C336.39,-149.74 252.27,-80.35 207.65,-43.55"/>
<polygon fill="black" stroke="black" points="210.13,-41.05 200.19,-37.39 205.67,-46.45 210.13,-41.05"/>
</g>
<!-- n3&#45;&gt;n5 -->
<g id="edge2" class="edge">
<title>
n3-&gt;n5
</title>
<path fill="none" stroke="black" d="M415.91,-181.66C454.61,-149.74 538.73,-80.35 583.35,-43.55"/>
<polygon fill="black" stroke="black" points="585.33,-46.45 590.81,-37.39 580.87,-41.05 585.33,-46.45"/>
</g>
<!-- n6 -->
<g id="node4" class="node">
<title>
n6
</title>
<polygon fill="none" stroke="black" points="495,-181.5 495,-217.5 852,-217.5 852,-181.5 495,-181.5"/>
<text text-anchor="middle" x="647.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000100</text>
<polyline fill="none" stroke="black" points="800,-181.5 800,-217.5"/>
<text text-anchor="middle" x="813" y="-195.8" font-family="Times,serif" font-size="14.00">4</text>
<polyline fill="none" stroke="black" points="826,-181.5 826,-217.5"/>
<text text-anchor="middle" x="839" y="-195.8" font-family="Times,serif" font-size="14.00">5</text>
</g>
<!-- n2 -->
<g id="node5" class="node">
<title>
n2
</title>
<polygon fill="none" stroke="black" points="646.5,-362.5 646.5,-398.5 700.5,-398.5 700.5,-362.5 646.5,-362.5"/>
<text text-anchor="middle" x="673.5" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n2&#45;&gt;n3 -->
<g id="edge3" class="edge">
<title>
n2-&gt;n3
</title>
<path fill="none" stroke="black" d="M647.35,-362.66C597.36,-330.48 488.2,-260.19 431.42,-223.63"/>
<polygon fill="black" stroke="black" points="433.36,-220.72 423.06,-218.24 429.57,-226.6 433.36,-220.72"/>
</g>
<!-- n2&#45;&gt;n6 -->
<g id="edge4" class="edge">
<title>
n2-&gt;n6
</title>
<path fill="none" stroke="black" d="M673.5,-362.66C673.5,-331.88 673.5,-266.23 673.5,-228.58"/>
<polygon fill="black" stroke="black" points="677,-228.94 673.5,-218.94 670,-228.94 677,-228.94"/>
</g>
<!-- n8 -->
<g id="node6" class="node">
<title>
n8
</title>
<polygon fill="none" stroke="black" points="924,-181.5 924,-217.5 1281,-217.5 1281,-181.5 924,-181.5"/>
<text text-anchor="middle" x="1076.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000010</text>
<polyline fill="none" stroke="black" points="1229,-181.5 1229,-217.5"/>
<text text-anchor="middle" x="1242" y="-195.8" font-family="Times,serif" font-size="14.00">2</text>
<polyline fill="none" stroke="black" points="1255,-181.5 1255,-217.5"/>
<text text-anchor="middle" x="1268" y="-195.8" font-family="Times,serif" font-size="14.00">2</text>
</g>
<!-- n9 -->
<g id="node7" class="node">
<title>
n9
</title>
<polygon fill="none" stroke="black" points="1353.5,-181.5 1353.5,-217.5 1719.5,-217.5 1719.5,-181.5 1353.5,-181.5"/>
<text text-anchor="middle" x="1506" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000110</text>
<polyline fill="none" stroke="black" points="1658.5,-181.5 1658.5,-217.5"/>
<text text-anchor="middle" x="1671.5" y="-195.8" font-family="Times,serif" font-size="14.00">6</text>
<polyline fill="none" stroke="black" points="1684.5,-181.5 1684.5,-217.5"/>
<text text-anchor="middle" x="1702" y="-195.8" font-family="Times,serif" font-size="14.00">13</text>
</g>
<!-- n7 -->
<g id="node8" class="node">
<title>
n7
</title>
<polygon fill="none" stroke="black" points="1292.5,-362.5 1292.5,-398.5 1346.5,-398.5 1346.5,-362.5 1292.5,-362.5"/>
<text text-anchor="middle" x="1319.5" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n7&#45;&gt;n8 -->
<g id="edge5" class="edge">
<title>
n7-&gt;n8
</title>
<path fill="none" stroke="black" d="M1299.09,-362.66C1260.39,-330.74 1176.27,-261.35 1131.65,-224.55"/>
<polygon fill="black" stroke="black" points="1134.13,-222.05 1124.19,-218.39 1129.67,-227.45 1134.13,-222.05"/>
</g>
<!-- n7&#45;&gt;n9 -->
<g id="edge6" class="edge">
<title>
n7-&gt;n9
</title>
<path fill="none" stroke="black" d="M1339.91,-362.66C1378.61,-330.74 1462.73,-261.35 1507.35,-224.55"/>
<polygon fill="black" stroke="black" points="1509.33,-227.45 1514.81,-218.39 1504.87,-222.05 1509.33,-227.45"/>
</g>
<!-- n1 -->
<g id="node9" class="node">
<title>
n1
</title>
<polygon fill="none" stroke="black" points="1292.5,-543.5 1292.5,-579.5 1346.5,-579.5 1346.5,-543.5 1292.5,-543.5"/>
<text text-anchor="middle" x="1319.5" y="-557.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n1&#45;&gt;n2 -->
<g id="edge7" class="edge">
<title>
n1-&gt;n2
</title>
<path fill="none" stroke="black" d="M1292.56,-553.04C1190.77,-524.83 829.31,-424.67 711.17,-391.94"/>
<polygon fill="black" stroke="black" points="712.22,-388.6 701.65,-389.3 710.35,-395.34 712.22,-388.6"/>
</g>
<!-- n1&#45;&gt;n7 -->
<g id="edge8" class="edge">
<title>
n1-&gt;n7
</title>
<path fill="none" stroke="black" d="M1319.5,-543.66C1319.5,-512.88 1319.5,-447.23 1319.5,-409.58"/>
<polygon fill="black" stroke="black" points="1323,-409.94 1319.5,-399.94 1316,-409.94 1323,-409.94"/>
</g>
<!-- n12 -->
<g id="node10" class="node">
<title>
n12
</title>
<polygon fill="none" stroke="black" points="1792,-181.5 1792,-217.5 2149,-217.5 2149,-181.5 1792,-181.5"/>
<text text-anchor="middle" x="1944.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000001</text>
<polyline fill="none" stroke="black" points="2097,-181.5 2097,-217.5"/>
<text text-anchor="middle" x="2110" y="-195.8" font-family="Times,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="2123,-181.5 2123,-217.5"/>
<text text-anchor="middle" x="2136" y="-195.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- n13 -->
<g id="node11" class="node">
<title>
n13
</title>
<polygon fill="none" stroke="black" points="2221,-181.5 2221,-217.5 2578,-217.5 2578,-181.5 2221,-181.5"/>
<text text-anchor="middle" x="2373.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000101</text>
<polyline fill="none" stroke="black" points="2526,-181.5 2526,-217.5"/>
<text text-anchor="middle" x="2539" y="-195.8" font-family="Times,serif" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="2552,-181.5 2552,-217.5"/>
<text text-anchor="middle" x="2565" y="-195.8" font-family="Times,serif" font-size="14.00">8</text>
</g>
<!-- n11 -->
<g id="node12" class="node">
<title>
n11
</title>
<polygon fill="none" stroke="black" points="2157.5,-362.5 2157.5,-398.5 2211.5,-398.5 2211.5,-362.5 2157.5,-362.5"/>
<text text-anchor="middle" x="2184.5" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n11&#45;&gt;n12 -->
<g id="edge9" class="edge">
<title>
n11-&gt;n12
</title>
<path fill="none" stroke="black" d="M2164.37,-362.66C2126.29,-330.81 2043.6,-261.64 1999.52,-224.78"/>
<polygon fill="black" stroke="black" points="2001.81,-222.13 1991.9,-218.4 1997.32,-227.5 2001.81,-222.13"/>
</g>
<!-- n11&#45;&gt;n13 -->
<g id="edge10" class="edge">
<title>
n11-&gt;n13
</title>
<path fill="none" stroke="black" d="M2204.73,-362.66C2242.98,-330.81 2326.06,-261.64 2370.34,-224.78"/>
<polygon fill="black" stroke="black" points="2372.56,-227.48 2378.01,-218.39 2368.08,-222.1 2372.56,-227.48"/>
</g>
<!-- n15 -->
<g id="node13" class="node">
<title>
n15
</title>
<polygon fill="none" stroke="black" points="2650,-181.5 2650,-217.5 3007,-217.5 3007,-181.5 2650,-181.5"/>
<text text-anchor="middle" x="2802.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000011</text>
<polyline fill="none" stroke="black" points="2955,-181.5 2955,-217.5"/>
<text text-anchor="middle" x="2968" y="-195.8" font-family="Times,serif" font-size="14.00">3</text>
<polyline fill="none" stroke="black" points="2981,-181.5 2981,-217.5"/>
<text text-anchor="middle" x="2994" y="-195.8" font-family="Times,serif" font-size="14.00">3</text>
</g>
<!-- n16 -->
<g id="node14" class="node">
<title>
n16
</title>
<polygon fill="none" stroke="black" points="3079.5,-181.5 3079.5,-217.5 3445.5,-217.5 3445.5,-181.5 3079.5,-181.5"/>
<text text-anchor="middle" x="3232" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000111</text>
<polyline fill="none" stroke="black" points="3384.5,-181.5 3384.5,-217.5"/>
<text text-anchor="middle" x="3397.5" y="-195.8" font-family="Times,serif" font-size="14.00">7</text>
<polyline fill="none" stroke="black" points="3410.5,-181.5 3410.5,-217.5"/>
<text text-anchor="middle" x="3428" y="-195.8" font-family="Times,serif" font-size="14.00">21</text>
</g>
<!-- n14 -->
<g id="node15" class="node">
<title>
n14
</title>
<polygon fill="none" stroke="black" points="2801.5,-362.5 2801.5,-398.5 2855.5,-398.5 2855.5,-362.5 2801.5,-362.5"/>
<text text-anchor="middle" x="2828.5" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n14&#45;&gt;n15 -->
<g id="edge11" class="edge">
<title>
n14-&gt;n15
</title>
<path fill="none" stroke="black" d="M2828.5,-362.66C2828.5,-331.88 2828.5,-266.23 2828.5,-228.58"/>
<polygon fill="black" stroke="black" points="2832,-228.94 2828.5,-218.94 2825,-228.94 2832,-228.94"/>
</g>
<!-- n14&#45;&gt;n16 -->
<g id="edge12" class="edge">
<title>
n14-&gt;n16
</title>
<path fill="none" stroke="black" d="M2855.32,-368.44C2925.98,-339.29 3118.19,-260.02 3211,-221.74"/>
<polygon fill="black" stroke="black" points="3212.23,-225.02 3220.14,-217.97 3209.56,-218.55 3212.23,-225.02"/>
</g>
<!-- n10 -->
<g id="node16" class="node">
<title>
n10
</title>
<polygon fill="none" stroke="black" points="2157.5,-543.5 2157.5,-579.5 2211.5,-579.5 2211.5,-543.5 2157.5,-543.5"/>
<text text-anchor="middle" x="2184.5" y="-557.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n10&#45;&gt;n11 -->
<g id="edge13" class="edge">
<title>
n10-&gt;n11
</title>
<path fill="none" stroke="black" d="M2184.5,-543.66C2184.5,-512.88 2184.5,-447.23 2184.5,-409.58"/>
<polygon fill="black" stroke="black" points="2188,-409.94 2184.5,-399.94 2181,-409.94 2188,-409.94"/>
</g>
<!-- n10&#45;&gt;n14 -->
<g id="edge14" class="edge">
<title>
n10-&gt;n14
</title>
<path fill="none" stroke="black" d="M2211.35,-553.04C2312.72,-524.86 2672.35,-424.9 2790.55,-392.05"/>
<polygon fill="black" stroke="black" points="2791.38,-395.45 2800.08,-389.4 2789.5,-388.71 2791.38,-395.45"/>
</g>
<!-- n0 -->
<g id="node17" class="node">
<title>
n0
</title>
<polygon fill="none" stroke="black" points="1726.5,-724.5 1726.5,-760.5 1780.5,-760.5 1780.5,-724.5 1726.5,-724.5"/>
<text text-anchor="middle" x="1753.5" y="-738.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n0&#45;&gt;n1 -->
<g id="edge15" class="edge">
<title>
n0-&gt;n1
</title>
<path fill="none" stroke="black" d="M1726.68,-730.44C1652.23,-699.73 1442.87,-613.38 1356.98,-577.96"/>
<polygon fill="black" stroke="black" points="1358.4,-574.76 1347.82,-574.18 1355.73,-581.23 1358.4,-574.76"/>
</g>
<!-- n0&#45;&gt;n10 -->
<g id="edge16" class="edge">
<title>
n0-&gt;n10
</title>
<path fill="none" stroke="black" d="M1780.46,-730.3C1854.61,-699.51 2061.75,-613.48 2147.05,-578.05"/>
<polygon fill="black" stroke="black" points="2148.26,-581.34 2156.15,-574.27 2145.57,-574.88 2148.26,-581.34"/>
</g>
</g>
</svg>
</div>
</details>
<p>As we can see, this data structure does actually work, and if that’s all we require, we could probably stop here. However, the most obvious issue is that the low branching factor of 2 means that our trees get too deep too quickly and that negatively impacts the time and space complexity of most operations. We will address this shortly, but first I would like to take a slight detour and do some <a href="https://martinfowler.com/articles/preparatory-refactoring-example.html">prefactoring</a> to make this possible: instead of having child nodes point directly to a parent node, let’s store a 2-element array in the parent node and have the children live there.</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">=</span><span> </span><span class="dt">Hash2ArrayMappedTrieNone</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">Hash2ArrayMappedTrieLeaf</span><span> </span><span class="dt">Hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">Hash2ArrayMappedTrieNode</span><span> </span><span class="ot">(</span><span class="dt">Vector</span><span> </span><span class="ot">(</span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span class="ot">)</span><span class="ot">)</span><span>
    </span><span class="kw">deriving</span><span> </span><span class="ot">(</span><span class="dt">Eq</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span class="ot">)</span></code></pre></div>
<p>We can reuse most of our existing code with only minor changes to account for the existence of the array, which will always have two elements.</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">insertHash2ArrayMappedTrie</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHash2ArrayMappedTrieHelper</span><span> </span><span class="dv">0</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="va">key</span><span>

</span><span class="va">insertHash2ArrayMappedTrieHelper</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="dt">Hash2ArrayMappedTrieNone</span><span> </span><span class="ot">=</span><span>
    </span><span class="dt">Hash2ArrayMappedTrieLeaf</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="dt">Hash2ArrayMappedTrieLeaf</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span class="ot">)</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">hash</span><span> </span><span class="op">==</span><span> </span><span class="va">leafHash</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Hash2ArrayMappedTrieLeaf</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">otherwise</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
        </span><span class="va">emptyNode</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Hash2ArrayMappedTrieNode</span><span> </span><span class="ot">(</span><span class="va">replicate</span><span> </span><span class="dv">2</span><span> </span><span class="dt">Hash2ArrayMappedTrieNone</span><span class="ot">)</span><span>
        </span><span class="va">leafInsertedNode</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span> </span><span class="va">emptyNode</span><span>
        </span><span class="kw">in</span><span> </span><span class="va">insertHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="va">leafInsertedNode</span><span>
</span><span class="va">insertHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="dt">Hash2ArrayMappedTrieNode</span><span> </span><span class="va">children</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">goRight</span><span> </span><span class="ot">=</span><span> </span><span class="va">testBit</span><span> </span><span class="va">hash</span><span> </span><span class="va">depth</span><span>
    </span><span class="va">depth&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">depth</span><span> </span><span class="op">+</span><span> </span><span class="dv">1</span><span>
    </span><span class="kw">in</span><span> </span><span class="kw">if</span><span> </span><span class="va">goRight</span><span>
        </span><span class="kw">then</span><span> </span><span class="dt">Hash2ArrayMappedTrieNode</span><span> </span><span class="op">$</span><span> </span><span class="va">children</span><span> </span><span class="op">//</span><span> </span><span class="ot">[</span><span class="ot">(</span><span class="dv">1</span><span class="ot">,</span><span> </span><span class="va">insertHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="va">children</span><span> </span><span class="op">!</span><span> </span><span class="dv">1</span><span class="ot">)</span><span class="ot">)</span><span class="ot">]</span><span>
        </span><span class="kw">else</span><span> </span><span class="dt">Hash2ArrayMappedTrieNode</span><span> </span><span class="op">$</span><span> </span><span class="va">children</span><span> </span><span class="op">//</span><span> </span><span class="ot">[</span><span class="ot">(</span><span class="dv">0</span><span class="ot">,</span><span> </span><span class="va">insertHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="va">children</span><span> </span><span class="op">!</span><span> </span><span class="dv">0</span><span class="ot">)</span><span class="ot">)</span><span class="ot">]</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">lookupHash2ArrayMappedTrie</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Maybe</span><span> </span><span class="va">value</span><span>
</span><span class="va">lookupHash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="ot">=</span><span> </span><span class="va">lookupHash2ArrayMappedTrieHelper</span><span> </span><span class="dv">0</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="va">key</span><span>

</span><span class="va">lookupHash2ArrayMappedTrieHelper</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Maybe</span><span> </span><span class="va">value</span><span>
</span><span class="va">lookupHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="dt">Hash2ArrayMappedTrieNone</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Nothing</span><span>
</span><span class="va">lookupHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="dt">Hash2ArrayMappedTrieLeaf</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span class="ot">)</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">hash</span><span> </span><span class="op">==</span><span> </span><span class="va">leafHash</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Just</span><span> </span><span class="va">leafValue</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">otherwise</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Nothing</span><span>
</span><span class="va">lookupHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="dt">Hash2ArrayMappedTrieNode</span><span> </span><span class="va">children</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">goRight</span><span> </span><span class="ot">=</span><span> </span><span class="va">testBit</span><span> </span><span class="va">hash</span><span> </span><span class="va">depth</span><span>
    </span><span class="va">depth&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">depth</span><span> </span><span class="op">+</span><span> </span><span class="dv">1</span><span>
    </span><span class="kw">in</span><span> </span><span class="kw">if</span><span> </span><span class="va">goRight</span><span>
        </span><span class="kw">then</span><span> </span><span class="va">lookupHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="va">children</span><span> </span><span class="op">!</span><span> </span><span class="dv">1</span><span class="ot">)</span><span>
        </span><span class="kw">else</span><span> </span><span class="va">lookupHash2ArrayMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="va">children</span><span> </span><span class="op">!</span><span> </span><span class="dv">0</span><span class="ot">)</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">emptyHash2ArrayMappedTrie</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Hash2ArrayMappedTrieNone</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">instance</span><span> </span><span class="dt">Mapping</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="kw">where</span><span>
    </span><span class="va">empty</span><span> </span><span class="ot">=</span><span> </span><span class="va">emptyHash2ArrayMappedTrie</span><span>
    </span><span class="va">insert</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHash2ArrayMappedTrie</span><span>
    </span><span class="va">lookup</span><span> </span><span class="ot">=</span><span> </span><span class="va">lookupHash2ArrayMappedTrie</span></code></pre></div>
And as before we can define a function to render this tree using Graphviz:
<details>
<summary>
Hash 2-Array Mapped Trie
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span><span> </span><span class="dt">Hash2ArrayMappedTrieGraphvizNode</span><span>
    </span><span class="ot">=</span><span> </span><span class="dt">Hash2ArrayMappedTrieGraphvizNode</span><span>
        </span><span class="ot">{</span><span> </span><span class="va">hash2ArrayMappedTrieGraphvizNodeId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hash2ArrayMappedTrieGraphvizFields</span><span> </span><span class="ot">::</span><span> </span><span class="ot">[</span><span class="dt">Int</span><span class="ot">]</span><span>
        </span><span class="ot">}</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">Hash2ArrayMappedTrieGraphvizLeafNode</span><span>
        </span><span class="ot">{</span><span> </span><span class="va">hash2ArrayMappedTrieGraphvizLeafNodeId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hash2ArrayMappedTrieGraphvizLeafHash</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hash2ArrayMappedTrieGraphvizLeafKey</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hash2ArrayMappedTrieGraphvizLeafNodeValue</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">}</span><span>
    </span><span class="kw">deriving</span><span> </span><span class="ot">(</span><span class="dt">Eq</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span class="ot">)</span><span>

</span><span class="va">numberH2AMT</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Show</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">WriterT</span><span> </span><span class="ot">[</span><span class="dt">Hash2ArrayMappedTrieGraphvizNode</span><span class="ot">]</span><span> </span><span class="ot">(</span><span class="dt">State</span><span> </span><span class="dt">Int</span><span class="ot">)</span><span> </span><span class="dt">Int</span><span>
</span><span class="va">numberH2AMT</span><span> </span><span class="dt">Hash2ArrayMappedTrieNone</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">tell</span><span> </span><span class="va">mempty</span><span>
    </span><span class="va">pure</span><span> </span><span class="dv">0</span><span>
</span><span class="va">numberH2AMT</span><span> </span><span class="ot">(</span><span class="dt">Hash2ArrayMappedTrieLeaf</span><span> </span><span class="va">h</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">i</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">lift</span><span> </span><span class="va">getFreshId</span><span>
    </span><span class="va">tell</span><span> </span><span class="ot">[</span><span class="dt">Hash2ArrayMappedTrieGraphvizLeafNode</span><span> </span><span class="va">i</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">h</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">k</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">v</span><span class="ot">)</span><span class="ot">]</span><span>
    </span><span class="va">pure</span><span> </span><span class="va">i</span><span>
</span><span class="va">numberH2AMT</span><span> </span><span class="ot">(</span><span class="dt">Hash2ArrayMappedTrieNode</span><span> </span><span class="va">hs</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">i</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">lift</span><span> </span><span class="va">getFreshId</span><span>
    </span><span class="va">numbered</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">Vector.toList</span><span> </span><span class="op">&lt;$&gt;</span><span> </span><span class="va">traverse</span><span> </span><span class="va">numberH2AMT</span><span> </span><span class="va">hs</span><span>
    </span><span class="va">tell</span><span> </span><span class="ot">[</span><span class="dt">Hash2ArrayMappedTrieGraphvizNode</span><span> </span><span class="va">i</span><span> </span><span class="va">numbered</span><span class="ot">]</span><span>
    </span><span class="va">pure</span><span> </span><span class="va">i</span><span>

</span><span class="va">nodeLinesH2AMT</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Hash2ArrayMappedTrieGraphvizNode</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="ot">[</span><span class="dt">String</span><span class="ot">]</span><span>
</span><span class="va">nodeLinesH2AMT</span><span> </span><span class="ot">(</span><span class="dt">Hash2ArrayMappedTrieGraphvizLeafNode</span><span> </span><span class="va">i</span><span> </span><span class="va">h</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="kw">label</span><span> </span><span class="ot">=</span><span> </span><span class="va">intercalate</span><span> </span><span class="st">&quot;|&quot;</span><span> </span><span class="ot">[</span><span class="va">h</span><span class="ot">,</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="va">v</span><span class="ot">]</span><span>
    </span><span class="va">line</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span class="ot">)</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;[label=\&quot;&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">escape</span><span> </span><span class="kw">label</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;\&quot;]&quot;</span><span>
    </span><span class="kw">in</span><span> </span><span class="ot">[</span><span class="va">line</span><span class="ot">]</span><span>
</span><span class="va">nodeLinesH2AMT</span><span> </span><span class="ot">(</span><span class="dt">Hash2ArrayMappedTrieGraphvizNode</span><span> </span><span class="va">i</span><span> </span><span class="va">fs</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">indices</span><span> </span><span class="ot">=</span><span> </span><span class="va">Prelude.take</span><span> </span><span class="ot">(</span><span class="va">length</span><span> </span><span class="va">fs</span><span class="ot">)</span><span> </span><span class="ot">[</span><span class="dv">0</span><span class="ot">..</span><span class="ot">]</span><span>
    </span><span class="va">pairs</span><span> </span><span class="ot">=</span><span> </span><span class="va">zip</span><span> </span><span class="va">indices</span><span> </span><span class="va">fs</span><span>
    </span><span class="va">edges</span><span> </span><span class="ot">=</span><span> </span><span class="va">flip</span><span> </span><span class="va">map</span><span> </span><span class="va">pairs</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="ot">(</span><span class="va">f</span><span class="ot">,</span><span class="va">t</span><span class="ot">)</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;:&quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;f&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">f</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; -&gt; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">t</span><span>
    </span><span class="va">fields</span><span> </span><span class="ot">=</span><span> </span><span class="va">flip</span><span> </span><span class="va">map</span><span> </span><span class="va">indices</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="va">ix</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;&lt;f&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">ix</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;&gt;&quot;</span><span>
    </span><span class="kw">label</span><span> </span><span class="ot">=</span><span> </span><span class="va">intercalate</span><span> </span><span class="st">&quot;|&quot;</span><span> </span><span class="va">fields</span><span>
    </span><span class="va">line</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span class="ot">)</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;[label=\&quot;&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">escape</span><span> </span><span class="kw">label</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;\&quot;]&quot;</span><span>
    </span><span class="kw">in</span><span> </span><span class="ot">(</span><span class="va">line</span><span class="ot">:</span><span class="va">edges</span><span class="ot">)</span><span>

</span><span class="va">dotFromH2AMT</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Show</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="dt">Hash2ArrayMappedTrie</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">String</span><span>
</span><span class="va">dotFromH2AMT</span><span> </span><span class="ot">=</span><span> </span><span class="va">makeDot</span><span> </span><span class="op">.</span><span> </span><span class="va">makeDotLines</span><span class="op">.</span><span> </span><span class="va">concatMap</span><span> </span><span class="va">nodeLinesH2AMT</span><span> </span><span class="op">.</span><span> </span><span class="va">flip</span><span> </span><span class="va">evalState</span><span> </span><span class="dv">0</span><span> </span><span class="op">.</span><span> </span><span class="va">execWriterT</span><span> </span><span class="op">.</span><span> </span><span class="va">numberH2AMT</span></code></pre></div>
</details>
The corresponding tree created by <code>fib' 8</code> looks very similar:
<details>
<summary>
Hash 2-Array Mapped Trie
</summary>
<div style="overflow: scroll">
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">dot</span><span> </span><span class="op">$</span><span> </span><span class="va">dotFromH2AMT</span><span> </span><span class="op">$</span><span> </span><span class="va">snd</span><span> </span><span class="op">$</span><span> </span><span class="va">fib&#39;</span><span> </span><span class="va">emptyHash2ArrayMappedTrie</span><span> </span><span class="dv">8</span></code></pre></div>
<svg width="3454pt" height="769pt" viewBox="0.00 0.00 3453.50 769.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 765)">
<polygon fill="white" stroke="none" points="-4,4 -4,-765 3449.5,-765 3449.5,4 -4,4"/>
<!-- n4 -->
<g id="node1" class="node">
<title>
n4
</title>
<polygon fill="none" stroke="black" points="0,-0.5 0,-36.5 357,-36.5 357,-0.5 0,-0.5"/>
<text text-anchor="middle" x="152.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000000</text>
<polyline fill="none" stroke="black" points="305,-0.5 305,-36.5"/>
<text text-anchor="middle" x="318" y="-14.8" font-family="Times,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="331,-0.5 331,-36.5"/>
<text text-anchor="middle" x="344" y="-14.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- n5 -->
<g id="node2" class="node">
<title>
n5
</title>
<polygon fill="none" stroke="black" points="429.5,-0.5 429.5,-36.5 795.5,-36.5 795.5,-0.5 429.5,-0.5"/>
<text text-anchor="middle" x="582" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000001000</text>
<polyline fill="none" stroke="black" points="734.5,-0.5 734.5,-36.5"/>
<text text-anchor="middle" x="747.5" y="-14.8" font-family="Times,serif" font-size="14.00">8</text>
<polyline fill="none" stroke="black" points="760.5,-0.5 760.5,-36.5"/>
<text text-anchor="middle" x="778" y="-14.8" font-family="Times,serif" font-size="14.00">34</text>
</g>
<!-- n3 -->
<g id="node3" class="node">
<title>
n3
</title>
<polygon fill="none" stroke="black" points="368.5,-181.5 368.5,-217.5 422.5,-217.5 422.5,-181.5 368.5,-181.5"/>
<text text-anchor="middle" x="382" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="395.5,-181.5 395.5,-217.5"/>
<text text-anchor="middle" x="409" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n3&#45;&gt;n4 -->
<g id="edge1" class="edge">
<title>
n3:f0-&gt;n4
</title>
<path fill="none" stroke="black" d="M381.5,-181C381.5,-181 265.26,-88.53 208.7,-43.53"/>
<polygon fill="black" stroke="black" points="211.03,-40.91 201.03,-37.42 206.67,-46.39 211.03,-40.91"/>
</g>
<!-- n3&#45;&gt;n5 -->
<g id="edge2" class="edge">
<title>
n3:f1-&gt;n5
</title>
<path fill="none" stroke="black" d="M409.5,-181C409.5,-181 525.74,-88.53 582.3,-43.53"/>
<polygon fill="black" stroke="black" points="584.33,-46.39 589.97,-37.42 579.97,-40.91 584.33,-46.39"/>
</g>
<!-- n6 -->
<g id="node4" class="node">
<title>
n6
</title>
<polygon fill="none" stroke="black" points="495,-181.5 495,-217.5 852,-217.5 852,-181.5 495,-181.5"/>
<text text-anchor="middle" x="647.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000100</text>
<polyline fill="none" stroke="black" points="800,-181.5 800,-217.5"/>
<text text-anchor="middle" x="813" y="-195.8" font-family="Times,serif" font-size="14.00">4</text>
<polyline fill="none" stroke="black" points="826,-181.5 826,-217.5"/>
<text text-anchor="middle" x="839" y="-195.8" font-family="Times,serif" font-size="14.00">5</text>
</g>
<!-- n2 -->
<g id="node5" class="node">
<title>
n2
</title>
<polygon fill="none" stroke="black" points="632.5,-362.5 632.5,-398.5 686.5,-398.5 686.5,-362.5 632.5,-362.5"/>
<text text-anchor="middle" x="646" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="659.5,-362.5 659.5,-398.5"/>
<text text-anchor="middle" x="673" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n2&#45;&gt;n3 -->
<g id="edge3" class="edge">
<title>
n2:f0-&gt;n3
</title>
<path fill="none" stroke="black" d="M631.5,-380.5C631.5,-380.5 490.65,-273.07 426.57,-224.2"/>
<polygon fill="black" stroke="black" points="428.98,-221.63 418.91,-218.35 424.73,-227.2 428.98,-221.63"/>
</g>
<!-- n2&#45;&gt;n6 -->
<g id="edge4" class="edge">
<title>
n2:f1-&gt;n6
</title>
<path fill="none" stroke="black" d="M673.5,-362C673.5,-362 673.5,-275.16 673.5,-228.83"/>
<polygon fill="black" stroke="black" points="677,-228.99 673.5,-218.99 670,-228.99 677,-228.99"/>
</g>
<!-- n8 -->
<g id="node6" class="node">
<title>
n8
</title>
<polygon fill="none" stroke="black" points="924,-181.5 924,-217.5 1281,-217.5 1281,-181.5 924,-181.5"/>
<text text-anchor="middle" x="1076.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000010</text>
<polyline fill="none" stroke="black" points="1229,-181.5 1229,-217.5"/>
<text text-anchor="middle" x="1242" y="-195.8" font-family="Times,serif" font-size="14.00">2</text>
<polyline fill="none" stroke="black" points="1255,-181.5 1255,-217.5"/>
<text text-anchor="middle" x="1268" y="-195.8" font-family="Times,serif" font-size="14.00">2</text>
</g>
<!-- n9 -->
<g id="node7" class="node">
<title>
n9
</title>
<polygon fill="none" stroke="black" points="1353.5,-181.5 1353.5,-217.5 1719.5,-217.5 1719.5,-181.5 1353.5,-181.5"/>
<text text-anchor="middle" x="1506" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000110</text>
<polyline fill="none" stroke="black" points="1658.5,-181.5 1658.5,-217.5"/>
<text text-anchor="middle" x="1671.5" y="-195.8" font-family="Times,serif" font-size="14.00">6</text>
<polyline fill="none" stroke="black" points="1684.5,-181.5 1684.5,-217.5"/>
<text text-anchor="middle" x="1702" y="-195.8" font-family="Times,serif" font-size="14.00">13</text>
</g>
<!-- n7 -->
<g id="node8" class="node">
<title>
n7
</title>
<polygon fill="none" stroke="black" points="1292.5,-362.5 1292.5,-398.5 1346.5,-398.5 1346.5,-362.5 1292.5,-362.5"/>
<text text-anchor="middle" x="1306" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1319.5,-362.5 1319.5,-398.5"/>
<text text-anchor="middle" x="1333" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n7&#45;&gt;n8 -->
<g id="edge5" class="edge">
<title>
n7:f0-&gt;n8
</title>
<path fill="none" stroke="black" d="M1305.5,-362C1305.5,-362 1189.26,-269.53 1132.7,-224.53"/>
<polygon fill="black" stroke="black" points="1135.03,-221.91 1125.03,-218.42 1130.67,-227.39 1135.03,-221.91"/>
</g>
<!-- n7&#45;&gt;n9 -->
<g id="edge6" class="edge">
<title>
n7:f1-&gt;n9
</title>
<path fill="none" stroke="black" d="M1333.5,-362C1333.5,-362 1449.74,-269.53 1506.3,-224.53"/>
<polygon fill="black" stroke="black" points="1508.33,-227.39 1513.97,-218.42 1503.97,-221.91 1508.33,-227.39"/>
</g>
<!-- n1 -->
<g id="node9" class="node">
<title>
n1
</title>
<polygon fill="none" stroke="black" points="1278.5,-543.5 1278.5,-579.5 1332.5,-579.5 1332.5,-543.5 1278.5,-543.5"/>
<text text-anchor="middle" x="1292" y="-557.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1305.5,-543.5 1305.5,-579.5"/>
<text text-anchor="middle" x="1319" y="-557.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n1&#45;&gt;n2 -->
<g id="edge7" class="edge">
<title>
n1:f0-&gt;n2
</title>
<path fill="none" stroke="black" d="M1277.5,-561.5C1277.5,-561.5 831.76,-431.67 697.39,-392.54"/>
<polygon fill="black" stroke="black" points="698.49,-389.21 687.91,-389.77 696.53,-395.93 698.49,-389.21"/>
</g>
<!-- n1&#45;&gt;n7 -->
<g id="edge8" class="edge">
<title>
n1:f1-&gt;n7
</title>
<path fill="none" stroke="black" d="M1319.5,-543C1319.5,-543 1319.5,-456.16 1319.5,-409.83"/>
<polygon fill="black" stroke="black" points="1323,-409.99 1319.5,-399.99 1316,-409.99 1323,-409.99"/>
</g>
<!-- n12 -->
<g id="node10" class="node">
<title>
n12
</title>
<polygon fill="none" stroke="black" points="1792,-181.5 1792,-217.5 2149,-217.5 2149,-181.5 1792,-181.5"/>
<text text-anchor="middle" x="1944.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000001</text>
<polyline fill="none" stroke="black" points="2097,-181.5 2097,-217.5"/>
<text text-anchor="middle" x="2110" y="-195.8" font-family="Times,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="2123,-181.5 2123,-217.5"/>
<text text-anchor="middle" x="2136" y="-195.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- n13 -->
<g id="node11" class="node">
<title>
n13
</title>
<polygon fill="none" stroke="black" points="2221,-181.5 2221,-217.5 2578,-217.5 2578,-181.5 2221,-181.5"/>
<text text-anchor="middle" x="2373.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000101</text>
<polyline fill="none" stroke="black" points="2526,-181.5 2526,-217.5"/>
<text text-anchor="middle" x="2539" y="-195.8" font-family="Times,serif" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="2552,-181.5 2552,-217.5"/>
<text text-anchor="middle" x="2565" y="-195.8" font-family="Times,serif" font-size="14.00">8</text>
</g>
<!-- n11 -->
<g id="node12" class="node">
<title>
n11
</title>
<polygon fill="none" stroke="black" points="2157.5,-362.5 2157.5,-398.5 2211.5,-398.5 2211.5,-362.5 2157.5,-362.5"/>
<text text-anchor="middle" x="2171" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="2184.5,-362.5 2184.5,-398.5"/>
<text text-anchor="middle" x="2198" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n11&#45;&gt;n12 -->
<g id="edge9" class="edge">
<title>
n11:f0-&gt;n12
</title>
<path fill="none" stroke="black" d="M2170.5,-362C2170.5,-362 2055.98,-269.53 2000.26,-224.53"/>
<polygon fill="black" stroke="black" points="2002.68,-221.99 1992.7,-218.43 1998.28,-227.43 2002.68,-221.99"/>
</g>
<!-- n11&#45;&gt;n13 -->
<g id="edge10" class="edge">
<title>
n11:f1-&gt;n13
</title>
<path fill="none" stroke="black" d="M2198.5,-362C2198.5,-362 2313.59,-269.53 2369.6,-224.53"/>
<polygon fill="black" stroke="black" points="2371.59,-227.42 2377.19,-218.43 2367.2,-221.96 2371.59,-227.42"/>
</g>
<!-- n15 -->
<g id="node13" class="node">
<title>
n15
</title>
<polygon fill="none" stroke="black" points="2650,-181.5 2650,-217.5 3007,-217.5 3007,-181.5 2650,-181.5"/>
<text text-anchor="middle" x="2802.5" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000011</text>
<polyline fill="none" stroke="black" points="2955,-181.5 2955,-217.5"/>
<text text-anchor="middle" x="2968" y="-195.8" font-family="Times,serif" font-size="14.00">3</text>
<polyline fill="none" stroke="black" points="2981,-181.5 2981,-217.5"/>
<text text-anchor="middle" x="2994" y="-195.8" font-family="Times,serif" font-size="14.00">3</text>
</g>
<!-- n16 -->
<g id="node14" class="node">
<title>
n16
</title>
<polygon fill="none" stroke="black" points="3079.5,-181.5 3079.5,-217.5 3445.5,-217.5 3445.5,-181.5 3079.5,-181.5"/>
<text text-anchor="middle" x="3232" y="-195.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000111</text>
<polyline fill="none" stroke="black" points="3384.5,-181.5 3384.5,-217.5"/>
<text text-anchor="middle" x="3397.5" y="-195.8" font-family="Times,serif" font-size="14.00">7</text>
<polyline fill="none" stroke="black" points="3410.5,-181.5 3410.5,-217.5"/>
<text text-anchor="middle" x="3428" y="-195.8" font-family="Times,serif" font-size="14.00">21</text>
</g>
<!-- n14 -->
<g id="node15" class="node">
<title>
n14
</title>
<polygon fill="none" stroke="black" points="2815.5,-362.5 2815.5,-398.5 2869.5,-398.5 2869.5,-362.5 2815.5,-362.5"/>
<text text-anchor="middle" x="2829" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="2842.5,-362.5 2842.5,-398.5"/>
<text text-anchor="middle" x="2856" y="-376.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n14&#45;&gt;n15 -->
<g id="edge11" class="edge">
<title>
n14:f0-&gt;n15
</title>
<path fill="none" stroke="black" d="M2828.5,-362C2828.5,-362 2828.5,-275.16 2828.5,-228.83"/>
<polygon fill="black" stroke="black" points="2832,-228.99 2828.5,-218.99 2825,-228.99 2832,-228.99"/>
</g>
<!-- n14&#45;&gt;n16 -->
<g id="edge12" class="edge">
<title>
n14:f1-&gt;n16
</title>
<path fill="none" stroke="black" d="M2870.5,-380.5C2870.5,-380.5 3111.22,-269.97 3215.39,-222.13"/>
<polygon fill="black" stroke="black" points="3216.62,-225.42 3224.25,-218.07 3213.7,-219.06 3216.62,-225.42"/>
</g>
<!-- n10 -->
<g id="node16" class="node">
<title>
n10
</title>
<polygon fill="none" stroke="black" points="2171.5,-543.5 2171.5,-579.5 2225.5,-579.5 2225.5,-543.5 2171.5,-543.5"/>
<text text-anchor="middle" x="2185" y="-557.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="2198.5,-543.5 2198.5,-579.5"/>
<text text-anchor="middle" x="2212" y="-557.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n10&#45;&gt;n11 -->
<g id="edge13" class="edge">
<title>
n10:f0-&gt;n11
</title>
<path fill="none" stroke="black" d="M2184.5,-543C2184.5,-543 2184.5,-456.16 2184.5,-409.83"/>
<polygon fill="black" stroke="black" points="2188,-409.99 2184.5,-399.99 2181,-409.99 2188,-409.99"/>
</g>
<!-- n10&#45;&gt;n14 -->
<g id="edge14" class="edge">
<title>
n10:f1-&gt;n14
</title>
<path fill="none" stroke="black" d="M2226.5,-561.5C2226.5,-561.5 2670.8,-431.67 2804.73,-392.54"/>
<polygon fill="black" stroke="black" points="2805.57,-395.94 2814.18,-389.77 2803.6,-389.22 2805.57,-395.94"/>
</g>
<!-- n0 -->
<g id="node17" class="node">
<title>
n0
</title>
<polygon fill="none" stroke="black" points="1726.5,-724.5 1726.5,-760.5 1780.5,-760.5 1780.5,-724.5 1726.5,-724.5"/>
<text text-anchor="middle" x="1740" y="-738.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1753.5,-724.5 1753.5,-760.5"/>
<text text-anchor="middle" x="1767" y="-738.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n0&#45;&gt;n1 -->
<g id="edge15" class="edge">
<title>
n0:f0-&gt;n1
</title>
<path fill="none" stroke="black" d="M1725.5,-742.5C1725.5,-742.5 1445.86,-622.65 1342.94,-578.54"/>
<polygon fill="black" stroke="black" points="1344.37,-575.35 1333.8,-574.63 1341.62,-581.79 1344.37,-575.35"/>
</g>
<!-- n0&#45;&gt;n10 -->
<g id="edge16" class="edge">
<title>
n0:f1-&gt;n10
</title>
<path fill="none" stroke="black" d="M1781.5,-742.5C1781.5,-742.5 2059.14,-622.65 2161.33,-578.54"/>
<polygon fill="black" stroke="black" points="2162.6,-581.81 2170.39,-574.63 2159.82,-575.38 2162.6,-581.81"/>
</g>
</g>
</svg>
</div>
</details>
<p>Now that we’re using arrays, we can fix our branching factor problem by recognising the relationship between the number of bits of the hash that we are plucking off and inspecting at each level and the children each node can have. So far we have only been inspecting one bit, which can have two values and therefore two children. If we were to inspect two bits at each level, we could have four possible children per fragment (corresponding to the values 00, 01, 10, and 11), 8 children for 3 bits, and so on. I’ve chosen to use 4 bits which means 16 children.</p>
<p>I’m going to call this iteration <code>HashArrayMappedTrieSpacious</code> because it’s space-inefficient in a way we’ll discuss and fix later.</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousNone</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousLeaf</span><span> </span><span class="dt">Hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousNode</span><span> </span><span class="ot">(</span><span class="dt">Vector</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span class="ot">)</span><span class="ot">)</span><span>
    </span><span class="kw">deriving</span><span> </span><span class="ot">(</span><span class="dt">Eq</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span class="ot">)</span></code></pre></div>
<p>An important point is that we re-interpret the hash fragment as the index into our array, e.g. <code>0110</code> is the 6th index. We’ll need some bit-twiddling functions to make this easier.</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">hashFragmentLength</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
</span><span class="va">hashFragmentLength</span><span> </span><span class="ot">=</span><span> </span><span class="dv">4</span><span>

</span><span class="va">hashMask</span><span> </span><span class="ot">=</span><span> </span><span class="va">bit</span><span> </span><span class="va">hashFragmentLength</span><span> </span><span class="op">-</span><span> </span><span class="dv">1</span><span> </span><span class="co">-- 0b1111</span></code></pre></div>
<p>To <code>insert</code> and <code>lookup</code> elements, we now need to:</p>
<ol type="1">
<li>Mask off the correct 4 bits of the hash.</li>
<li>Interpret the 4-bit hash fragment as an index from 0 to 15.</li>
<li>Insert/lookup the element at the corresponding index of the array, recursively creating it if required.</li>
</ol>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">insertHashArrayMappedTrieSpacious</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="dv">0</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="va">key</span><span>

</span><span class="va">insertHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousNone</span><span> </span><span class="ot">=</span><span>
    </span><span class="dt">HashArrayMappedTrieSpaciousLeaf</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieSpaciousLeaf</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span class="ot">)</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">hash</span><span> </span><span class="op">==</span><span> </span><span class="va">leafHash</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousLeaf</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">otherwise</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
        </span><span class="va">emptyNode</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousNode</span><span> </span><span class="ot">(</span><span class="va">replicate</span><span> </span><span class="ot">(</span><span class="dv">2</span><span class="op">^</span><span class="va">hashFragmentLength</span><span class="ot">)</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousNone</span><span class="ot">)</span><span>
        </span><span class="va">leafInsertedNode</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span> </span><span class="va">emptyNode</span><span>
        </span><span class="kw">in</span><span> </span><span class="va">insertHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="va">leafInsertedNode</span><span>
</span><span class="va">insertHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieSpaciousNode</span><span> </span><span class="va">children</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">hashFragment</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="ot">`</span><span class="va">shiftR</span><span class="ot">`</span><span> </span><span class="va">depth</span><span class="ot">)</span><span> </span><span class="op">.&amp;.</span><span> </span><span class="va">hashMask</span><span>
    </span><span class="va">index</span><span> </span><span class="ot">=</span><span> </span><span class="va">fromIntegral</span><span> </span><span class="va">hashFragment</span><span>
    </span><span class="va">depth&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">depth</span><span> </span><span class="op">+</span><span> </span><span class="va">hashFragmentLength</span><span>
    </span><span class="kw">in</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousNode</span><span>
        </span><span class="ot">(</span><span class="va">children</span><span> </span><span class="op">//</span><span> </span><span class="ot">[</span><span class="ot">(</span><span class="va">index</span><span class="ot">,</span><span> </span><span class="va">insertHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="va">children</span><span> </span><span class="op">!</span><span> </span><span class="va">index</span><span class="ot">)</span><span class="ot">)</span><span class="ot">]</span><span class="ot">)</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">lookupHashArrayMappedTrieSpacious</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Maybe</span><span> </span><span class="va">value</span><span>
</span><span class="va">lookupHashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="ot">=</span><span> </span><span class="va">lookupHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="dv">0</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="va">key</span><span>

</span><span class="va">lookupHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Maybe</span><span> </span><span class="va">value</span><span>
</span><span class="va">lookupHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousNone</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Nothing</span><span>
</span><span class="va">lookupHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieSpaciousLeaf</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span class="ot">)</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">hash</span><span> </span><span class="op">==</span><span> </span><span class="va">leafHash</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Just</span><span> </span><span class="va">leafValue</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">otherwise</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Nothing</span><span>
</span><span class="va">lookupHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieSpaciousNode</span><span> </span><span class="va">children</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">hashFragment</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="ot">`</span><span class="va">shiftR</span><span class="ot">`</span><span> </span><span class="va">depth</span><span class="ot">)</span><span> </span><span class="op">.&amp;.</span><span> </span><span class="va">hashMask</span><span>
    </span><span class="va">index</span><span> </span><span class="ot">=</span><span> </span><span class="va">fromIntegral</span><span> </span><span class="va">hashFragment</span><span>
    </span><span class="va">depth&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">depth</span><span> </span><span class="op">+</span><span> </span><span class="va">hashFragmentLength</span><span>
    </span><span class="kw">in</span><span> </span><span class="va">lookupHashArrayMappedTrieSpaciousHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="va">children</span><span> </span><span class="op">!</span><span> </span><span class="va">index</span><span class="ot">)</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">emptyHashArrayMappedTrieSpacious</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousNone</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">instance</span><span> </span><span class="dt">Mapping</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="kw">where</span><span>
    </span><span class="va">empty</span><span> </span><span class="ot">=</span><span> </span><span class="va">emptyHashArrayMappedTrieSpacious</span><span>
    </span><span class="va">insert</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHashArrayMappedTrieSpacious</span><span>
    </span><span class="va">lookup</span><span> </span><span class="ot">=</span><span> </span><span class="va">lookupHashArrayMappedTrieSpacious</span></code></pre></div>
<p>Once again we can define a rendering function:</p>
<details>
<summary>
Hash Array Mapped Trie (Spacious)
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousGraphvizNode</span><span>
    </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousGraphvizNode</span><span>
        </span><span class="ot">{</span><span> </span><span class="va">hashArrayMappedTrieSpaciousGraphvizNodeId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashArrayMappedTrieSpaciousGraphvizFields</span><span> </span><span class="ot">::</span><span> </span><span class="ot">[</span><span class="dt">Int</span><span class="ot">]</span><span>
        </span><span class="ot">}</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousGraphvizLeafNode</span><span>
        </span><span class="ot">{</span><span> </span><span class="va">hashArrayMappedTrieSpaciousGraphvizLeafNodeId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashArrayMappedTrieSpaciousGraphvizLeafHash</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashArrayMappedTrieSpaciousGraphvizLeafKey</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashArrayMappedTrieSpaciousGraphvizLeafNodeValue</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">}</span><span>
    </span><span class="kw">deriving</span><span> </span><span class="ot">(</span><span class="dt">Eq</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span class="ot">)</span><span>

</span><span class="va">numberHAMTS</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Show</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">WriterT</span><span> </span><span class="ot">[</span><span class="dt">HashArrayMappedTrieSpaciousGraphvizNode</span><span class="ot">]</span><span> </span><span class="ot">(</span><span class="dt">State</span><span> </span><span class="dt">Int</span><span class="ot">)</span><span> </span><span class="dt">Int</span><span>
</span><span class="va">numberHAMTS</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousNone</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">tell</span><span> </span><span class="va">mempty</span><span>
    </span><span class="va">pure</span><span> </span><span class="dv">0</span><span>
</span><span class="va">numberHAMTS</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieSpaciousLeaf</span><span> </span><span class="va">h</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">i</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">lift</span><span> </span><span class="va">getFreshId</span><span>
    </span><span class="va">tell</span><span> </span><span class="ot">[</span><span class="dt">HashArrayMappedTrieSpaciousGraphvizLeafNode</span><span> </span><span class="va">i</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">h</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">k</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">v</span><span class="ot">)</span><span class="ot">]</span><span>
    </span><span class="va">pure</span><span> </span><span class="va">i</span><span>
</span><span class="va">numberHAMTS</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieSpaciousNode</span><span> </span><span class="va">hs</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">i</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">lift</span><span> </span><span class="va">getFreshId</span><span>
    </span><span class="va">numbered</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">Vector.toList</span><span> </span><span class="op">&lt;$&gt;</span><span> </span><span class="va">traverse</span><span> </span><span class="va">numberHAMTS</span><span> </span><span class="va">hs</span><span>
    </span><span class="va">tell</span><span> </span><span class="ot">[</span><span class="dt">HashArrayMappedTrieSpaciousGraphvizNode</span><span> </span><span class="va">i</span><span> </span><span class="va">numbered</span><span class="ot">]</span><span>
    </span><span class="va">pure</span><span> </span><span class="va">i</span><span>

</span><span class="va">nodeLinesHAMTS</span><span> </span><span class="ot">::</span><span> </span><span class="dt">HashArrayMappedTrieSpaciousGraphvizNode</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="ot">[</span><span class="dt">String</span><span class="ot">]</span><span>
</span><span class="va">nodeLinesHAMTS</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieSpaciousGraphvizLeafNode</span><span> </span><span class="va">i</span><span> </span><span class="va">h</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="kw">label</span><span> </span><span class="ot">=</span><span> </span><span class="va">intercalate</span><span> </span><span class="st">&quot;|&quot;</span><span> </span><span class="ot">[</span><span class="va">h</span><span class="ot">,</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="va">v</span><span class="ot">]</span><span>
    </span><span class="va">line</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span class="ot">)</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;[label=\&quot;&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">escape</span><span> </span><span class="kw">label</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;\&quot;]&quot;</span><span>
    </span><span class="kw">in</span><span> </span><span class="ot">[</span><span class="va">line</span><span class="ot">]</span><span>
</span><span class="va">nodeLinesHAMTS</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieSpaciousGraphvizNode</span><span> </span><span class="va">i</span><span> </span><span class="va">fs</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">indices</span><span> </span><span class="ot">=</span><span> </span><span class="va">Prelude.take</span><span> </span><span class="ot">(</span><span class="va">length</span><span> </span><span class="va">fs</span><span class="ot">)</span><span> </span><span class="ot">[</span><span class="dv">0</span><span class="ot">..</span><span class="ot">]</span><span>
    </span><span class="va">pairs</span><span> </span><span class="ot">=</span><span> </span><span class="va">filter</span><span> </span><span class="ot">(</span><span class="ot">\</span><span class="ot">(</span><span class="ot">_</span><span class="ot">,</span><span class="va">i</span><span class="ot">)</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">i</span><span> </span><span class="op">/=</span><span> </span><span class="dv">0</span><span class="ot">)</span><span> </span><span class="op">$</span><span> </span><span class="va">zip</span><span> </span><span class="va">indices</span><span> </span><span class="va">fs</span><span>
    </span><span class="va">edges</span><span> </span><span class="ot">=</span><span> </span><span class="va">flip</span><span> </span><span class="va">map</span><span> </span><span class="va">pairs</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="ot">(</span><span class="va">f</span><span class="ot">,</span><span class="va">t</span><span class="ot">)</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;:&quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;f&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">f</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; -&gt; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">t</span><span>
    </span><span class="va">fields</span><span> </span><span class="ot">=</span><span> </span><span class="va">flip</span><span> </span><span class="va">map</span><span> </span><span class="va">indices</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="va">ix</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;&lt;f&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">ix</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;&gt;&quot;</span><span>
    </span><span class="kw">label</span><span> </span><span class="ot">=</span><span> </span><span class="va">intercalate</span><span> </span><span class="st">&quot;|&quot;</span><span> </span><span class="va">fields</span><span>
    </span><span class="va">line</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span class="ot">)</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;[label=\&quot;&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">escape</span><span> </span><span class="kw">label</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;\&quot;]&quot;</span><span>
    </span><span class="kw">in</span><span> </span><span class="ot">(</span><span class="va">line</span><span class="ot">:</span><span class="va">edges</span><span class="ot">)</span><span>

</span><span class="va">dotFromHAMTS</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Show</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="dt">HashArrayMappedTrieSpacious</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">String</span><span>
</span><span class="va">dotFromHAMTS</span><span> </span><span class="ot">=</span><span> </span><span class="va">makeDot</span><span> </span><span class="op">.</span><span> </span><span class="va">makeDotLines</span><span class="op">.</span><span> </span><span class="va">concatMap</span><span> </span><span class="va">nodeLinesHAMTS</span><span> </span><span class="op">.</span><span> </span><span class="va">flip</span><span> </span><span class="va">evalState</span><span> </span><span class="dv">0</span><span> </span><span class="op">.</span><span> </span><span class="va">execWriterT</span><span> </span><span class="op">.</span><span> </span><span class="va">numberHAMTS</span></code></pre></div>
</details>
And inspect our handiwork:
<details>
<summary>
Hash Array Mapped Trie (Spacious)
</summary>
<div style="overflow: scroll">
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">dot</span><span> </span><span class="op">$</span><span> </span><span class="va">dotFromHAMTS</span><span> </span><span class="op">$</span><span> </span><span class="va">snd</span><span> </span><span class="op">$</span><span> </span><span class="va">fib&#39;</span><span> </span><span class="va">emptyHashArrayMappedTrieSpacious</span><span> </span><span class="dv">8</span></code></pre></div>
<svg width="3825pt" height="226pt" viewBox="0.00 0.00 3824.50 226.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 222)">
<polygon fill="white" stroke="none" points="-4,4 -4,-222 3820.5,-222 3820.5,4 -4,4"/>
<!-- n1 -->
<g id="node1" class="node">
<title>
n1
</title>
<polygon fill="none" stroke="black" points="0,-0.5 0,-36.5 357,-36.5 357,-0.5 0,-0.5"/>
<text text-anchor="middle" x="152.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000000</text>
<polyline fill="none" stroke="black" points="305,-0.5 305,-36.5"/>
<text text-anchor="middle" x="318" y="-14.8" font-family="Times,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="331,-0.5 331,-36.5"/>
<text text-anchor="middle" x="344" y="-14.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- n2 -->
<g id="node2" class="node">
<title>
n2
</title>
<polygon fill="none" stroke="black" points="429,-0.5 429,-36.5 786,-36.5 786,-0.5 429,-0.5"/>
<text text-anchor="middle" x="581.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000001</text>
<polyline fill="none" stroke="black" points="734,-0.5 734,-36.5"/>
<text text-anchor="middle" x="747" y="-14.8" font-family="Times,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="760,-0.5 760,-36.5"/>
<text text-anchor="middle" x="773" y="-14.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- n3 -->
<g id="node3" class="node">
<title>
n3
</title>
<polygon fill="none" stroke="black" points="858,-0.5 858,-36.5 1215,-36.5 1215,-0.5 858,-0.5"/>
<text text-anchor="middle" x="1010.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000010</text>
<polyline fill="none" stroke="black" points="1163,-0.5 1163,-36.5"/>
<text text-anchor="middle" x="1176" y="-14.8" font-family="Times,serif" font-size="14.00">2</text>
<polyline fill="none" stroke="black" points="1189,-0.5 1189,-36.5"/>
<text text-anchor="middle" x="1202" y="-14.8" font-family="Times,serif" font-size="14.00">2</text>
</g>
<!-- n4 -->
<g id="node4" class="node">
<title>
n4
</title>
<polygon fill="none" stroke="black" points="1287,-0.5 1287,-36.5 1644,-36.5 1644,-0.5 1287,-0.5"/>
<text text-anchor="middle" x="1439.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000011</text>
<polyline fill="none" stroke="black" points="1592,-0.5 1592,-36.5"/>
<text text-anchor="middle" x="1605" y="-14.8" font-family="Times,serif" font-size="14.00">3</text>
<polyline fill="none" stroke="black" points="1618,-0.5 1618,-36.5"/>
<text text-anchor="middle" x="1631" y="-14.8" font-family="Times,serif" font-size="14.00">3</text>
</g>
<!-- n5 -->
<g id="node5" class="node">
<title>
n5
</title>
<polygon fill="none" stroke="black" points="1716,-0.5 1716,-36.5 2073,-36.5 2073,-0.5 1716,-0.5"/>
<text text-anchor="middle" x="1868.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000100</text>
<polyline fill="none" stroke="black" points="2021,-0.5 2021,-36.5"/>
<text text-anchor="middle" x="2034" y="-14.8" font-family="Times,serif" font-size="14.00">4</text>
<polyline fill="none" stroke="black" points="2047,-0.5 2047,-36.5"/>
<text text-anchor="middle" x="2060" y="-14.8" font-family="Times,serif" font-size="14.00">5</text>
</g>
<!-- n6 -->
<g id="node6" class="node">
<title>
n6
</title>
<polygon fill="none" stroke="black" points="2145,-0.5 2145,-36.5 2502,-36.5 2502,-0.5 2145,-0.5"/>
<text text-anchor="middle" x="2297.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000101</text>
<polyline fill="none" stroke="black" points="2450,-0.5 2450,-36.5"/>
<text text-anchor="middle" x="2463" y="-14.8" font-family="Times,serif" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="2476,-0.5 2476,-36.5"/>
<text text-anchor="middle" x="2489" y="-14.8" font-family="Times,serif" font-size="14.00">8</text>
</g>
<!-- n7 -->
<g id="node7" class="node">
<title>
n7
</title>
<polygon fill="none" stroke="black" points="2574.5,-0.5 2574.5,-36.5 2940.5,-36.5 2940.5,-0.5 2574.5,-0.5"/>
<text text-anchor="middle" x="2727" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000110</text>
<polyline fill="none" stroke="black" points="2879.5,-0.5 2879.5,-36.5"/>
<text text-anchor="middle" x="2892.5" y="-14.8" font-family="Times,serif" font-size="14.00">6</text>
<polyline fill="none" stroke="black" points="2905.5,-0.5 2905.5,-36.5"/>
<text text-anchor="middle" x="2923" y="-14.8" font-family="Times,serif" font-size="14.00">13</text>
</g>
<!-- n8 -->
<g id="node8" class="node">
<title>
n8
</title>
<polygon fill="none" stroke="black" points="3012.5,-0.5 3012.5,-36.5 3378.5,-36.5 3378.5,-0.5 3012.5,-0.5"/>
<text text-anchor="middle" x="3165" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000111</text>
<polyline fill="none" stroke="black" points="3317.5,-0.5 3317.5,-36.5"/>
<text text-anchor="middle" x="3330.5" y="-14.8" font-family="Times,serif" font-size="14.00">7</text>
<polyline fill="none" stroke="black" points="3343.5,-0.5 3343.5,-36.5"/>
<text text-anchor="middle" x="3361" y="-14.8" font-family="Times,serif" font-size="14.00">21</text>
</g>
<!-- n9 -->
<g id="node9" class="node">
<title>
n9
</title>
<polygon fill="none" stroke="black" points="3450.5,-0.5 3450.5,-36.5 3816.5,-36.5 3816.5,-0.5 3450.5,-0.5"/>
<text text-anchor="middle" x="3603" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000001000</text>
<polyline fill="none" stroke="black" points="3755.5,-0.5 3755.5,-36.5"/>
<text text-anchor="middle" x="3768.5" y="-14.8" font-family="Times,serif" font-size="14.00">8</text>
<polyline fill="none" stroke="black" points="3781.5,-0.5 3781.5,-36.5"/>
<text text-anchor="middle" x="3799" y="-14.8" font-family="Times,serif" font-size="14.00">34</text>
</g>
<!-- n0 -->
<g id="node10" class="node">
<title>
n0
</title>
<polygon fill="none" stroke="black" points="1800.5,-181.5 1800.5,-217.5 2136.5,-217.5 2136.5,-181.5 1800.5,-181.5"/>
<text text-anchor="middle" x="1811" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1821.5,-181.5 1821.5,-217.5"/>
<text text-anchor="middle" x="1832" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1842.5,-181.5 1842.5,-217.5"/>
<text text-anchor="middle" x="1853" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1863.5,-181.5 1863.5,-217.5"/>
<text text-anchor="middle" x="1874" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1884.5,-181.5 1884.5,-217.5"/>
<text text-anchor="middle" x="1895" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1905.5,-181.5 1905.5,-217.5"/>
<text text-anchor="middle" x="1916" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1926.5,-181.5 1926.5,-217.5"/>
<text text-anchor="middle" x="1937" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1947.5,-181.5 1947.5,-217.5"/>
<text text-anchor="middle" x="1958" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1968.5,-181.5 1968.5,-217.5"/>
<text text-anchor="middle" x="1979" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1989.5,-181.5 1989.5,-217.5"/>
<text text-anchor="middle" x="2000" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="2010.5,-181.5 2010.5,-217.5"/>
<text text-anchor="middle" x="2021" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="2031.5,-181.5 2031.5,-217.5"/>
<text text-anchor="middle" x="2042" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="2052.5,-181.5 2052.5,-217.5"/>
<text text-anchor="middle" x="2063" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="2073.5,-181.5 2073.5,-217.5"/>
<text text-anchor="middle" x="2084" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="2094.5,-181.5 2094.5,-217.5"/>
<text text-anchor="middle" x="2105" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="2115.5,-181.5 2115.5,-217.5"/>
<text text-anchor="middle" x="2126" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n0&#45;&gt;n1 -->
<g id="edge1" class="edge">
<title>
n0:f0-&gt;n1
</title>
<path fill="none" stroke="black" d="M1799.5,-199.5C1799.5,-199.5 754.82,-83.5 342.77,-37.74"/>
<polygon fill="black" stroke="black" points="343.19,-34.27 332.87,-36.64 342.42,-41.22 343.19,-34.27"/>
</g>
<!-- n0&#45;&gt;n2 -->
<g id="edge2" class="edge">
<title>
n0:f1-&gt;n2
</title>
<path fill="none" stroke="black" d="M1831.5,-181C1831.5,-181 1068.63,-80.34 747.41,-37.96"/>
<polygon fill="black" stroke="black" points="748.05,-34.51 737.68,-36.68 747.13,-41.45 748.05,-34.51"/>
</g>
<!-- n0&#45;&gt;n3 -->
<g id="edge3" class="edge">
<title>
n0:f2-&gt;n3
</title>
<path fill="none" stroke="black" d="M1852.5,-181C1852.5,-181 1349.43,-81.43 1133.29,-38.66"/>
<polygon fill="black" stroke="black" points="1134.26,-35.28 1123.77,-36.77 1132.9,-42.15 1134.26,-35.28"/>
</g>
<!-- n0&#45;&gt;n4 -->
<g id="edge4" class="edge">
<title>
n0:f3-&gt;n4
</title>
<path fill="none" stroke="black" d="M1873.5,-181C1873.5,-181 1629.65,-84.48 1518.99,-40.67"/>
<polygon fill="black" stroke="black" points="1520.39,-37.46 1509.8,-37.04 1517.81,-43.97 1520.39,-37.46"/>
</g>
<!-- n0&#45;&gt;n5 -->
<g id="edge5" class="edge">
<title>
n0:f4-&gt;n5
</title>
<path fill="none" stroke="black" d="M1894.5,-181C1894.5,-181 1894.5,-94.16 1894.5,-47.83"/>
<polygon fill="black" stroke="black" points="1898,-47.99 1894.5,-37.99 1891,-47.99 1898,-47.99"/>
</g>
<!-- n0&#45;&gt;n6 -->
<g id="edge6" class="edge">
<title>
n0:f5-&gt;n6
</title>
<path fill="none" stroke="black" d="M1915.5,-181C1915.5,-181 2159.35,-84.48 2270.01,-40.67"/>
<polygon fill="black" stroke="black" points="2271.19,-43.97 2279.2,-37.04 2268.61,-37.46 2271.19,-43.97"/>
</g>
<!-- n0&#45;&gt;n7 -->
<g id="edge7" class="edge">
<title>
n0:f6-&gt;n7
</title>
<path fill="none" stroke="black" d="M1936.5,-181C1936.5,-181 2442.65,-81.43 2660.11,-38.66"/>
<polygon fill="black" stroke="black" points="2660.57,-42.13 2669.71,-36.77 2659.22,-35.27 2660.57,-42.13"/>
</g>
<!-- n0&#45;&gt;n8 -->
<g id="edge8" class="edge">
<title>
n0:f7-&gt;n8
</title>
<path fill="none" stroke="black" d="M1957.5,-181C1957.5,-181 2729.09,-80.34 3053.99,-37.96"/>
<polygon fill="black" stroke="black" points="3054.39,-41.44 3063.85,-36.67 3053.48,-34.5 3054.39,-41.44"/>
</g>
<!-- n0&#45;&gt;n9 -->
<g id="edge9" class="edge">
<title>
n0:f8-&gt;n9
</title>
<path fill="none" stroke="black" d="M1979.5,-181C1979.5,-181 3016.49,-79.75 3448.28,-37.59"/>
<polygon fill="black" stroke="black" points="3448.5,-41.08 3458.11,-36.63 3447.82,-34.11 3448.5,-41.08"/>
</g>
</g>
</svg>
</div>
</details>
<p>This is much better from a time-complexity perspective because the branching factor is higher. However, there’s one new issue we have introduced: it might not be so obvious in our small 8-element tree above, but every parent node now stores a 16-element array regardless of how many children it has. This is unnecessarily wasteful, and we can improve here.</p>
<p>Ideally we’d want to store an array that’s just big enough to fit the correct number of children, which we would resize as necessary when inserting or deleting elements. To accomplish this, we’ll paradoxically need to store another mapping between hash fragments and array indices. We’ll of course want this mapping to have minimal overhead, otherwise it wouldn’t end up saving much (or any) space.</p>
<p>This impressive technical feat is made possible by the magic of bitmaps! The general idea is that we store an additional bitmap that is the same size as the maximum length of the array (16 in our case), and then we do some more bit-twiddling that uses a hash fragment together with this bitmap to determine the correct index. The algorithm is:</p>
<ol type="1">
<li>Interpret the hash fragment as a number <code>n</code>.</li>
<li>If inserting, set the <code>n</code>th bit of the bitmap.</li>
<li>Mask off all bits <code>n</code> and above in the bitmap.</li>
<li>The <a href="https://vaibhavsagar.com/blog/2019/09/08/popcount/#hash-array-mapped-tries">population count</a> of the remaining bits is the index.</li>
</ol>
<p>Let’s try an example. We start with an empty bitmap:</p>
<pre><code>┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
  5   4   3   2   1   0   9   8   7   6   5   4   3   2   1   0
  1   1   1   1   1   1</code></pre>
<p>And we want to insert an element <code>x</code> with a hash fragment of <code>0b0100</code>. This is interpreted as <code>4</code>, so we set that in the bitmap:</p>
<pre><code>┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
  5   4   3   2   1   0   9   8   7   6   5   4   3   2   1   0
  1   1   1   1   1   1</code></pre>
<p>Then we mask off all bits <code>4</code> and above:</p>
<pre><code>┌───┬───┬───┬───┐
│ 0 │ 0 │ 0 │ 0 │
└───┴───┴───┴───┘
  3   2   1   0</code></pre>
<p>And the population count of this bitmap is <code>0</code>, which is our index.</p>
<p>The array looks like this:</p>
<pre><code>┌───┐
│ x │
└───┘
  0</code></pre>
<p>Let’s now insert an element <code>y</code> with a hash fragment of <code>0b1001</code>. This is interpreted as <code>9</code>, so we set that:</p>
<pre><code>┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
  5   4   3   2   1   0   9   8   7   6   5   4   3   2   1   0
  1   1   1   1   1   1</code></pre>
<p>Mask off all bits <code>9</code> and above:</p>
<pre><code>┌───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 0 │ 0 │ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │
└───┴───┴───┴───┴───┴───┴───┴───┴───┘
  8   7   6   5   4   3   2   1   0</code></pre>
<p>And the population count of this bitmap is <code>1</code>, which is our index.</p>
<p>The array now looks like this:</p>
<pre><code>┌───┬───┐
│ x │ y │
└───┴───┘
  0   1</code></pre>
<p>Finally, let’s insert an element <code>z</code> with a hash fragment of <code>0b0010</code>, or <code>2</code>:</p>
<pre><code>┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │ 1 │ 0 │ 1 │ 0 │ 0 │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
  5   4   3   2   1   0   9   8   7   6   5   4   3   2   1   0
  1   1   1   1   1   1</code></pre>
<p>We mask off bits <code>2</code> and above:</p>
<pre><code>┌───┬───┐
│ 0 │ 0 │
└───┴───┘
  1   0</code></pre>
<p>The population count of this bitmap is also <code>0</code>, which means we need to insert this new element at the beginning of the array and shift the other elements to the right:</p>
<pre><code>┌───┬───┬───┐
│ z │ x │ y │
└───┴───┴───┘
  0   1   2</code></pre>
<p>The updated bitmap means that looking up our other elements will still work correctly.</p>
<p>With that taken care of, we arrive at our final data structure:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieNone</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">HashArrayMappedTrieLeaf</span><span> </span><span class="dt">Hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">HashArrayMappedTrieNode</span><span> </span><span class="ot">(</span><span class="dt">Binary</span><span> </span><span class="dt">Word16</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="dt">Vector</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span class="ot">)</span><span class="ot">)</span><span>
    </span><span class="kw">deriving</span><span> </span><span class="ot">(</span><span class="dt">Eq</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span class="ot">)</span></code></pre></div>
<p>We modify our <code>insert</code> and <code>lookup</code> functions to use bitmaps as described above:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">insertHashArrayMappedTrie</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHashArrayMappedTrieHelper</span><span> </span><span class="dv">0</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="va">key</span><span>

</span><span class="va">insertHashArrayMappedTrieHelper</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHashArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="dt">HashArrayMappedTrieNone</span><span> </span><span class="ot">=</span><span>
    </span><span class="dt">HashArrayMappedTrieLeaf</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
</span><span class="va">insertHashArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="va">leaf</span><span class="ot">@</span><span class="ot">(</span><span class="dt">HashArrayMappedTrieLeaf</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span class="ot">)</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">hash</span><span> </span><span class="op">==</span><span> </span><span class="va">leafHash</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieLeaf</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">otherwise</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
        </span><span class="va">leafHashFragment</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="va">leafHash</span><span> </span><span class="ot">`</span><span class="va">shiftR</span><span class="ot">`</span><span> </span><span class="va">depth</span><span class="ot">)</span><span> </span><span class="op">.&amp;.</span><span> </span><span class="va">hashMask</span><span>
        </span><span class="va">leafBitmap</span><span> </span><span class="ot">=</span><span> </span><span class="va">bit</span><span> </span><span class="ot">(</span><span class="va">fromIntegral</span><span> </span><span class="va">leafHashFragment</span><span class="ot">)</span><span>
        </span><span class="va">leafInsertedNode</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieNode</span><span> </span><span class="va">leafBitmap</span><span> </span><span class="ot">(</span><span class="va">singleton</span><span> </span><span class="va">leaf</span><span class="ot">)</span><span>
        </span><span class="kw">in</span><span> </span><span class="va">insertHashArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="va">leafInsertedNode</span><span>
</span><span class="va">insertHashArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieNode</span><span> </span><span class="va">bitmap</span><span> </span><span class="va">children</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">hashFragment</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="ot">`</span><span class="va">shiftR</span><span class="ot">`</span><span> </span><span class="va">depth</span><span class="ot">)</span><span> </span><span class="op">.&amp;.</span><span> </span><span class="va">hashMask</span><span>
    </span><span class="va">elemBitmap</span><span> </span><span class="ot">=</span><span> </span><span class="va">bit</span><span> </span><span class="ot">(</span><span class="va">fromIntegral</span><span> </span><span class="va">hashFragment</span><span class="ot">)</span><span>
    </span><span class="va">index</span><span> </span><span class="ot">=</span><span> </span><span class="va">popCount</span><span> </span><span class="ot">(</span><span class="va">bitmap</span><span> </span><span class="op">.&amp;.</span><span> </span><span class="ot">(</span><span class="va">elemBitmap</span><span> </span><span class="op">-</span><span> </span><span class="dv">1</span><span class="ot">)</span><span class="ot">)</span><span>
    </span><span class="va">depth&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">depth</span><span> </span><span class="op">+</span><span> </span><span class="va">hashFragmentLength</span><span>
    </span><span class="kw">in</span><span> </span><span class="kw">if</span><span> </span><span class="va">elemBitmap</span><span> </span><span class="op">.&amp;.</span><span> </span><span class="va">bitmap</span><span> </span><span class="op">==</span><span> </span><span class="dv">0</span><span>
        </span><span class="kw">then</span><span> </span><span class="kw">let</span><span>
            </span><span class="va">leaf</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieLeaf</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span>
            </span><span class="va">bitmap&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">bitmap</span><span> </span><span class="op">.|.</span><span> </span><span class="va">elemBitmap</span><span>
            </span><span class="va">children&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">take</span><span> </span><span class="va">index</span><span> </span><span class="va">children</span><span> </span><span class="op">&lt;&gt;</span><span> </span><span class="va">singleton</span><span> </span><span class="va">leaf</span><span> </span><span class="op">&lt;&gt;</span><span> </span><span class="va">drop</span><span> </span><span class="va">index</span><span> </span><span class="va">children</span><span>
            </span><span class="kw">in</span><span> </span><span class="dt">HashArrayMappedTrieNode</span><span> </span><span class="va">bitmap&#39;</span><span> </span><span class="va">children&#39;</span><span>
        </span><span class="kw">else</span><span> </span><span class="kw">let</span><span>
            </span><span class="va">subtree</span><span> </span><span class="ot">=</span><span> </span><span class="va">children</span><span> </span><span class="op">!</span><span> </span><span class="va">index</span><span>
            </span><span class="va">subtree&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHashArrayMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="va">subtree</span><span>
            </span><span class="va">children&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">children</span><span> </span><span class="op">//</span><span> </span><span class="ot">[</span><span class="ot">(</span><span class="va">index</span><span class="ot">,</span><span> </span><span class="va">subtree&#39;</span><span class="ot">)</span><span class="ot">]</span><span>
            </span><span class="kw">in</span><span> </span><span class="dt">HashArrayMappedTrieNode</span><span> </span><span class="va">bitmap</span><span> </span><span class="va">children&#39;</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">lookupHashArrayMappedTrie</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Hashable</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Maybe</span><span> </span><span class="va">value</span><span>
</span><span class="va">lookupHashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="ot">=</span><span> </span><span class="va">lookupHashArrayMappedTrieHelper</span><span> </span><span class="dv">0</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="va">key</span><span class="ot">)</span><span> </span><span class="va">key</span><span>

</span><span class="va">lookupHashArrayMappedTrieHelper</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Hash</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">key</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">key</span><span> </span><span class="va">value</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Maybe</span><span> </span><span class="va">value</span><span>
</span><span class="va">lookupHashArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="dt">HashArrayMappedTrieNone</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Nothing</span><span>
</span><span class="va">lookupHashArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieLeaf</span><span> </span><span class="va">leafHash</span><span> </span><span class="va">leafKey</span><span> </span><span class="va">leafValue</span><span class="ot">)</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">hash</span><span> </span><span class="op">==</span><span> </span><span class="va">leafHash</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Just</span><span> </span><span class="va">leafValue</span><span>
    </span><span class="ot">|</span><span> </span><span class="va">otherwise</span><span> </span><span class="ot">=</span><span> </span><span class="dt">Nothing</span><span>
</span><span class="va">lookupHashArrayMappedTrieHelper</span><span> </span><span class="va">depth</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieNode</span><span> </span><span class="va">bitmap</span><span> </span><span class="va">children</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">hashFragment</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="va">hash</span><span> </span><span class="ot">`</span><span class="va">shiftR</span><span class="ot">`</span><span> </span><span class="va">depth</span><span class="ot">)</span><span> </span><span class="op">.&amp;.</span><span> </span><span class="va">hashMask</span><span>
    </span><span class="va">elemBitmap</span><span> </span><span class="ot">=</span><span> </span><span class="va">bit</span><span> </span><span class="ot">(</span><span class="va">fromIntegral</span><span> </span><span class="va">hashFragment</span><span class="ot">)</span><span>
    </span><span class="va">index</span><span> </span><span class="ot">=</span><span> </span><span class="va">popCount</span><span> </span><span class="ot">(</span><span class="va">bitmap</span><span> </span><span class="op">.&amp;.</span><span> </span><span class="ot">(</span><span class="va">elemBitmap</span><span> </span><span class="op">-</span><span> </span><span class="dv">1</span><span class="ot">)</span><span class="ot">)</span><span>
    </span><span class="va">depth&#39;</span><span> </span><span class="ot">=</span><span> </span><span class="va">depth</span><span> </span><span class="op">+</span><span> </span><span class="va">hashFragmentLength</span><span>
    </span><span class="kw">in</span><span> </span><span class="kw">if</span><span> </span><span class="va">elemBitmap</span><span> </span><span class="op">.&amp;.</span><span> </span><span class="va">bitmap</span><span> </span><span class="op">==</span><span> </span><span class="dv">0</span><span>
        </span><span class="kw">then</span><span> </span><span class="dt">Nothing</span><span>
        </span><span class="kw">else</span><span> </span><span class="va">lookupHashArrayMappedTrieHelper</span><span> </span><span class="va">depth&#39;</span><span> </span><span class="va">hash</span><span> </span><span class="va">key</span><span> </span><span class="ot">(</span><span class="va">children</span><span> </span><span class="op">!</span><span> </span><span class="va">index</span><span class="ot">)</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">emptyHashArrayMappedTrie</span><span> </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieNone</span></code></pre></div>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">instance</span><span> </span><span class="dt">Mapping</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="kw">where</span><span>
    </span><span class="va">empty</span><span> </span><span class="ot">=</span><span> </span><span class="va">emptyHashArrayMappedTrie</span><span>
    </span><span class="va">insert</span><span> </span><span class="ot">=</span><span> </span><span class="va">insertHashArrayMappedTrie</span><span>
    </span><span class="va">lookup</span><span> </span><span class="ot">=</span><span> </span><span class="va">lookupHashArrayMappedTrie</span></code></pre></div>
And one last time, we can render these:
<details>
<summary>
Hash Array Mapped Trie
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span><span> </span><span class="dt">HashArrayMappedTrieGraphvizNode</span><span>
    </span><span class="ot">=</span><span> </span><span class="dt">HashArrayMappedTrieGraphvizNode</span><span>
        </span><span class="ot">{</span><span> </span><span class="va">hashArrayMappedTrieGraphvizNodeId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashArrayMappedTrieGraphvizBitmap</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashArrayMappedTrieGraphvizFields</span><span> </span><span class="ot">::</span><span> </span><span class="ot">[</span><span class="dt">Int</span><span class="ot">]</span><span>
        </span><span class="ot">}</span><span>
    </span><span class="ot">|</span><span> </span><span class="dt">HashArrayMappedTrieGraphvizLeafNode</span><span>
        </span><span class="ot">{</span><span> </span><span class="va">hashArrayMappedTrieGraphvizLeafNodeId</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Int</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashArrayMappedTrieGraphvizLeafHash</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashArrayMappedTrieGraphvizLeafKey</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">,</span><span> </span><span class="va">hashArrayMappedTrieGraphvizLeafNodeValue</span><span> </span><span class="ot">::</span><span> </span><span class="dt">String</span><span>
        </span><span class="ot">}</span><span>
    </span><span class="kw">deriving</span><span> </span><span class="ot">(</span><span class="dt">Eq</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span class="ot">)</span><span>

</span><span class="va">numberHAMT</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Show</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">WriterT</span><span> </span><span class="ot">[</span><span class="dt">HashArrayMappedTrieGraphvizNode</span><span class="ot">]</span><span> </span><span class="ot">(</span><span class="dt">State</span><span> </span><span class="dt">Int</span><span class="ot">)</span><span> </span><span class="dt">Int</span><span>
</span><span class="va">numberHAMT</span><span> </span><span class="dt">HashArrayMappedTrieNone</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">tell</span><span> </span><span class="va">mempty</span><span>
    </span><span class="va">pure</span><span> </span><span class="dv">0</span><span>
</span><span class="va">numberHAMT</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieLeaf</span><span> </span><span class="va">h</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">i</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">lift</span><span> </span><span class="va">getFreshId</span><span>
    </span><span class="va">tell</span><span> </span><span class="ot">[</span><span class="dt">HashArrayMappedTrieGraphvizLeafNode</span><span> </span><span class="va">i</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">h</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">k</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">v</span><span class="ot">)</span><span class="ot">]</span><span>
    </span><span class="va">pure</span><span> </span><span class="va">i</span><span>
</span><span class="va">numberHAMT</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieNode</span><span> </span><span class="va">b</span><span> </span><span class="va">hs</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">i</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">lift</span><span> </span><span class="va">getFreshId</span><span>
    </span><span class="va">numbered</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">Vector.toList</span><span> </span><span class="op">&lt;$&gt;</span><span> </span><span class="va">traverse</span><span> </span><span class="va">numberHAMT</span><span> </span><span class="va">hs</span><span>
    </span><span class="va">tell</span><span> </span><span class="ot">[</span><span class="dt">HashArrayMappedTrieGraphvizNode</span><span> </span><span class="va">i</span><span> </span><span class="ot">(</span><span class="va">show</span><span> </span><span class="va">b</span><span class="ot">)</span><span> </span><span class="va">numbered</span><span class="ot">]</span><span>
    </span><span class="va">pure</span><span> </span><span class="va">i</span><span>

</span><span class="va">nodeLinesHAMT</span><span> </span><span class="ot">::</span><span> </span><span class="dt">HashArrayMappedTrieGraphvizNode</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="ot">[</span><span class="dt">String</span><span class="ot">]</span><span>
</span><span class="va">nodeLinesHAMT</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieGraphvizLeafNode</span><span> </span><span class="va">i</span><span> </span><span class="va">h</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="kw">label</span><span> </span><span class="ot">=</span><span> </span><span class="va">intercalate</span><span> </span><span class="st">&quot;|&quot;</span><span> </span><span class="ot">[</span><span class="va">h</span><span class="ot">,</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="va">v</span><span class="ot">]</span><span>
    </span><span class="va">line</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span class="ot">)</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;[label=\&quot;&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">escape</span><span> </span><span class="kw">label</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;\&quot;]&quot;</span><span>
    </span><span class="kw">in</span><span> </span><span class="ot">[</span><span class="va">line</span><span class="ot">]</span><span>
</span><span class="va">nodeLinesHAMT</span><span> </span><span class="ot">(</span><span class="dt">HashArrayMappedTrieGraphvizNode</span><span> </span><span class="va">i</span><span> </span><span class="va">b</span><span> </span><span class="va">fs</span><span class="ot">)</span><span> </span><span class="ot">=</span><span> </span><span class="kw">let</span><span>
    </span><span class="va">indices</span><span> </span><span class="ot">=</span><span> </span><span class="va">Prelude.take</span><span> </span><span class="ot">(</span><span class="va">length</span><span> </span><span class="va">fs</span><span class="ot">)</span><span> </span><span class="ot">[</span><span class="dv">0</span><span class="ot">..</span><span class="ot">]</span><span>
    </span><span class="va">pairs</span><span> </span><span class="ot">=</span><span> </span><span class="va">zip</span><span> </span><span class="va">indices</span><span> </span><span class="va">fs</span><span>
    </span><span class="va">edges</span><span> </span><span class="ot">=</span><span> </span><span class="va">flip</span><span> </span><span class="va">map</span><span> </span><span class="va">pairs</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="ot">(</span><span class="va">f</span><span class="ot">,</span><span class="va">t</span><span class="ot">)</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;:&quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;f&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">f</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; -&gt; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">t</span><span>
    </span><span class="va">fields</span><span> </span><span class="ot">=</span><span> </span><span class="va">flip</span><span> </span><span class="va">map</span><span> </span><span class="va">indices</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="va">ix</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;&lt;f&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">ix</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;&gt;&quot;</span><span>
    </span><span class="kw">label</span><span> </span><span class="ot">=</span><span> </span><span class="va">intercalate</span><span> </span><span class="st">&quot;|&quot;</span><span> </span><span class="op">$</span><span> </span><span class="va">b</span><span class="ot">:</span><span class="va">fields</span><span>
    </span><span class="va">line</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="st">&quot;n&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">show</span><span> </span><span class="va">i</span><span class="ot">)</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot; &quot;</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;[label=\&quot;&quot;</span><span> </span><span class="op">++</span><span> </span><span class="va">escape</span><span> </span><span class="kw">label</span><span> </span><span class="op">++</span><span> </span><span class="st">&quot;\&quot;]&quot;</span><span>
    </span><span class="kw">in</span><span> </span><span class="ot">(</span><span class="va">line</span><span class="ot">:</span><span class="va">edges</span><span class="ot">)</span><span>

</span><span class="va">dotFromHAMT</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Show</span><span> </span><span class="va">k</span><span class="ot">,</span><span> </span><span class="dt">Show</span><span> </span><span class="va">v</span><span class="ot">)</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="dt">HashArrayMappedTrie</span><span> </span><span class="va">k</span><span> </span><span class="va">v</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">String</span><span>
</span><span class="va">dotFromHAMT</span><span> </span><span class="ot">=</span><span> </span><span class="va">makeDot</span><span> </span><span class="op">.</span><span> </span><span class="va">makeDotLines</span><span class="op">.</span><span> </span><span class="va">concatMap</span><span> </span><span class="va">nodeLinesHAMT</span><span> </span><span class="op">.</span><span> </span><span class="va">flip</span><span> </span><span class="va">evalState</span><span> </span><span class="dv">0</span><span> </span><span class="op">.</span><span> </span><span class="va">execWriterT</span><span> </span><span class="op">.</span><span> </span><span class="va">numberHAMT</span></code></pre></div>
</details>
<details>
<summary>
Hash Array Mapped Trie
</summary>
<div style="overflow: scroll">
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">dot</span><span> </span><span class="op">$</span><span> </span><span class="va">dotFromHAMT</span><span> </span><span class="op">$</span><span> </span><span class="va">snd</span><span> </span><span class="op">$</span><span> </span><span class="va">fib&#39;</span><span> </span><span class="va">emptyHashArrayMappedTrie</span><span> </span><span class="dv">8</span></code></pre></div>
<svg width="3825pt" height="226pt" viewBox="0.00 0.00 3824.50 226.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 222)">
<polygon fill="white" stroke="none" points="-4,4 -4,-222 3820.5,-222 3820.5,4 -4,4"/>
<!-- n1 -->
<g id="node1" class="node">
<title>
n1
</title>
<polygon fill="none" stroke="black" points="0,-0.5 0,-36.5 357,-36.5 357,-0.5 0,-0.5"/>
<text text-anchor="middle" x="152.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000000</text>
<polyline fill="none" stroke="black" points="305,-0.5 305,-36.5"/>
<text text-anchor="middle" x="318" y="-14.8" font-family="Times,serif" font-size="14.00">0</text>
<polyline fill="none" stroke="black" points="331,-0.5 331,-36.5"/>
<text text-anchor="middle" x="344" y="-14.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- n2 -->
<g id="node2" class="node">
<title>
n2
</title>
<polygon fill="none" stroke="black" points="429,-0.5 429,-36.5 786,-36.5 786,-0.5 429,-0.5"/>
<text text-anchor="middle" x="581.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000001</text>
<polyline fill="none" stroke="black" points="734,-0.5 734,-36.5"/>
<text text-anchor="middle" x="747" y="-14.8" font-family="Times,serif" font-size="14.00">1</text>
<polyline fill="none" stroke="black" points="760,-0.5 760,-36.5"/>
<text text-anchor="middle" x="773" y="-14.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- n3 -->
<g id="node3" class="node">
<title>
n3
</title>
<polygon fill="none" stroke="black" points="858,-0.5 858,-36.5 1215,-36.5 1215,-0.5 858,-0.5"/>
<text text-anchor="middle" x="1010.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000010</text>
<polyline fill="none" stroke="black" points="1163,-0.5 1163,-36.5"/>
<text text-anchor="middle" x="1176" y="-14.8" font-family="Times,serif" font-size="14.00">2</text>
<polyline fill="none" stroke="black" points="1189,-0.5 1189,-36.5"/>
<text text-anchor="middle" x="1202" y="-14.8" font-family="Times,serif" font-size="14.00">2</text>
</g>
<!-- n4 -->
<g id="node4" class="node">
<title>
n4
</title>
<polygon fill="none" stroke="black" points="1287,-0.5 1287,-36.5 1644,-36.5 1644,-0.5 1287,-0.5"/>
<text text-anchor="middle" x="1439.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000011</text>
<polyline fill="none" stroke="black" points="1592,-0.5 1592,-36.5"/>
<text text-anchor="middle" x="1605" y="-14.8" font-family="Times,serif" font-size="14.00">3</text>
<polyline fill="none" stroke="black" points="1618,-0.5 1618,-36.5"/>
<text text-anchor="middle" x="1631" y="-14.8" font-family="Times,serif" font-size="14.00">3</text>
</g>
<!-- n5 -->
<g id="node5" class="node">
<title>
n5
</title>
<polygon fill="none" stroke="black" points="1716,-0.5 1716,-36.5 2073,-36.5 2073,-0.5 1716,-0.5"/>
<text text-anchor="middle" x="1868.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000100</text>
<polyline fill="none" stroke="black" points="2021,-0.5 2021,-36.5"/>
<text text-anchor="middle" x="2034" y="-14.8" font-family="Times,serif" font-size="14.00">4</text>
<polyline fill="none" stroke="black" points="2047,-0.5 2047,-36.5"/>
<text text-anchor="middle" x="2060" y="-14.8" font-family="Times,serif" font-size="14.00">5</text>
</g>
<!-- n6 -->
<g id="node6" class="node">
<title>
n6
</title>
<polygon fill="none" stroke="black" points="2145,-0.5 2145,-36.5 2502,-36.5 2502,-0.5 2145,-0.5"/>
<text text-anchor="middle" x="2297.5" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000101</text>
<polyline fill="none" stroke="black" points="2450,-0.5 2450,-36.5"/>
<text text-anchor="middle" x="2463" y="-14.8" font-family="Times,serif" font-size="14.00">5</text>
<polyline fill="none" stroke="black" points="2476,-0.5 2476,-36.5"/>
<text text-anchor="middle" x="2489" y="-14.8" font-family="Times,serif" font-size="14.00">8</text>
</g>
<!-- n7 -->
<g id="node7" class="node">
<title>
n7
</title>
<polygon fill="none" stroke="black" points="2574.5,-0.5 2574.5,-36.5 2940.5,-36.5 2940.5,-0.5 2574.5,-0.5"/>
<text text-anchor="middle" x="2727" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000110</text>
<polyline fill="none" stroke="black" points="2879.5,-0.5 2879.5,-36.5"/>
<text text-anchor="middle" x="2892.5" y="-14.8" font-family="Times,serif" font-size="14.00">6</text>
<polyline fill="none" stroke="black" points="2905.5,-0.5 2905.5,-36.5"/>
<text text-anchor="middle" x="2923" y="-14.8" font-family="Times,serif" font-size="14.00">13</text>
</g>
<!-- n8 -->
<g id="node8" class="node">
<title>
n8
</title>
<polygon fill="none" stroke="black" points="3012.5,-0.5 3012.5,-36.5 3378.5,-36.5 3378.5,-0.5 3012.5,-0.5"/>
<text text-anchor="middle" x="3165" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000000111</text>
<polyline fill="none" stroke="black" points="3317.5,-0.5 3317.5,-36.5"/>
<text text-anchor="middle" x="3330.5" y="-14.8" font-family="Times,serif" font-size="14.00">7</text>
<polyline fill="none" stroke="black" points="3343.5,-0.5 3343.5,-36.5"/>
<text text-anchor="middle" x="3361" y="-14.8" font-family="Times,serif" font-size="14.00">21</text>
</g>
<!-- n9 -->
<g id="node9" class="node">
<title>
n9
</title>
<polygon fill="none" stroke="black" points="3450.5,-0.5 3450.5,-36.5 3816.5,-36.5 3816.5,-0.5 3450.5,-0.5"/>
<text text-anchor="middle" x="3603" y="-14.8" font-family="Times,serif" font-size="14.00">00000000000000000000000000001000</text>
<polyline fill="none" stroke="black" points="3755.5,-0.5 3755.5,-36.5"/>
<text text-anchor="middle" x="3768.5" y="-14.8" font-family="Times,serif" font-size="14.00">8</text>
<polyline fill="none" stroke="black" points="3781.5,-0.5 3781.5,-36.5"/>
<text text-anchor="middle" x="3799" y="-14.8" font-family="Times,serif" font-size="14.00">34</text>
</g>
<!-- n0 -->
<g id="node10" class="node">
<title>
n0
</title>
<polygon fill="none" stroke="black" points="1638.5,-181.5 1638.5,-217.5 1988.5,-217.5 1988.5,-181.5 1638.5,-181.5"/>
<text text-anchor="middle" x="1719" y="-195.8" font-family="Times,serif" font-size="14.00">0000000111111111</text>
<polyline fill="none" stroke="black" points="1799.5,-181.5 1799.5,-217.5"/>
<text text-anchor="middle" x="1810" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1820.5,-181.5 1820.5,-217.5"/>
<text text-anchor="middle" x="1831" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1841.5,-181.5 1841.5,-217.5"/>
<text text-anchor="middle" x="1852" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1862.5,-181.5 1862.5,-217.5"/>
<text text-anchor="middle" x="1873" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1883.5,-181.5 1883.5,-217.5"/>
<text text-anchor="middle" x="1894" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1904.5,-181.5 1904.5,-217.5"/>
<text text-anchor="middle" x="1915" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1925.5,-181.5 1925.5,-217.5"/>
<text text-anchor="middle" x="1936" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1946.5,-181.5 1946.5,-217.5"/>
<text text-anchor="middle" x="1957" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
<polyline fill="none" stroke="black" points="1967.5,-181.5 1967.5,-217.5"/>
<text text-anchor="middle" x="1978" y="-195.8" font-family="Times,serif" font-size="14.00"> </text>
</g>
<!-- n0&#45;&gt;n1 -->
<g id="edge1" class="edge">
<title>
n0:f0-&gt;n1
</title>
<path fill="none" stroke="black" d="M1809.5,-181C1809.5,-181 786.93,-79.75 361.15,-37.59"/>
<polygon fill="black" stroke="black" points="361.77,-34.13 351.47,-36.63 361.08,-41.1 361.77,-34.13"/>
</g>
<!-- n0&#45;&gt;n2 -->
<g id="edge2" class="edge">
<title>
n0:f1-&gt;n2
</title>
<path fill="none" stroke="black" d="M1831.5,-181C1831.5,-181 1068.63,-80.34 747.41,-37.96"/>
<polygon fill="black" stroke="black" points="748.05,-34.51 737.68,-36.68 747.13,-41.45 748.05,-34.51"/>
</g>
<!-- n0&#45;&gt;n3 -->
<g id="edge3" class="edge">
<title>
n0:f2-&gt;n3
</title>
<path fill="none" stroke="black" d="M1852.5,-181C1852.5,-181 1349.43,-81.43 1133.29,-38.66"/>
<polygon fill="black" stroke="black" points="1134.26,-35.28 1123.77,-36.77 1132.9,-42.15 1134.26,-35.28"/>
</g>
<!-- n0&#45;&gt;n4 -->
<g id="edge4" class="edge">
<title>
n0:f3-&gt;n4
</title>
<path fill="none" stroke="black" d="M1873.5,-181C1873.5,-181 1629.65,-84.48 1518.99,-40.67"/>
<polygon fill="black" stroke="black" points="1520.39,-37.46 1509.8,-37.04 1517.81,-43.97 1520.39,-37.46"/>
</g>
<!-- n0&#45;&gt;n5 -->
<g id="edge5" class="edge">
<title>
n0:f4-&gt;n5
</title>
<path fill="none" stroke="black" d="M1894.5,-181C1894.5,-181 1894.5,-94.16 1894.5,-47.83"/>
<polygon fill="black" stroke="black" points="1898,-47.99 1894.5,-37.99 1891,-47.99 1898,-47.99"/>
</g>
<!-- n0&#45;&gt;n6 -->
<g id="edge6" class="edge">
<title>
n0:f5-&gt;n6
</title>
<path fill="none" stroke="black" d="M1915.5,-181C1915.5,-181 2159.35,-84.48 2270.01,-40.67"/>
<polygon fill="black" stroke="black" points="2271.19,-43.97 2279.2,-37.04 2268.61,-37.46 2271.19,-43.97"/>
</g>
<!-- n0&#45;&gt;n7 -->
<g id="edge7" class="edge">
<title>
n0:f6-&gt;n7
</title>
<path fill="none" stroke="black" d="M1936.5,-181C1936.5,-181 2442.65,-81.43 2660.11,-38.66"/>
<polygon fill="black" stroke="black" points="2660.57,-42.13 2669.71,-36.77 2659.22,-35.27 2660.57,-42.13"/>
</g>
<!-- n0&#45;&gt;n8 -->
<g id="edge8" class="edge">
<title>
n0:f7-&gt;n8
</title>
<path fill="none" stroke="black" d="M1957.5,-181C1957.5,-181 2729.09,-80.34 3053.99,-37.96"/>
<polygon fill="black" stroke="black" points="3054.39,-41.44 3063.85,-36.67 3053.48,-34.5 3054.39,-41.44"/>
</g>
<!-- n0&#45;&gt;n9 -->
<g id="edge9" class="edge">
<title>
n0:f8-&gt;n9
</title>
<path fill="none" stroke="black" d="M1989.5,-199.5C1989.5,-199.5 3049.52,-83.44 3467.21,-37.71"/>
<polygon fill="black" stroke="black" points="3467.4,-41.21 3476.96,-36.64 3466.64,-34.25 3467.4,-41.21"/>
</g>
</g>
</svg>
</div>
</details>
<p>And we’re done! Here are a few more things to explore that I didn’t have space to cover here:</p>
<ul>
<li><p>These data structures don’t handle collisions, but these could be added with a <code>Collision</code> node that stores a list of key-value pairs where the keys all share the same hash.</p></li>
<li><p>In the case where a node’s bitmap is full, we don’t need to do most of the bit-twiddling above, and in practice most implementations also have a special <code>Full</code> node for this purpose.</p></li>
<li><p>I’ve only looked at <code>insert</code> and <code>lookup</code>, but there are some intricacies to implementing <code>delete</code> etc.</p></li>
<li><p>All these data structures are persistent, by virtue of them being implemented with immutable vectors. The original paper uses mutable vectors and is not persistent.</p></li>
<li><p>Even in the case where we want a persistent data structure, we might want to do a series of updates to a “thawed” version of the structure and then “freeze” it afterwards like we do with vectors in Haskell. I don’t know of an implementation that has this capability.</p></li>
</ul>
</summary>
</entry>
<entry>
    <title>Using `ghc-syntax-highlighter` with Hakyll</title>
    <link href="https://vaibhavsagar.com/blog/2023/01/29/ghc-syntax-hakyll/" />
    <id>https://vaibhavsagar.com/blog/2023/01/29/ghc-syntax-hakyll/index.html</id>
    <published>2023-01-29</published>
    <updated>2023-01-29T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on 29 January 2023
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;haskell&#39;." href="/blog/tags/haskell/index.html" rel="tag">haskell</a>, <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>
    
</div>

<p>In 2018, <a href="https://markkarpov.com/post/announcing-ghc-syntax-highlighter.html">Mark Karpov announced
<code>ghc-syntax-highlighter</code></a>,
a project which uses GHC’s own lexer to tokenise Haskell source code for the
best possible syntax highlighting. I thought this was extremely cool, and
really wanted to use it for this blog. Unfortunately, this is what the post had
to say about <code>pandoc</code>, which Hakyll uses to process Markdown:</p>
<blockquote>
<p><a href="https://hackage.haskell.org/package/skylighting"><code>skylighting</code></a> is what
Pandoc uses btw. And from what I can tell it’s hardcoded to use only that
library for highlighting, so some creativity may be necessary to get it work.</p>
</blockquote>
<p>I briefly looked into this and reached the same conclusion (and as of this
writing <a href="https://github.com/jgm/pandoc/blob/5f31a01d77f5fea46e2deca51165d5af8fc99677/src/Text/Pandoc/Highlighting.hs#L76-L106">it is still the
case</a>)
so, as a deeply uncreative individual, I sighed deeply and resigned myself to never knowing this particular joy.</p>
<p>Until, just a few days ago, I read <a href="https://tony-zorman.com/posts/2023-01-21-pygmentising-hakyll.html">this lovely blog post by Tony Zorman about
customising Hakyll’s syntax
highlighting</a>
which included this gem of a sentence in the very first paragraph:</p>
<blockquote>
<p>Using <code>pygmentize</code> as an example, I will show you how you can swap out
pandoc’s native syntax highlighting with pretty much any third party tool that
can output HTML.</p>
</blockquote>
<p>And in fact this is an accurate description of what follows. This sounds like
exactly what I want to do, and between this and Mark’s
<a href="https://hackage.haskell.org/package/mmark-ext"><code>mmark-ext</code></a> (which <a href="https://hackage.haskell.org/package/mmark-ext-0.2.1.5/docs/Text-MMark-Extension-GhcSyntaxHighlighter.html">implements
<code>ghc-syntax-highlighter</code> support as an extension for
<code>mmark</code></a>)
I was able to successfully follow the instructions to get
<code>ghc-syntax-highlighter</code> working with my blog. Let me walk you through what
I did.</p>
<p>Here are the language extensions I will be using:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="pp">{-# LANGUAGE LambdaCase        #-}</span><span>
</span><span class="pp">{-# LANGUAGE OverloadedStrings #-}</span><span>
</span><span class="pp">{-# LANGUAGE ViewPatterns      #-}</span></code></pre></div>
<p>and these additional imports:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import</span><span>           </span><span class="dt">GHC.SyntaxHighlighter</span><span> </span><span class="ot">(</span><span class="dt">Token</span><span class="ot">(</span><span class="ot">..</span><span class="ot">)</span><span class="ot">,</span><span> </span><span class="va">tokenizeHaskell</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span>           </span><span class="dt">Text.Blaze.Html.Renderer.Text</span><span> </span><span class="ot">(</span><span class="va">renderHtml</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span>           </span><span class="dt">Text.Pandoc.Definition</span><span> </span><span class="ot">(</span><span class="dt">Block</span><span> </span><span class="ot">(</span><span class="dt">CodeBlock</span><span class="ot">,</span><span> </span><span class="dt">RawBlock</span><span class="ot">)</span><span class="ot">,</span><span> </span><span class="dt">Pandoc</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span>           </span><span class="dt">Text.Pandoc.Walk</span><span> </span><span class="ot">(</span><span class="va">walk</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="kw">qualified</span><span> </span><span class="dt">Text.Blaze.Html5</span><span> </span><span class="kw">as</span><span> </span><span class="dt">H</span><span>
</span><span class="kw">import</span><span> </span><span class="kw">qualified</span><span> </span><span class="dt">Text.Blaze.Html5.Attributes</span><span> </span><span class="kw">as</span><span> </span><span class="dt">A</span></code></pre></div>
<p>I chose to use <a href="https://hackage.haskell.org/package/blaze-html"><code>blaze-html</code></a>
since it is already a transitive dependency of <code>pandoc</code> and using it has no
impact on our dependency tree.</p>
<p>Tony uses <code>walkM</code> since an external program (<code>pygmentize</code>) is involved, but
since we are working with pure Haskell code we can get away with just <code>walk</code>:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">ghcSyntaxHighlight</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Pandoc</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Pandoc</span><span>
</span><span class="va">ghcSyntaxHighlight</span><span> </span><span class="ot">=</span><span> </span><span class="va">walk</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="ot">case</span><span>
    </span><span class="dt">CodeBlock</span><span> </span><span class="ot">(</span><span class="ot">_</span><span class="ot">,</span><span> </span><span class="ot">(</span><span class="va">isHaskell</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">True</span><span class="ot">)</span><span class="ot">:</span><span class="ot">_</span><span class="ot">,</span><span> </span><span class="ot">_</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="va">tokenizeHaskell</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">Just</span><span> </span><span class="va">tokens</span><span class="ot">)</span><span> </span><span class="ot">-&gt;</span><span>
        </span><span class="dt">RawBlock</span><span> </span><span class="st">&quot;html&quot;</span><span> </span><span class="op">.</span><span> </span><span class="va">L.toStrict</span><span> </span><span class="op">.</span><span> </span><span class="va">renderHtml</span><span> </span><span class="op">$</span><span> </span><span class="va">formatHaskellTokens</span><span> </span><span class="va">tokens</span><span>
    </span><span class="va">block</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">block</span><span>
    </span><span class="kw">where</span><span> </span><span class="va">isHaskell</span><span> </span><span class="ot">=</span><span> </span><span class="ot">(</span><span class="op">==</span><span> </span><span class="st">&quot;haskell&quot;</span><span class="ot">)</span></code></pre></div>
<p>This only matches Haskell code blocks which <code>tokenizeHaskell</code> is able to
successfully tokenise and otherwise falls back on existing <code>pandoc</code> behaviour.</p>
<p><code>formatHaskellTokens</code> generates markup very similarly to what <code>pandoc</code> already
does:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">formatHaskellTokens</span><span> </span><span class="ot">::</span><span> </span><span class="ot">[</span><span class="ot">(</span><span class="dt">Token</span><span class="ot">,</span><span> </span><span class="dt">T.Text</span><span class="ot">)</span><span class="ot">]</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">H.Html</span><span>
</span><span class="va">formatHaskellTokens</span><span> </span><span class="va">tokens</span><span> </span><span class="ot">=</span><span>
    </span><span class="va">H.div</span><span> </span><span class="op">H.!</span><span> </span><span class="va">A.class_</span><span> </span><span class="st">&quot;sourceCode&quot;</span><span> </span><span class="op">$</span><span>
        </span><span class="va">H.pre</span><span> </span><span class="op">H.!</span><span> </span><span class="va">A.class_</span><span> </span><span class="st">&quot;sourceCode haskell&quot;</span><span> </span><span class="op">$</span><span>
            </span><span class="va">H.code</span><span> </span><span class="op">H.!</span><span> </span><span class="va">A.class_</span><span> </span><span class="st">&quot;sourceCode haskell&quot;</span><span> </span><span class="op">$</span><span>
                </span><span class="va">mapM_</span><span> </span><span class="va">tokenToHtml</span><span> </span><span class="va">tokens</span></code></pre></div>
<p><code>tokenizeHaskell</code> produces a list of pairs of the token type (<code>KeywordToken</code>,
<code>VariableToken</code>, etc.) and the matched text, and the <code>tokenToHtml</code> (<a href="https://hackage.haskell.org/package/mmark-ext-0.2.1.5/docs/src/Text.MMark.Extension.GhcSyntaxHighlighter.html#tokenToHtml">adapted
from
<code>mmark-ext</code></a>)
function creates a <code>span</code> element with the appropriate class name for our CSS
to style:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">tokenToHtml</span><span> </span><span class="ot">::</span><span> </span><span class="ot">(</span><span class="dt">Token</span><span class="ot">,</span><span> </span><span class="dt">T.Text</span><span class="ot">)</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">H.Html</span><span>
</span><span class="va">tokenToHtml</span><span> </span><span class="ot">(</span><span class="va">tokenClass</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">className</span><span class="ot">,</span><span> </span><span class="va">text</span><span class="ot">)</span><span> </span><span class="ot">=</span><span>
    </span><span class="va">H.span</span><span> </span><span class="op">H.!?</span><span> </span><span class="ot">(</span><span class="va">not</span><span> </span><span class="op">$</span><span> </span><span class="va">T.null</span><span> </span><span class="va">className</span><span class="ot">,</span><span> </span><span class="va">A.class_</span><span> </span><span class="ot">(</span><span class="va">H.toValue</span><span> </span><span class="va">className</span><span class="ot">)</span><span class="ot">)</span><span> </span><span class="op">$</span><span>
        </span><span class="va">H.toHtml</span><span> </span><span class="va">text</span></code></pre></div>
<p><code>tokenClass</code> (<a href="https://hackage.haskell.org/package/mmark-ext-0.2.1.5/docs/src/Text.MMark.Extension.GhcSyntaxHighlighter.html#tokenClass">also adapted from
<code>mmark-ext</code></a>)
outputs the appropriate class name for each token, and I made only minor
changes for styling purposes:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">tokenClass</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Token</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">T.Text</span><span>
</span><span class="va">tokenClass</span><span> </span><span class="ot">=</span><span> </span><span class="ot">\</span><span class="ot">case</span><span>
    </span><span class="dt">KeywordTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;kw&quot;</span><span>
    </span><span class="dt">PragmaTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;pp&quot;</span><span> </span><span class="co">-- Preprocessor</span><span>
    </span><span class="dt">SymbolTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;ot&quot;</span><span> </span><span class="co">-- Other</span><span>
    </span><span class="dt">VariableTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;va&quot;</span><span>
    </span><span class="dt">ConstructorTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;dt&quot;</span><span> </span><span class="co">-- DataType</span><span>
    </span><span class="dt">OperatorTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;op&quot;</span><span>
    </span><span class="dt">CharTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;ch&quot;</span><span>
    </span><span class="dt">StringTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;st&quot;</span><span>
    </span><span class="dt">IntegerTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;dv&quot;</span><span> </span><span class="co">-- DecVal</span><span>
    </span><span class="dt">RationalTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;dv&quot;</span><span> </span><span class="co">-- DecVal</span><span>
    </span><span class="dt">CommentTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;co&quot;</span><span>
    </span><span class="dt">SpaceTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;&quot;</span><span>
    </span><span class="dt">OtherTok</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="st">&quot;ot&quot;</span></code></pre></div>
<p>Finally we have to actually use <code>ghcSyntaxHighlight</code>, for which we define
a replacement for <code>pandocCompiler</code> called (imaginatively)
<code>customPandocCompiler</code> and use it everywhere:</p>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">customPandocCompiler</span><span> </span><span class="ot">::</span><span> </span><span class="dt">Compiler</span><span> </span><span class="ot">(</span><span class="dt">Item</span><span> </span><span class="dt">String</span><span class="ot">)</span><span>
</span><span class="va">customPandocCompiler</span><span> </span><span class="ot">=</span><span>
    </span><span class="va">pandocCompilerWithTransform</span><span>
        </span><span class="va">defaultHakyllReaderOptions</span><span>
        </span><span class="va">defaultHakyllWriterOptions</span><span>
        </span><span class="va">ghcSyntaxHighlight</span></code></pre></div>
<p>Again, since we are using pure functions, we can get away with
<code>pandocCompilerWithTransform</code> instead of <code>pandocCompilerWithTransformM</code>.</p>
<p>And we’re done! I also had to tweak my CSS slightly since <code>pandoc</code> was
generating a <code>span</code> for each line of source code instead of each token like
<code>ghc-syntax-highlighter</code> does. For the complete listing, see
<a href="https://github.com/vaibhavsagar/website/blob/6415721a318fc8fb31f4aadb7cd40ab6aad4fbc4/site.hs">here</a>.</p>
</summary>
</entry>
<entry>
    <title>Updating IHaskell to a Newer GHC</title>
    <link href="https://vaibhavsagar.com/blog/2021/05/02/updating-ihaskell-newer-ghc/" />
    <id>https://vaibhavsagar.com/blog/2021/05/02/updating-ihaskell-newer-ghc/index.html</id>
    <published>2021-05-02</published>
    <updated>2021-05-02T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on  2 May 2021
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;haskell&#39;." href="/blog/tags/haskell/index.html" rel="tag">haskell</a>, <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>, <a title="All pages tagged &#39;nix&#39;." href="/blog/tags/nix/index.html" rel="tag">nix</a>
    
</div>

<p>As the current maintainer of IHaskell, I see myself as having one primary
responsibility: keeping it up-to-date with newer GHC releases. The chain of
events that led to me becoming a maintainer started with the then-latest
version of IHaskell not having support for GHC 8.0, and I still remember how
frustrated I felt when dealing with this limitation.</p>
<p>Since then I’ve had the opportunity to add GHC 8.2, 8.4, 8.6, 8.8, 8.10, and
now 9.0 support, but because I only have to do this every 6 months or so (at
the earliest) I promptly forget the details of this work afterwards and have to
spelunk through old, often heavily amended, commits to rediscover what
past me (who is notoriously bad at documentation) did.</p>
<p>At the time of writing, GHC 9.2 is expected to be released soon and I don’t
want to forget everything I’ve just (re)learned when that happens.
Additionally, it is conceivable that at some point someone other than me would
like to take a crack at updating IHaskell to the newest version of GHC. This
blog post details the steps I took to make these tasks easier in the future.</p>
<h2 id="building-ihaskells-dependencies">Building IHaskell’s dependencies</h2>
<p>I should start by saying that my current approach relies heavily on Nix and the
infrastructure available in <a href="https://github.com/NixOS/nixpkgs"><code>nixpkgs</code></a>. If
you don’t want to use Nix for whatever reason the general ideas might still
translate to whatever method you use instead but the details will almost
certainly vary widely.</p>
<p>The objective of this first step is to get us to the point where all of
IHaskell’s dependencies are building, so that we can then focus on
<code>ghc-parser</code>, <code>ipython-kernel</code>, and <code>ihaskell</code> exclusively.</p>
<p>I start with a version of Nixpkgs that has the necessary GHC version and
package overrides to minimise work. As of this writing, the Nixpkgs maintainers
base the Haskell package set they use on <a href="https://www.stackage.org/">Stackage</a>
Nightlies with overrides added from
<a href="https://gitlab.haskell.org/ghc/head.hackage"><code>head.hackage</code></a>. Updates seem to
go into the
<a href="https://github.com/NixOS/nixpkgs/tree/haskell-updates"><code>haskell-updates</code></a>
branch first and are then periodically merged into <code>master</code>. I started with
<a href="https://github.com/NixOS/nixpkgs/commit/64c6086db4a6c19bb9960baf165c867c1774ab3d">this
commit</a>
but it had issues with building <code>alex</code> that I sent pull requests for <a href="https://github.com/NixOS/nixpkgs/pull/120535">to
Nixpkgs</a> and <a href="https://github.com/simonmar/alex/pull/185">to the
project</a>. In the meantime it’s very
easy to make any required changes to a fork or a local copy of Nixpkgs. I start
by copying <code>release.nix</code> from the IHaskell project root and changing the
reference to Nixpkgs:</p>
<details>
<summary style="cursor: pointer">
Changing Nixpkgs
</summary>
<div class="sourceCode" id="cb1"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="va">nixpkgs-src</span> <span class="op">=</span> <span class="bu">builtins</span><span class="op">.</span>fetchTarball <span class="op">{</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="va">url</span> <span class="op">=</span> <span class="st">&quot;https://github.com/NixOS/nixpkgs/tarball/8795d39ce70f04e3fd609422d522e5b2594f3a70&quot;</span><span class="op">;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="va">sha256</span> <span class="op">=</span> <span class="st">&quot;01w7q0nqydippj0ygbg77byb770snhc5rnqzc6isws58642l8z4s&quot;</span><span class="op">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">compiler</span> <span class="op">?</span> <span class="st">&quot;ghc901&quot;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="op">,</span> <span class="va">jupyterlabAppDir</span> <span class="op">?</span> <span class="cn">null</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="op">,</span> <span class="va">nixpkgs</span> <span class="op">?</span> <span class="bu">import</span> nixpkgs<span class="op">-</span>src <span class="op">{}</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="op">,</span> <span class="va">packages</span> <span class="op">?</span> <span class="op">(</span><span class="va">_</span><span class="op">:</span> <span class="op">[])</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="op">,</span> <span class="va">pythonPackages</span> <span class="op">?</span> <span class="op">(</span><span class="va">_</span><span class="op">:</span> <span class="op">[])</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="op">,</span> <span class="va">rtsopts</span> <span class="op">?</span> <span class="st">&quot;-M3g -N2&quot;</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="op">,</span> <span class="va">systemPackages</span> <span class="op">?</span> <span class="op">(</span><span class="va">_</span><span class="op">:</span> <span class="op">[])</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>:</span></code></pre></div>
</details>
<p>Then it’s possible to build this and see how many packages need changes:</p>
<pre class="shell"><code>$ nix-build release-9.0.nix --keep-going 2&gt;&amp;1 | wc -l</code></pre>
<p>Fixing the affected packages might involve patching, jailbreaking it so that
its dependency bounds are relaxed, using a newer version that is not included
in the package set by default, or any number of other changes. Here’s what I ended up with this time:</p>
<details>
<summary style="cursor: pointer">
Package set overrides
</summary>
<div class="sourceCode" id="cb3"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>      cryptohash<span class="op">-</span>md5    = nixpkgs<span class="op">.</span>haskell<span class="op">.</span>lib<span class="op">.</span>doJailbreak super<span class="op">.</span>cryptohash<span class="op">-</span>md5;</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>      cryptohash<span class="op">-</span>sha1   = nixpkgs<span class="op">.</span>haskell<span class="op">.</span>lib<span class="op">.</span>doJailbreak super<span class="op">.</span>cryptohash<span class="op">-</span>sha1;</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>      basement          = super<span class="op">.</span>basement_0_0_12;</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>      foundation        = super<span class="op">.</span>foundation_0_0_26_1;</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>      memory            = nixpkgs<span class="op">.</span>haskell<span class="op">.</span>lib<span class="op">.</span>appendPatch super<span class="op">.</span>memory <span class="op">(</span>nixpkgs<span class="op">.</span>fetchpatch <span class="op">{</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>        <span class="va">url</span> <span class="op">=</span> <span class="st">&quot;https://gitlab.haskell.org/ghc/head.hackage/-/raw/c89c1e27af8f180b3be476e102147557f922b224/patches/memory-0.15.0.patch&quot;</span><span class="op">;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>        <span class="va">sha256</span> <span class="op">=</span> <span class="st">&quot;0mkjbrzi05h1xds8rf5wfky176hrl03q0d7ipklp9x4ls3yyqj5x&quot;</span><span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>      <span class="op">})</span>;</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>      cryptonite        = nixpkgs<span class="op">.</span>haskell<span class="op">.</span>lib<span class="op">.</span>appendPatch super<span class="op">.</span>cryptonite <span class="op">(</span>nixpkgs<span class="op">.</span>fetchpatch <span class="op">{</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>        <span class="va">url</span> <span class="op">=</span> <span class="st">&quot;https://gitlab.haskell.org/ghc/head.hackage/-/raw/6a65307bbdc73c5eb4165a67ee97c7b9faa818e1/patches/cryptonite-0.28.patch&quot;</span><span class="op">;</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>        <span class="va">sha256</span> <span class="op">=</span> <span class="st">&quot;1wq9hw16qj2yqy7lyqbi7106lhk199hvnkj5xr7h0ip854gjsr5j&quot;</span><span class="op">;</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>      <span class="op">})</span>;</span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>      profunctors       = self<span class="op">.</span>callCabal2nix <span class="st">&quot;profunctors&quot;</span> profunctors<span class="op">-</span>src <span class="op">{}</span>; <span class="co"># `profunctors-src` is defined above</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>      mono<span class="op">-</span>traversable  = nixpkgs<span class="op">.</span>haskell<span class="op">.</span>lib<span class="op">.</span>dontCheck super<span class="op">.</span>mono<span class="op">-</span>traversable;</span></code></pre></div>
</details>
<p>After every few changes, I like to rerun <code>nix-build</code> and watch the number go
down. It’s also possible to build an individual package, e.g. to build
<code>foundation</code> (and any dependencies) one would run</p>
<pre class="shell"><code>$ nix-build release-9.0.nix -A passthru.haskellPackages.foundation</code></pre>
<p><a href="https://github.com/gibiansky/IHaskell/pull/1215/commits/12f50f34d9cf6dceb3ca5adc9fa450cee6e7dcee">This is what the final <code>release-9.0.nix</code> looked
like</a>.</p>
<p>Eventually only <code>ghc-parser</code>, maybe <code>ipython-kernel</code>, and <code>ihaskell</code> should fail to
build.</p>
<h2 id="updating-ghc-parser">Updating <code>ghc-parser</code></h2>
<p><code>ghc-parser</code> has the fewest dependencies of the three packages we are changing
so it makes sense to start there. I’m relatively low-tech as far as development
workflow goes and I prefer <code>ghcid</code> and a text editor, mostly because I haven’t
yet figured out how to get anything more advanced to work. To get <code>ghcid</code>
running, assuming you have it installed globally like I do, you relax the
version bounds in <code>ghc-parser.cabal</code> and run</p>
<pre class="shell"><code>$ nix-shell release-9.0.nix -A passthru.haskellPackages.ghc-parser.env
$ cd ghc-parser
$ runhaskell Setup.hs configure
$ ghcid -c runhaskell Setup.hs repl lib:ghc-parser</code></pre>
<p>Most of the compilation errors are related to <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/13009">this GHC module
restructuring</a> that started
in GHC 8.10 and continued in GHC 9.0. If I had kept better notes from last time
I would have looked at
<a href="https://github.com/hsyl20/ghc-api-compat/"><code>ghc-api-compat</code></a> which offers
a compatibility shim and whose
<a href="https://github.com/hsyl20/ghc-api-compat/blob/master/ghc-api-compat.cabal"><code>.cabal</code></a>
file makes translating between old and new module names very easy. Instead
I ended up looking at the <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/libraries/ghc-9.0.1/index.html">GHC 9.0 API
Haddocks</a>
and the <a href="https://hackage.haskell.org/package/ghc-8.10.2">GHC 8.10 API
Haddocks</a>. As an aside, I am
irritated that the most recently released GHC API documentation isn’t available
on Hackage. I also like to have a local checkout of the GHC source so that
I can look at the code across different commits if required.</p>
<p><a href="https://github.com/gibiansky/IHaskell/pull/1215/commits/063e6bb0459b7ff8d9a2e92090332bf7a1e92a63">These are the changes I needed to make to
<code>ghc-parser</code></a>.</p>
<h2 id="updating-ipython-kernel">Updating <code>ipython-kernel</code></h2>
<p><code>ipython-kernel</code> doesn’t depend on the GHC API directly, so changes to it are
usually related to breaking API changes in other dependencies. In this case, no
changes were required!</p>
<h2 id="updating-ihaskell">Updating <code>ihaskell</code></h2>
<p>This is usually the most involved package to update, as its operation is
intimately tied with the details of the GHC API. Most of the changes required
were for three reasons:</p>
<ol type="1">
<li><p>The aforementioned module hierarchy change</p></li>
<li><p>Changing the terminology from
“packages” to “units” as described in <a href="https://gitlab.haskell.org/ghc/ghc/-/commit/10a2ba90aa6a788677104cc43318c66f46e2e2b0">this
commit</a></p></li>
<li><p>Removing specialised <code>gcatch</code>, <code>gtry</code>, etc. functions in favour of the more
general versions in <code>exceptions</code>, as detailed in <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/9.0.1-notes.html#ghc-library">this section of the release
notes</a></p></li>
</ol>
<p>As before, it’s possible to get <code>ghcid</code> running with</p>
<pre class="shell"><code>$ nix-shell release-9.0.nix -A passthru.haskellPackages.ghc-parser.env
$ runhaskell Setup.hs configure --enable-tests
$ ghcid -c runhaskell Setup.hs repl lib:ihaskell</code></pre>
<p>After getting everything compiling, I like to build the <code>ihaskell</code> package by
running</p>
<pre class="shell"><code>$ nix-build release-9.0.nix -A passthru.haskellPackages.ihaskell</code></pre>
<p>because this sets up the test environment correctly (i.e. putting the built
<code>ihaskell</code> executable in the <code>$PATH</code>) before running tests, although course you
could do this manually. This usually catches any issues that have slipped
through and <a href="https://github.com/gibiansky/IHaskell/pull/1215/commits/1796c35119ced7a564e75fe07067797fb182149d#diff-409dc396158ef9f7f39928cb144c6c3037072f0d9932499d2213862e0f5fbae6">small formatting changes in GHC output across
versions</a>.</p>
<p><a href="https://github.com/gibiansky/IHaskell/pull/1215/commits/1796c35119ced7a564e75fe07067797fb182149d">Here are the changes I made to
<code>ihaskell</code></a>.</p>
<h2 id="acceptance-testing">Acceptance testing</h2>
<p>Since IHaskell bridges the Jupyter and GHC ecosystems, we have an acceptance
test that essentially runs an IHaskell notebook through
<a href="https://nbconvert.readthedocs.io/en/latest/"><code>nbconvert</code></a> and ensures that the
output is identical to the input. Because GHC output (amongst other things)
differs across GHC versions, this acceptance test was frequently broken and/or
a bad indicator of whether any changes were correct. Recently <a href="https://github.com/jamesdbrock">James
Brock</a> simplified and greatly improved the
acceptance test to be more reliable. Unfortunately the latest releases of
Jupyter now include additional metadata with each response including the time
of reply, which cannot be expected to be the same across runs. In the past it’s
been possible to filter the offending fields out using <code>grep -e</code> but a more
sophisticated approach was required this time so I took the opportunity to
learn a little more about <a href="https://stedolan.github.io/jq/"><code>jq</code></a> and used that
instead. This new approach should also be more flexible and better at
accommodating future output changes.</p>
<p><a href="https://github.com/gibiansky/IHaskell/pull/1215/commits/4b62c964fb8937353d39a8798dc13d06260c9257">Here are the changes I made to the acceptance
tests</a>.</p>
<h2 id="using-the-updated-ihaskell">Using the updated IHaskell</h2>
<p>We’re done! I like to quickly try out a new notebook, as a quick test that
everything works as expected (and also for the novelty of being the first
person to try IHaskell on the newest GHC). To do this, I run</p>
<pre class="shell"><code>$ nix-build release-9.0.nix
$ result/bin/jupyter-notebook</code></pre>
</summary>
</entry>
<entry>
    <title>Writing GitHub Secrets to a Repository You Don't Own</title>
    <link href="https://vaibhavsagar.com/blog/2020/05/04/github-secrets-api/" />
    <id>https://vaibhavsagar.com/blog/2020/05/04/github-secrets-api/index.html</id>
    <published>2020-05-04</published>
    <updated>2020-05-04T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on  4 May 2020
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;ci&#39;." href="/blog/tags/ci/index.html" rel="tag">ci</a>, <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>
    
</div>

<p>I’ve been having a lot of fun migrating the CI systems of my repositories to
use GitHub Actions, but it’s been more difficult to do the same with projects
that are owned by someone else because I don’t have access to the repository
settings that would allow me to create secrets. This means that I can build and
test those projects but not e.g. upload a Docker container as part of
a successful build or upload artifacts somewhere else.</p>
<p>I’ve tried to work around this limitation by creating a separate repository
that I own and using the <code>cron</code> functionality to do this on a schedule, but
this is a poor substitute. I’ve been frustrated by this situation for a while,
and while reading the
<a href="https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets">documentation</a>
I noticed this interesting snippet:</p>
<blockquote>
<p>If you are using the REST API to create secrets, anyone with write access to
the repository can create secrets. For more information, see <a href="https://developer.github.com/v3/actions/secrets/#create-or-update-a-secret-for-a-repository">“GitHub Actions
secrets
API”</a>
in the GitHub Developer documentation.</p>
</blockquote>
<p>Amazing! This makes it sound like it’s purely a UI issue. So emboldened, I was
able to create and use my secrets only a couple of hours later by poking at the
GitHub API.</p>
<p>I’m going to go ahead and write down the steps I took in order to make this
happen, because this seems like the kind of thing I might have to do more than
once and it’s just fiddly enough that I will quickly forget if I don’t.</p>
<p>The first thing I need is a GitHub Personal Access Token with the <code>repo</code> scope,
which I can create from <a href="https://github.com/settings/tokens">this page</a>.</p>
<p>The next thing to do is to retrieve the public key for the relevant repository:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> curl <span class="at">-H</span> <span class="st">&quot;Authorization: token </span><span class="va">$TOKEN</span><span class="st">&quot;</span> https://api.github.com/repos/gibiansky/IHaskell/actions/secrets/public-key</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">{</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;key_id&quot;</span><span class="ex">:</span> <span class="st">&quot;</span><span class="va">$KEY_ID</span><span class="st">&quot;</span>,</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;key&quot;</span><span class="ex">:</span> <span class="st">&quot;</span><span class="va">$PUBLIC_KEY</span><span class="st">&quot;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span></code></pre></div>
<p>Then I can see what secrets are available:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> curl <span class="at">-H</span> <span class="st">&quot;Authorization: token </span><span class="va">$TOKEN</span><span class="st">&quot;</span> https://api.github.com/repos/gibiansky/IHaskell/actions/secrets</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">{</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;total_count&quot;</span><span class="ex">:</span> 0,</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;secrets&quot;</span><span class="ex">:</span> [</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>  <span class="ex">]</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span></code></pre></div>
<p>The secrets need to be encrypted, and there is sample code for doing this in
Python:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> base64 <span class="im">import</span> b64encode</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> nacl <span class="im">import</span> encoding, public</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> encrypt(public_key: <span class="bu">str</span>, secret_value: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;&quot;&quot;Encrypt a Unicode string using the public key.&quot;&quot;&quot;</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    public_key <span class="op">=</span> public.PublicKey(public_key.encode(<span class="st">&quot;utf-8&quot;</span>), encoding.Base64Encoder())</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>    sealed_box <span class="op">=</span> public.SealedBox(public_key)</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>    encrypted <span class="op">=</span> sealed_box.encrypt(secret_value.encode(<span class="st">&quot;utf-8&quot;</span>))</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> b64encode(encrypted).decode(<span class="st">&quot;utf-8&quot;</span>)</span></code></pre></div>
<p>I added a Nix shebang line and decided to generate all the encrypted secrets
I needed:</p>
<p><em>secret.py</em></p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">#! /usr/bin/env nix-shell</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="co">#! nix-shell -i python</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co">#! nix-shell -p &quot;python3.withPackages (p: [ p.pynacl ])&quot;</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="im">from</span> base64 <span class="im">import</span> b64encode</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> nacl <span class="im">import</span> encoding, public</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> encrypt(public_key: <span class="bu">str</span>, secret_value: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;&quot;&quot;Encrypt a Unicode string using the public key.&quot;&quot;&quot;</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    public_key <span class="op">=</span> public.PublicKey(public_key.encode(<span class="st">&quot;utf-8&quot;</span>), encoding.Base64Encoder())</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>    sealed_box <span class="op">=</span> public.SealedBox(public_key)</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>    encrypted <span class="op">=</span> sealed_box.encrypt(secret_value.encode(<span class="st">&quot;utf-8&quot;</span>))</span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> b64encode(encrypted).decode(<span class="st">&quot;utf-8&quot;</span>)</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>public_key <span class="op">=</span> <span class="st">&quot;$PUBLIC_KEY&quot;</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;CACHIX_SIGNING_KEY=&quot;</span>, encrypt(public_key, <span class="st">&quot;$CACHIX_SIGNING_KEY&quot;</span>))</span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;DOCKER_USERNAME=&quot;</span>, encrypt(public_key, <span class="st">&#39;$DOCKER_USERNAME&#39;</span>))</span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;DOCKER_PASSWORD=&quot;</span>, encrypt(public_key, <span class="st">&#39;$DOCKER_PASSWORD&#39;</span>))</span></code></pre></div>
<p>This was easy to run:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> chmod +x secret.py</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> ./secret.py</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="va">CACHIX_SIGNING_KEY</span><span class="op">=</span> <span class="va">$ENCRYPTED_CACHIX_SIGNING_KEY</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="va">DOCKER_USERNAME</span><span class="op">=</span> <span class="va">$ENCRYPTED_DOCKER_USERNAME</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="va">DOCKER_PASSWORD</span><span class="op">=</span> <span class="va">$ENCRYPTED_DOCKER_PASSWORD</span></span></code></pre></div>
<p>And I chose to update the secrets manually with <code>curl</code> even though I could have
automated it with <code>requests</code> or something similar (which I might if I have to
do this again soon), for example:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> curl <span class="at">-X</span> PUT <span class="at">-H</span> <span class="st">&quot;Authorization: token </span><span class="va">$TOKEN</span><span class="st">&quot;</span> <span class="at">-H</span> <span class="st">&quot;Content-Type: application/json&quot;</span> <span class="at">-i</span> https://api.github.com/repos/gibiansky/IHaskell/actions/secrets/CACHIX_SIGNING_KEY <span class="at">-d</span> <span class="st">&#39;{&quot;key_id&quot;: &quot;$KEY_ID&quot;, &quot;encrypted_value&quot;: &quot;$ENCRYPTED_CACHIX_SIGNING_KEY&quot;}&#39;</span></span></code></pre></div>
<p>Finally I can check that the secrets were created correctly:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> curl <span class="at">-H</span> <span class="st">&quot;Authorization: token </span><span class="va">$TOKEN</span><span class="st">&quot;</span> https://api.github.com/repos/gibiansky/IHaskell/actions/secrets</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="kw">{</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;total_count&quot;</span><span class="ex">:</span> 3,</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;secrets&quot;</span><span class="ex">:</span> [</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">{</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;name&quot;</span><span class="ex">:</span> <span class="st">&quot;CACHIX_SIGNING_KEY&quot;</span>,</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;created_at&quot;</span><span class="ex">:</span> <span class="st">&quot;2020-05-03T04:45:07Z&quot;</span>,</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;updated_at&quot;</span><span class="ex">:</span> <span class="st">&quot;2020-05-03T04:45:07Z&quot;</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>    <span class="ex">},</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>    <span class="kw">{</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;name&quot;</span><span class="ex">:</span> <span class="st">&quot;DOCKER_PASSWORD&quot;</span>,</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;created_at&quot;</span><span class="ex">:</span> <span class="st">&quot;2020-05-03T04:49:59Z&quot;</span>,</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;updated_at&quot;</span><span class="ex">:</span> <span class="st">&quot;2020-05-03T04:49:59Z&quot;</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>    <span class="ex">},</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>    <span class="kw">{</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;name&quot;</span><span class="ex">:</span> <span class="st">&quot;DOCKER_USERNAME&quot;</span>,</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;created_at&quot;</span><span class="ex">:</span> <span class="st">&quot;2020-05-03T04:48:52Z&quot;</span>,</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;updated_at&quot;</span><span class="ex">:</span> <span class="st">&quot;2020-05-03T04:48:52Z&quot;</span></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a>    <span class="kw">}</span></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a>  <span class="ex">]</span></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span></code></pre></div>
<p>I hope these instructions are useful, future me!</p>
</summary>
</entry>
<entry>
    <title>Getting Along with JavaScript</title>
    <link href="https://vaibhavsagar.com/blog/2019/10/29/getting-along-with-javascript/" />
    <id>https://vaibhavsagar.com/blog/2019/10/29/getting-along-with-javascript/index.html</id>
    <published>2019-10-29</published>
    <updated>2019-10-29T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on 29 October 2019
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>, <a title="All pages tagged &#39;haskell&#39;." href="/blog/tags/haskell/index.html" rel="tag">haskell</a>, <a title="All pages tagged &#39;nix&#39;." href="/blog/tags/nix/index.html" rel="tag">nix</a>
    
</div>

<p>For the last couple of weeks, I’ve been obsessed with the idea of running
Haskell in the browser. I know this is possible, because this is what I do at
work every day, but the applications I work on professionally are complex
beasts with Haskell backends and dedicated servers making them available to
users. I’m looking for something lighter that I can serve statically using
GitHub Pages or <a href="https://glitch.com">Glitch</a>, so I can plop some code on a
webpage and never worry about hosting ever again.</p>
<p>My first instinct was to reach for a tool like
<a href="https://github.com/obsidiansystems/obelisk">Obelisk</a>, which bills itself as
“an easy way to develop and deploy your Reflex project”. Although it does work
as advertised(!), it is geared towards the needs of the large apps I mentioned
above. It prerenders webpages where possible to make projects as snappy as
possible, works best within the confines of the Obelisk libraries, and assumes
at least one NixOS target that will host your website, all of which mean it
doesn’t yet scale down to my comparatively modest needs. It is possible to use
Obelisk anyway, but I found myself using too few of its features to justify the
effort, and I decided to move down a level and use <a href="https://github.com/reflex-frp/reflex-platform">Reflex
Platform</a> directly, which is a
set of changes and overrides to a revision of
<a href="https://github.com/NixOS/nixpkgs">Nixpkgs</a> to best support building full-stack
and mobile Haskell applications.</p>
<p>If you’d like to follow along, I have the code available <a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8">at this
gist</a>
with <a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/revisions">each
revision</a>
representing a step in the progression.</p>
<h3 id="setting-up-reflex-platform">Setting up reflex-platform</h3>
<p>I like to use the <code>updater</code> script described in <a href="/blog/2018/05/27/quick-easy-nixpkgs-pinning">a previous blog
post</a>, so I’ll start by copying that over and
creating a <code>versions.json</code> with the following contents:</p>
<details>
<summary style="cursor: pointer">
<code>versions.json</code>
</summary>
<div class="sourceCode" id="cb1"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;reflex-platform&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;owner&quot;</span><span class="fu">:</span> <span class="st">&quot;reflex-frp&quot;</span><span class="fu">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;repo&quot;</span><span class="fu">:</span> <span class="st">&quot;reflex-platform&quot;</span><span class="fu">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;branch&quot;</span><span class="fu">:</span> <span class="st">&quot;develop&quot;</span><span class="fu">,</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;rev&quot;</span><span class="fu">:</span> <span class="st">&quot;&quot;</span><span class="fu">,</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;sha256&quot;</span><span class="fu">:</span> <span class="st">&quot;&quot;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>  <span class="fu">}</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
</details>
<p>I can then update this by running:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> ./updater versions.json reflex-platform</span></code></pre></div>
<p>to get the latest <code>reflex-platform</code>. At the time of writing, this is the
revision I used:</p>
<details>
<summary style="cursor: pointer">
pinned <code>versions.json</code>
</summary>
<div class="sourceCode" id="cb3"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;reflex-platform&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;owner&quot;</span><span class="fu">:</span> <span class="st">&quot;reflex-frp&quot;</span><span class="fu">,</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;repo&quot;</span><span class="fu">:</span> <span class="st">&quot;reflex-platform&quot;</span><span class="fu">,</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;branch&quot;</span><span class="fu">:</span> <span class="st">&quot;develop&quot;</span><span class="fu">,</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;rev&quot;</span><span class="fu">:</span> <span class="st">&quot;8f4b8973a06f78c7aaf1a222f8f8443cd934569f&quot;</span><span class="fu">,</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;sha256&quot;</span><span class="fu">:</span> <span class="st">&quot;167smg7dyvg5yf1wn9bx6yxvazlk0qk64rzgm2kfzn9mx873s0vp&quot;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>  <span class="fu">}</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/c21e62ecdcc053273ee5e4815ef538e1e8a29e55#file-versions-json">revision</a>)</em></p>
<h3 id="creating-a-project-skeleton">Creating a project skeleton</h3>
<p>The next step is to get a Haskell project skeleton in place. I used <code>cabal init</code> for this as follows:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> nix-shell <span class="at">-p</span> ghc cabal-install <span class="at">--run</span> <span class="st">&#39;cabal init -lBSD3&#39;</span></span></code></pre></div>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/d4c4f9ff39b595f5c8858892328adfa6ab4a4cc8#file-small-viz-cabal">revision</a>)</em></p>
<p>which generated an executable-only project, just like I wanted. I named this
project <code>small-viz</code>, because it’s a small project using the
<a href="http://viz-js.com/">Viz.js</a> library, but more on that later.</p>
<p>The next step is to actually use <code>reflex-platform</code> to develop this project, for
which we need to write a little Nix. Here’s the <code>default.nix</code> I used:</p>
<details>
<summary style="cursor: pointer">
<code>default.nix</code>
</summary>
<div class="sourceCode" id="cb5"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>  <span class="co"># ./updater versions.json reflex-platform</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>  <span class="va">fetcher</span> <span class="op">=</span> <span class="op">{</span> <span class="va">owner</span><span class="op">,</span> <span class="va">repo</span><span class="op">,</span> <span class="va">rev</span><span class="op">,</span> <span class="va">sha256</span><span class="op">,</span> <span class="op">...</span> <span class="op">}</span>: <span class="bu">builtins</span><span class="op">.</span>fetchTarball <span class="op">{</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">inherit</span> <span class="va">sha256</span><span class="op">;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    <span class="va">url</span> <span class="op">=</span> <span class="st">&quot;https://github.com/</span><span class="sc">${</span>owner<span class="sc">}</span><span class="st">/</span><span class="sc">${</span>repo<span class="sc">}</span><span class="st">/tarball/</span><span class="sc">${</span>rev<span class="sc">}</span><span class="st">&quot;</span><span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>  <span class="va">reflex-platform</span> <span class="op">=</span> fetcher <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>fromJSON <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>readFile <span class="ss">./versions.json</span><span class="op">)).</span>reflex<span class="op">-</span>platform<span class="op">;</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> <span class="op">(</span><span class="bu">import</span> reflex<span class="op">-</span>platform <span class="op">{</span> <span class="va">system</span> <span class="op">=</span> <span class="bu">builtins</span><span class="op">.</span>currentSystem<span class="op">;</span> <span class="op">}).</span>project <span class="op">({</span> <span class="va">pkgs</span><span class="op">,</span> <span class="op">...</span> <span class="op">}</span>: <span class="op">{</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>  <span class="va">useWarp</span> <span class="op">=</span> <span class="cn">true</span><span class="op">;</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>  <span class="va">withHoogle</span> <span class="op">=</span> <span class="cn">false</span><span class="op">;</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>  <span class="va">packages</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>    <span class="va">small-viz</span> <span class="op">=</span> <span class="ss">./.</span><span class="op">;</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>  <span class="va">shells</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>    <span class="va">ghc</span> <span class="op">=</span> <span class="op">[</span><span class="st">&quot;small-viz&quot;</span><span class="op">];</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>    <span class="va">ghcjs</span> <span class="op">=</span> <span class="op">[</span><span class="st">&quot;small-viz&quot;</span><span class="op">];</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a><span class="op">})</span></span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/348b759019bc19aec9833a6b5f042c1d2f5e9b13#file-default-nix">revision</a>)</em></p>
<p>This sets up our project to build with both GHC and GHCJS, because we want to
develop with GHC but eventually use GHCJS to create our final artifact. I also
set a few more options:</p>
<ol type="1">
<li><p><code>useWarp = true</code> changes the JSaddle backend to <code>jsaddle-warp</code> so we can
develop using the browser, as described
<a href="https://github.com/reflex-frp/reflex-platform/blob/8f4b8973a06f78c7aaf1a222f8f8443cd934569f/docs/project-development.md#building-frontends-with-ghc">here</a>.</p></li>
<li><p><code>withHoogle = false</code> means we don’t build a local Hoogle database every time
our packages are updated, because this step is slow and I never used the
local documentation anyway.</p></li>
</ol>
<p>For the next step I’ll assume binary cache substitution has been set up as
described
<a href="https://github.com/reflex-frp/reflex-platform/blob/develop/notes/NixOS.md#enabling-the-binary-cache-on-nixos">here</a>:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> nix-shell <span class="at">-A</span> shells.ghc</span></code></pre></div>
<p>This should download a lot (and build almost nothing from source since we are
pulling from the cache), and then enter a shell environment with our
dependencies in scope.</p>
<h3 id="starting-our-reflex-app">Starting our Reflex app</h3>
<p>Now we can start developing our Reflex app! We can start from the small example
described
<a href="https://github.com/reflex-frp/reflex-platform/tree/8f4b8973a06f78c7aaf1a222f8f8443cd934569f#dynamics-and-events">here</a>:</p>
<details>
<summary style="cursor: pointer">
<code>Main.hs</code>
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="pp">{-# LANGUAGE OverloadedStrings #-}</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Reflex.Dom</span><span>

</span><span class="va">main</span><span> </span><span class="ot">=</span><span> </span><span class="va">mainWidget</span><span> </span><span class="op">$</span><span> </span><span class="va">el</span><span> </span><span class="st">&quot;div&quot;</span><span> </span><span class="op">$</span><span> </span><span class="kw">do</span><span>
  </span><span class="va">t</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">inputElement</span><span> </span><span class="va">def</span><span>
  </span><span class="va">dynText</span><span> </span><span class="op">$</span><span> </span><span class="va">_inputElement_value</span><span> </span><span class="va">t</span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/93c510d77f7a8d6b1d8d63bb1cb0be37c6d575b5#file-main-hs">revision</a>)</em></p>
<p>We also have to add <code>reflex-dom</code> and <code>reflex</code> to our dependencies in our
<code>.cabal</code> file, and then we can get a automatically-reloading development build
with one command:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> nix-shell <span class="at">-A</span> shells.ghc <span class="at">--run</span> <span class="st">&#39;ghcid -T &quot;Main.main&quot; --command &quot;cabal new-repl&quot;&#39;</span></span></code></pre></div>
<p>This allows a native Haskell process to control a web page, so we can navigate
to it using our browser at <code>http://localhost:3003</code> and have a fast feedback
loop. In practice there is a lot of browser refreshing involved, but this is
still much nicer than having to do a GHCJS build each time we want to look at
our changes. Now we have an input box that repeats what we type into it, which
is a good start. I should point out that this works a lot better on Google
Chrome (or Chromium) than it does on Firefox, and that’s what I’ll be using for
development. The final GHCJS output does not have this limitation.</p>
<p>So where are we going with this? My plan is to build a crude version of the
<a href="http://viz-js.com">Viz.js</a> homepage, where you can write
<a href="https://en.wikipedia.org/wiki/DOT_(graph_description_language)">DOT</a> and see
it rendered instantly. Viz.js is the result of compiling the venerable
<a href="http://graphviz.org/">Graphviz</a> to JavaScript using
<a href="https://emscripten.org">Emscripten</a>. It’s no longer maintained but still works
fine as far as I can tell. In order to do this I want to use some kind of
JavaScript FFI to call out to <code>viz.js</code>, but first I want to swap out our text
input for a text area, and move the repeated output to just below the text area
instead of beside it.</p>
<details>
<summary style="cursor: pointer">
<code>Main.hs</code>
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="pp">{-# LANGUAGE OverloadedStrings #-}</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Reflex.Dom</span><span>

</span><span class="va">main</span><span> </span><span class="ot">=</span><span> </span><span class="va">mainWidget</span><span> </span><span class="op">$</span><span> </span><span class="va">el</span><span> </span><span class="st">&quot;div&quot;</span><span> </span><span class="op">$</span><span> </span><span class="kw">do</span><span>
  </span><span class="va">t</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">textArea</span><span> </span><span class="va">def</span><span>
  </span><span class="va">el</span><span> </span><span class="st">&quot;div&quot;</span><span> </span><span class="op">$</span><span>
    </span><span class="va">dynText</span><span> </span><span class="op">$</span><span> </span><span class="va">_textArea_value</span><span> </span><span class="va">t</span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/d5ff4725b26db3dd596abb2e751711f5c568b6bc#file-main-hs">revision</a>)</em></p>
<h3 id="integrating-with-viz.js">Integrating with Viz.js</h3>
<p>The latest version of Viz.js is available
<a href="https://www.jsdelivr.com/package/npm/viz.js">here</a>, and we can include it
using <code>mainWidgetWithHead</code>:</p>
<details>
<summary style="cursor: pointer">
<code>Main.hs</code>
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="pp">{-# LANGUAGE OverloadedStrings #-}</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Reflex.Dom</span><span>

</span><span class="va">main</span><span> </span><span class="ot">=</span><span> </span><span class="va">mainWidgetWithHead</span><span> </span><span class="va">widgetHead</span><span> </span><span class="op">$</span><span> </span><span class="va">el</span><span> </span><span class="st">&quot;div&quot;</span><span> </span><span class="op">$</span><span> </span><span class="kw">do</span><span>
  </span><span class="va">t</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">textArea</span><span> </span><span class="va">def</span><span>
  </span><span class="va">el</span><span> </span><span class="st">&quot;div&quot;</span><span> </span><span class="op">$</span><span>
    </span><span class="va">dynText</span><span> </span><span class="op">$</span><span> </span><span class="va">_textArea_value</span><span> </span><span class="va">t</span><span>
  </span><span class="kw">where</span><span>
    </span><span class="va">widgetHead</span><span> </span><span class="ot">::</span><span> </span><span class="dt">DomBuilder</span><span> </span><span class="va">t</span><span> </span><span class="va">m</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">m</span><span> </span><span class="ot">(</span><span class="ot">)</span><span>
    </span><span class="va">widgetHead</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
      </span><span class="va">script</span><span> </span><span class="st">&quot;https://cdn.jsdelivr.net/npm/viz.js@2.1.2/viz.min.js&quot;</span><span>
      </span><span class="va">script</span><span> </span><span class="st">&quot;https://cdn.jsdelivr.net/npm/viz.js@2.1.2/full.render.min.js&quot;</span><span>
    </span><span class="va">script</span><span> </span><span class="va">src</span><span> </span><span class="ot">=</span><span> </span><span class="va">elAttr</span><span> </span><span class="st">&quot;script&quot;</span><span> </span><span class="ot">(</span><span class="st">&quot;type&quot;</span><span> </span><span class="op">=:</span><span> </span><span class="st">&quot;text/javascript&quot;</span><span> </span><span class="op">&lt;&gt;</span><span> </span><span class="st">&quot;src&quot;</span><span> </span><span class="op">=:</span><span> </span><span class="va">src</span><span class="ot">)</span><span> </span><span class="va">blank</span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/6a856c34755f730793f3b588a82f0fc9f836bf9c#file-main-hs">revision</a>)</em></p>
<p>Now we can poke around with our browser developer tools until we have a useful
JavaScript function. Here’s what I came up with, based on the examples in the
<a href="https://github.com/mdaines/viz.js/wiki/Usage#using-a-script-tag">wiki</a>:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span>(e<span class="op">,</span> string) {</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">var</span> viz <span class="op">=</span> <span class="kw">new</span> <span class="fu">Viz</span>()<span class="op">;</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>  viz<span class="op">.</span><span class="fu">renderSVGElement</span>(string)</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span><span class="fu">then</span>(<span class="kw">function</span>(element) {</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>    e<span class="op">.</span><span class="at">innerHTML</span> <span class="op">=</span> element<span class="op">.</span><span class="at">outerHTML</span><span class="op">;</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>  })</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span><span class="fu">catch</span>(<span class="kw">function</span>(error) {</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>    e<span class="op">.</span><span class="at">innerHTML</span> <span class="op">=</span> error<span class="op">;</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>  })</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>Then we can start thinking about how we want to do JavaScript interop! Although
there is a GHCJS FFI as described <a href="https://github.com/ghcjs/ghcjs/wiki/A-few-examples-of-Foreign-Function-Interface">in the
wiki</a>,
this doesn’t seem to work at all with GHC, and that means we can’t use it
during development. I don’t think that’s good enough, and fortunately we don’t
have to settle for this and instead can use
<a href="http://hackage.haskell.org/package/jsaddle-0.9.6.0"><code>jsaddle</code></a>, which
describes itself as “an EDSL for calling JavaScript that can be used both from
GHCJS and GHC”. We can add <code>jsaddle</code> to our dependencies, add <code>Viz</code> to the
<code>exposed-modules</code> stanza in our <code>.cabal</code> file, and create a new module <code>Viz</code>,
and then we can use the <code>eval</code> and <code>call</code> functions to call our JavaScript
directly:</p>
<details>
<summary style="cursor: pointer">
<code>Viz.hs</code>
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">module</span><span> </span><span class="dt">Viz</span><span> </span><span class="kw">where</span><span>

</span><span class="kw">import</span><span> </span><span class="dt">Language.Javascript.JSaddle</span><span>

</span><span class="va">viz</span><span> </span><span class="ot">::</span><span> </span><span class="dt">JSVal</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">JSVal</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">JSM</span><span> </span><span class="ot">(</span><span class="ot">)</span><span>
</span><span class="va">viz</span><span> </span><span class="va">element</span><span> </span><span class="va">string</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
  </span><span class="va">call</span><span> </span><span class="va">vizJs</span><span> </span><span class="va">vizJs</span><span> </span><span class="ot">[</span><span class="va">element</span><span class="ot">,</span><span> </span><span class="va">string</span><span class="ot">]</span><span>
  </span><span class="va">pure</span><span> </span><span class="ot">(</span><span class="ot">)</span><span>

</span><span class="va">vizJs</span><span> </span><span class="ot">::</span><span> </span><span class="dt">JSM</span><span> </span><span class="dt">JSVal</span><span>
</span><span class="va">vizJs</span><span> </span><span class="ot">=</span><span> </span><span class="va">eval</span><span>
  </span><span class="st">&quot;(function(e, string) { \
  \  var viz = new Viz(); \
  \  viz.renderSVGElement(string) \
  \  .then(function(element) { \
  \    e.innerHTML = element.outerHTML; \
  \  }) \
  \  .catch(function(error) { \
  \    e.innerHTML = error; \
  \  }) \
  \})&quot;</span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/b234b2649022b1b560df1281f053bac30289ce12#file-viz-hs">revision</a>)</em></p>
<p>JSaddle runs operations in <code>JSM</code>, which is similar to <code>IO</code>, and all functions
take values of type <code>JSVal</code> that can be represented as JavaScript values. We
pass <code>vizJs</code> to <code>call</code> twice because the second parameter represents the <code>this</code>
keyword.</p>
<p>Wiring everything up together is just a few more lines of code:</p>
<details>
<summary style="cursor: pointer">
<code>Main.hs</code>
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="pp">{-# LANGUAGE OverloadedStrings #-}</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Reflex.Dom</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Language.Javascript.JSaddle</span><span> </span><span class="ot">(</span><span class="va">liftJSM</span><span class="ot">,</span><span> </span><span class="va">toJSVal</span><span class="ot">)</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Viz</span><span> </span><span class="ot">(</span><span class="va">viz</span><span class="ot">)</span><span>

</span><span class="va">main</span><span> </span><span class="ot">=</span><span> </span><span class="va">mainWidgetWithHead</span><span> </span><span class="va">widgetHead</span><span> </span><span class="op">$</span><span> </span><span class="va">el</span><span> </span><span class="st">&quot;div&quot;</span><span> </span><span class="op">$</span><span> </span><span class="kw">do</span><span>
  </span><span class="va">t</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">textArea</span><span> </span><span class="va">def</span><span>
  </span><span class="va">e</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">_element_raw</span><span> </span><span class="op">.</span><span> </span><span class="va">fst</span><span> </span><span class="op">&lt;$&gt;</span><span> </span><span class="va">el&#39;</span><span> </span><span class="st">&quot;div&quot;</span><span> </span><span class="va">blank</span><span>
  </span><span class="va">performEvent_</span><span> </span><span class="op">$</span><span> </span><span class="va">ffor</span><span> </span><span class="ot">(</span><span class="va">updated</span><span> </span><span class="ot">(</span><span class="va">_textArea_value</span><span> </span><span class="va">t</span><span class="ot">)</span><span class="ot">)</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="va">text</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="va">liftJSM</span><span> </span><span class="op">$</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">jsE</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">toJSVal</span><span> </span><span class="va">e</span><span>
    </span><span class="va">jsT</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">toJSVal</span><span> </span><span class="va">text</span><span>
    </span><span class="va">viz</span><span> </span><span class="va">jsE</span><span> </span><span class="va">jsT</span><span>
  </span><span class="kw">where</span><span>
    </span><span class="va">widgetHead</span><span> </span><span class="ot">::</span><span> </span><span class="dt">DomBuilder</span><span> </span><span class="va">t</span><span> </span><span class="va">m</span><span> </span><span class="ot">=&gt;</span><span> </span><span class="va">m</span><span> </span><span class="ot">(</span><span class="ot">)</span><span>
    </span><span class="va">widgetHead</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
      </span><span class="va">script</span><span> </span><span class="st">&quot;https://cdn.jsdelivr.net/npm/viz.js@2.1.2/viz.min.js&quot;</span><span>
      </span><span class="va">script</span><span> </span><span class="st">&quot;https://cdn.jsdelivr.net/npm/viz.js@2.1.2/full.render.min.js&quot;</span><span>
    </span><span class="va">script</span><span> </span><span class="va">src</span><span> </span><span class="ot">=</span><span> </span><span class="va">elAttr</span><span> </span><span class="st">&quot;script&quot;</span><span> </span><span class="ot">(</span><span class="st">&quot;type&quot;</span><span> </span><span class="op">=:</span><span> </span><span class="st">&quot;text/javascript&quot;</span><span> </span><span class="op">&lt;&gt;</span><span> </span><span class="st">&quot;src&quot;</span><span> </span><span class="op">=:</span><span> </span><span class="va">src</span><span class="ot">)</span><span> </span><span class="va">blank</span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/e0e886959e338803f9c4a1a3596f8eb88474424d#file-main-hs">revision</a>)</em></p>
<p>There’s a lot going on here, so I’ll explain in a little more detail.</p>
<p>Instead of an element which displays the textarea contents as they are updated,
we just want a reference to a blank <code>&lt;div&gt;</code>, so we use the
<a href="https://hackage.haskell.org/package/reflex-dom-core-0.5/docs/Reflex-Dom-Widget-Basic.html#v:el-39-"><code>el'</code></a>
function and pull out the raw element.
<a href="http://hackage.haskell.org/package/reflex-0.6.2.4/docs/Reflex-PerformEvent-Class.html#v:performEvent_"><code>performEvent_</code></a>
mediates the interaction between Reflex and side-effecting actions, like our
function that updates the DOM with a rendered graph, so we want to use it to
render a new graph every time the textarea is updated.</p>
<p>An introduction to Reflex is out of scope for this blog post, but it’s worth
mentioning that the textarea value is represented as a
<a href="http://hackage.haskell.org/package/reflex-0.6.2.4/docs/Reflex-Class.html#t:Dynamic"><code>Dynamic</code></a>,
which can change over time and notify consumers when it has changed. This can
be thought of as the combination of a related
<a href="http://hackage.haskell.org/package/reflex-0.6.2.4/docs/Reflex-Class.html#t:Behavior"><code>Behavior</code></a>
and
<a href="http://hackage.haskell.org/package/reflex-0.6.2.4/docs/Reflex-Class.html#t:Event"><code>Event</code></a>.
<code>performEvent_</code> only takes an <code>Event</code>, and we can get the underlying <code>Event</code>
out of a <code>Dynamic</code> with
<a href="http://hackage.haskell.org/package/reflex-0.6.2.4/docs/Reflex-Class.html#v:updated"><code>updated</code></a>.</p>
<p><code>ffor</code> is just <code>flip fmap</code>, and we use it to operate on the underlying <code>Text</code>
value, convert both it and the reference to the element we want to update to
<code>JSVal</code>s, and then pass them as arguments to the <code>viz</code> function we defined
earlier. Now we should have a working GraphViz renderer in our browser!</p>
<h3 id="using-the-ffi-better">Using the FFI better</h3>
<p>We could stop here, but I think we can do better than evaluating JavaScript
strings directly. JSaddle is an EDSL, which means we can rewrite our JavaScript
in Haskell:</p>
<details>
<summary style="cursor: pointer">
<code>Viz.hs</code>
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">module</span><span> </span><span class="dt">Viz</span><span> </span><span class="kw">where</span><span>

</span><span class="kw">import</span><span> </span><span class="dt">Language.Javascript.JSaddle</span><span>

</span><span class="va">viz</span><span> </span><span class="ot">::</span><span> </span><span class="dt">JSVal</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">JSVal</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">JSM</span><span> </span><span class="ot">(</span><span class="ot">)</span><span>
</span><span class="va">viz</span><span> </span><span class="va">element</span><span> </span><span class="va">string</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
  </span><span class="va">viz</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">new</span><span> </span><span class="ot">(</span><span class="va">jsg</span><span> </span><span class="st">&quot;Viz&quot;</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="ot">)</span><span>
  </span><span class="va">render</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">viz</span><span> </span><span class="op">#</span><span> </span><span class="st">&quot;renderSVGElement&quot;</span><span> </span><span class="op">$</span><span> </span><span class="ot">[</span><span class="va">string</span><span class="ot">]</span><span>
  </span><span class="va">result</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">render</span><span> </span><span class="op">#</span><span> </span><span class="st">&quot;then&quot;</span><span> </span><span class="op">$</span><span> </span><span class="ot">[</span><span class="ot">(</span><span class="va">fun</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="ot">_</span><span> </span><span class="ot">_</span><span> </span><span class="ot">[</span><span class="va">e</span><span class="ot">]</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">outer</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">e</span><span> </span><span class="op">!</span><span> </span><span class="st">&quot;outerHTML&quot;</span><span>
    </span><span class="va">element</span><span> </span><span class="op">&lt;#</span><span> </span><span class="st">&quot;innerHTML&quot;</span><span> </span><span class="op">$</span><span> </span><span class="va">outer</span><span>
  </span><span class="ot">)</span><span class="ot">]</span><span>
  </span><span class="va">result</span><span> </span><span class="op">#</span><span> </span><span class="st">&quot;catch&quot;</span><span> </span><span class="op">$</span><span> </span><span class="ot">[</span><span class="ot">(</span><span class="va">fun</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="ot">_</span><span> </span><span class="ot">_</span><span> </span><span class="ot">[</span><span class="va">err</span><span class="ot">]</span><span> </span><span class="ot">-&gt;</span><span>
    </span><span class="va">element</span><span> </span><span class="op">&lt;#</span><span> </span><span class="st">&quot;innerHTML&quot;</span><span> </span><span class="op">$</span><span> </span><span class="va">err</span><span>
  </span><span class="ot">)</span><span class="ot">]</span><span>
  </span><span class="va">pure</span><span> </span><span class="ot">(</span><span class="ot">)</span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/96e0dbda1ba9dc5712342bf1b123fe5d463201d0#file-viz-hs">revision</a>)</em></p>
<p>This is recognisably the same logic as before, using some new JSaddle operators:</p>
<ul>
<li><a href="http://hackage.haskell.org/package/jsaddle-0.9.6.0/docs/Language-Javascript-JSaddle.html#v:-35-"><code>#</code></a>
is for calling a JavaScript function</li>
<li><a href="http://hackage.haskell.org/package/jsaddle-0.9.6.0/docs/Language-Javascript-JSaddle.html#v:-33-"><code>!</code></a>
is for property access</li>
<li><a href="http://hackage.haskell.org/package/jsaddle-0.9.6.0/docs/Language-Javascript-JSaddle.html#v:-60--35-"><code>&lt;#</code></a>
is a setter</li>
</ul>
<p>Note also that all callables take a list of <code>JSVal</code>s as arguments, since
JSaddle doesn’t know how many arguments we intend to pass in advance.</p>
<p>This is an improvement, but we can do even better using the lensy API (after
adding <code>lens</code> to our dependencies):</p>
<details>
<summary style="cursor: pointer">
<code>Viz.hs</code>
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">module</span><span> </span><span class="dt">Viz</span><span> </span><span class="kw">where</span><span>

</span><span class="kw">import</span><span> </span><span class="dt">Language.Javascript.JSaddle</span><span>
</span><span class="kw">import</span><span> </span><span class="dt">Control.Lens</span><span> </span><span class="ot">(</span><span class="ot">(</span><span class="op">^.</span><span class="ot">)</span><span class="ot">)</span><span>

</span><span class="va">viz</span><span> </span><span class="ot">::</span><span> </span><span class="dt">JSVal</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">JSVal</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="dt">JSM</span><span> </span><span class="ot">(</span><span class="ot">)</span><span>
</span><span class="va">viz</span><span> </span><span class="va">element</span><span> </span><span class="va">string</span><span> </span><span class="ot">=</span><span> </span><span class="kw">do</span><span>
  </span><span class="va">viz</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">new</span><span> </span><span class="ot">(</span><span class="va">jsg</span><span> </span><span class="st">&quot;Viz&quot;</span><span class="ot">)</span><span> </span><span class="ot">(</span><span class="ot">)</span><span>
  </span><span class="va">render</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">viz</span><span> </span><span class="op">^.</span><span> </span><span class="va">js1</span><span> </span><span class="st">&quot;renderSVGElement&quot;</span><span> </span><span class="va">string</span><span>
  </span><span class="va">result</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">render</span><span> </span><span class="op">^.</span><span> </span><span class="va">js1</span><span> </span><span class="st">&quot;then&quot;</span><span> </span><span class="ot">(</span><span class="va">fun</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="ot">_</span><span> </span><span class="ot">_</span><span> </span><span class="ot">[</span><span class="va">e</span><span class="ot">]</span><span> </span><span class="ot">-&gt;</span><span> </span><span class="kw">do</span><span>
    </span><span class="va">outer</span><span> </span><span class="ot">&lt;-</span><span> </span><span class="va">e</span><span> </span><span class="op">!</span><span> </span><span class="st">&quot;outerHTML&quot;</span><span>
    </span><span class="va">element</span><span> </span><span class="op">^.</span><span> </span><span class="va">jss</span><span> </span><span class="st">&quot;innerHTML&quot;</span><span> </span><span class="va">outer</span><span class="ot">)</span><span>
  </span><span class="va">result</span><span> </span><span class="op">^.</span><span> </span><span class="va">js1</span><span> </span><span class="st">&quot;catch&quot;</span><span> </span><span class="ot">(</span><span class="va">fun</span><span> </span><span class="op">$</span><span> </span><span class="ot">\</span><span class="ot">_</span><span> </span><span class="ot">_</span><span> </span><span class="ot">[</span><span class="va">err</span><span class="ot">]</span><span> </span><span class="ot">-&gt;</span><span>
    </span><span class="va">element</span><span> </span><span class="op">^.</span><span> </span><span class="va">jss</span><span> </span><span class="st">&quot;innerHTML&quot;</span><span> </span><span class="va">err</span><span class="ot">)</span><span>
  </span><span class="va">pure</span><span> </span><span class="ot">(</span><span class="ot">)</span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/2ede687d9969666897fb1ca944ed83d239b4386b#file-viz-hs">revision</a>)</em></p>
<p>Again, not much has changed except that we can use convenience functions like
<a href="http://hackage.haskell.org/package/jsaddle-0.9.6.0/docs/Language-Javascript-JSaddle.html#v:js1"><code>js1</code></a>
and
<a href="http://hackage.haskell.org/package/jsaddle-0.9.6.0/docs/Language-Javascript-JSaddle.html#v:jss"><code>jss</code></a>.</p>
<p>I’m told that there is some overhead to using JSaddle which it’s possible to
get rid of by using a library like
<a href="https://hackage.haskell.org/package/ghcjs-dom"><code>ghcjs-dom</code></a>, but I haven’t
explored this approach and I will leave this as an exercise for the reader. If
you learn how to do this, please teach me!</p>
<p>Now we are able to run Haskell on the frontend without having to write any
JavaScript ourselves. The final step is to put this on the internet somewhere!</p>
<h3 id="deploying-our-app">Deploying our app</h3>
<p>Building with GHCJS is straightforward:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> nix-build <span class="at">-A</span> ghcjs.small-viz</span></code></pre></div>
<p>I’m enamoured of the idea of deploying this to <a href="https://glitch.com/">Glitch</a>,
so let’s look into doing that. The <code>index.html</code> created by the default GHCJS
build is unnecessary, and we can simplify it:</p>
<details>
<summary style="cursor: pointer">
<code>index.html</code>
</summary>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;!DOCTYPE</span> html<span class="dt">&gt;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">html</span><span class="dt">&gt;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&lt;</span><span class="kw">head</span><span class="dt">&gt;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&lt;</span><span class="kw">script</span><span class="ot"> language</span><span class="op">=</span><span class="st">&quot;javascript&quot;</span><span class="ot"> src</span><span class="op">=</span><span class="st">&quot;all.js&quot;</span><span class="dt">&gt;&lt;/</span><span class="kw">script</span><span class="dt">&gt;</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&lt;/</span><span class="kw">head</span><span class="dt">&gt;</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&lt;</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&lt;/</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">html</span><span class="dt">&gt;</span></span></code></pre></div>
</details>
<p>The only JavaScript file that needs to be copied over is then <code>all.js</code>. We can
write a <code>glitch.nix</code> file to simplify this process:</p>
<details>
<summary style="cursor: pointer">
<code>glitch.nix</code>
</summary>
<div class="sourceCode" id="cb11"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>  <span class="co"># ./updater versions.json reflex-platform</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>  <span class="va">fetcher</span> <span class="op">=</span> <span class="op">{</span> <span class="va">owner</span><span class="op">,</span> <span class="va">repo</span><span class="op">,</span> <span class="va">rev</span><span class="op">,</span> <span class="va">sha256</span><span class="op">,</span> <span class="op">...</span> <span class="op">}</span>: <span class="bu">builtins</span><span class="op">.</span>fetchTarball <span class="op">{</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">inherit</span> <span class="va">sha256</span><span class="op">;</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>    <span class="va">url</span> <span class="op">=</span> <span class="st">&quot;https://github.com/</span><span class="sc">${</span>owner<span class="sc">}</span><span class="st">/</span><span class="sc">${</span>repo<span class="sc">}</span><span class="st">/tarball/</span><span class="sc">${</span>rev<span class="sc">}</span><span class="st">&quot;</span><span class="op">;</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>  <span class="va">reflex-platform</span> <span class="op">=</span> fetcher <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>fromJSON <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>readFile <span class="ss">./versions.json</span><span class="op">)).</span>reflex<span class="op">-</span>platform<span class="op">;</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>  <span class="va">pkgs</span> <span class="op">=</span> <span class="op">(</span><span class="bu">import</span> reflex<span class="op">-</span>platform <span class="op">{}).</span>nixpkgs<span class="op">;</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>  <span class="va">project</span> <span class="op">=</span> <span class="bu">import</span> <span class="ss">./default.nix</span><span class="op">;</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>  <span class="va">html</span> <span class="op">=</span> pkgs<span class="op">.</span>writeText <span class="st">&quot;index.html&quot;</span> <span class="st">&#39;&#39;</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a><span class="st">    &lt;!DOCTYPE html&gt;</span></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a><span class="st">    &lt;html&gt;</span></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a><span class="st">      &lt;head&gt;</span></span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a><span class="st">        &lt;script language=&quot;javascript&quot; src=&quot;all.js&quot;&gt;&lt;/script&gt;</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a><span class="st">      &lt;/head&gt;</span></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a><span class="st">      &lt;body&gt;</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a><span class="st">      &lt;/body&gt;</span></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a><span class="st">    &lt;/html&gt;</span></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a><span class="st">  &#39;&#39;</span><span class="op">;</span></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> pkgs<span class="op">.</span>runCommand <span class="st">&quot;glitch&quot;</span> <span class="op">{}</span> <span class="st">&#39;&#39;</span></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a><span class="st">  mkdir -p $out</span></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a><span class="st">  cp </span><span class="sc">${</span>html<span class="sc">}</span><span class="st"> $out/index.html</span></span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true" tabindex="-1"></a><span class="st">  cp </span><span class="sc">${</span>project<span class="op">.</span>ghcjs<span class="op">.</span>small<span class="op">-</span>viz<span class="sc">}</span><span class="st">/bin/small-viz.jsexe/all.js $out/all.js</span></span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true" tabindex="-1"></a><span class="st">&#39;&#39;</span></span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/a0127badaee44f316156121153c0e4bc41af9460#file-glitch-nix">revision</a>)</em></p>
<p>And then produce the files we need to copy over with:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> nix-build glitch.nix</span></code></pre></div>
<p>I’ve gone ahead and done this, and it’s up on
<a href="https://small-viz.glitch.me/">small-viz.glitch.me/</a>.</p>
<p>Now that everything’s working, it would be nice to reduce the size of <code>all.js</code>,
which is currently over 5MB. Obelisk uses the <a href="https://developers.google.com/closure/compiler">Closure
Compiler</a> to minify JavaScript,
and we can adapt <a href="https://github.com/obsidiansystems/obelisk/blob/071e2edb92e623b4415fb6deedc4219ad1f829f0/default.nix#L147">what it
does</a>
and <a href="https://github.com/tomsmalley/marking/blob/a522b8c75a96146883a7e32acf5b17bb5f4abf1b/makefile#L5-L10">another example by Tom
Smalley</a>
that I found when I was looking into this to update <code>glitch.nix</code>:</p>
<details>
<summary style="cursor: pointer">
<code>glitch.nix</code>
</summary>
<div class="sourceCode" id="cb13"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>  <span class="co"># ./updater versions.json reflex-platform</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>  <span class="va">fetcher</span> <span class="op">=</span> <span class="op">{</span> <span class="va">owner</span><span class="op">,</span> <span class="va">repo</span><span class="op">,</span> <span class="va">rev</span><span class="op">,</span> <span class="va">sha256</span><span class="op">,</span> <span class="op">...</span> <span class="op">}</span>: <span class="bu">builtins</span><span class="op">.</span>fetchTarball <span class="op">{</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">inherit</span> <span class="va">sha256</span><span class="op">;</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>    <span class="va">url</span> <span class="op">=</span> <span class="st">&quot;https://github.com/</span><span class="sc">${</span>owner<span class="sc">}</span><span class="st">/</span><span class="sc">${</span>repo<span class="sc">}</span><span class="st">/tarball/</span><span class="sc">${</span>rev<span class="sc">}</span><span class="st">&quot;</span><span class="op">;</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>  <span class="va">reflex-platform</span> <span class="op">=</span> fetcher <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>fromJSON <span class="op">(</span><span class="bu">builtins</span><span class="op">.</span>readFile <span class="ss">./versions.json</span><span class="op">)).</span>reflex<span class="op">-</span>platform<span class="op">;</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>  <span class="va">pkgs</span> <span class="op">=</span> <span class="op">(</span><span class="bu">import</span> reflex<span class="op">-</span>platform <span class="op">{}).</span>nixpkgs<span class="op">;</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>  <span class="va">project</span> <span class="op">=</span> <span class="bu">import</span> <span class="ss">./default.nix</span><span class="op">;</span></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a>  <span class="va">html</span> <span class="op">=</span> pkgs<span class="op">.</span>writeText <span class="st">&quot;index.html&quot;</span> <span class="st">&#39;&#39;</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a><span class="st">    &lt;!DOCTYPE html&gt;</span></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a><span class="st">    &lt;html&gt;</span></span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a><span class="st">      &lt;head&gt;</span></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a><span class="st">        &lt;script language=&quot;javascript&quot; src=&quot;all.js&quot;&gt;&lt;/script&gt;</span></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a><span class="st">      &lt;/head&gt;</span></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a><span class="st">      &lt;body&gt;</span></span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a><span class="st">      &lt;/body&gt;</span></span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a><span class="st">    &lt;/html&gt;</span></span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true" tabindex="-1"></a><span class="st">  &#39;&#39;</span><span class="op">;</span></span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> pkgs<span class="op">.</span>runCommand <span class="st">&quot;glitch&quot;</span> <span class="op">{}</span> <span class="st">&#39;&#39;</span></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true" tabindex="-1"></a><span class="st">  mkdir -p $out</span></span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true" tabindex="-1"></a><span class="st">  cp </span><span class="sc">${</span>html<span class="sc">}</span><span class="st"> $out/index.html</span></span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true" tabindex="-1"></a><span class="st">  </span><span class="sc">${</span>pkgs<span class="op">.</span>closurecompiler<span class="sc">}</span><span class="st">/bin/closure-compiler \</span></span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true" tabindex="-1"></a><span class="st">    --externs=</span><span class="sc">${</span>project<span class="op">.</span>ghcjs<span class="op">.</span>small<span class="op">-</span>viz<span class="sc">}</span><span class="st">/bin/small-viz.jsexe/all.js.externs \</span></span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true" tabindex="-1"></a><span class="st">    --jscomp_off=checkVars \</span></span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true" tabindex="-1"></a><span class="st">    --js_output_file=&quot;$out/all.js&quot; \</span></span>
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true" tabindex="-1"></a><span class="st">    -O ADVANCED \</span></span>
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true" tabindex="-1"></a><span class="st">    -W QUIET \</span></span>
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true" tabindex="-1"></a><span class="st">    </span><span class="sc">${</span>project<span class="op">.</span>ghcjs<span class="op">.</span>small<span class="op">-</span>viz<span class="sc">}</span><span class="st">/bin/small-viz.jsexe/all.js</span></span>
<span id="cb13-30"><a href="#cb13-30" aria-hidden="true" tabindex="-1"></a><span class="st">&#39;&#39;</span></span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/aac5fe1258ccfc8c9b8ca685b9db1a4f538ae183#file-glitch-nix">revision</a>)</em></p>
<p>And this brings the size down to under 2MB.</p>
<p><a href="https://github.com/tomsmalley">Tom Smalley</a> points out that there is even a
<code>-dedupe</code> flag that GHCJS accepts, and although I couldn’t find good
documentation for this (beyond
<a href="https://www.reddit.com/r/haskell/comments/54knub/ghcjs_dedupe/">a Reddit post</a>), it
does get the filesize down to 1MB:</p>
<details>
<summary style="cursor: pointer">
<code>small-viz.cabal</code>
</summary>
<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="va">cabal</span><span class="op">-</span><span class="va">version</span><span class="ot">:</span><span>       </span><span class="op">&gt;=</span><span class="dv">1.10</span><span>
</span><span class="co">-- Initial package description &#39;small-viz.cabal&#39; generated by &#39;cabal init&#39;.</span><span>
</span><span class="co">--   For further documentation, see http://haskell.org/cabal/users-guide/</span><span>

</span><span class="va">name</span><span class="ot">:</span><span>                </span><span class="va">small</span><span class="op">-</span><span class="va">viz</span><span>
</span><span class="va">version</span><span class="ot">:</span><span>             </span><span class="dv">0.1</span><span class="op">.</span><span class="dv">0.0</span><span>
</span><span class="co">-- synopsis:</span><span>
</span><span class="co">-- description:</span><span>
</span><span class="co">-- bug-reports:</span><span>
</span><span class="va">license</span><span class="ot">:</span><span>             </span><span class="dt">BSD3</span><span>
</span><span class="va">license</span><span class="op">-</span><span class="va">file</span><span class="ot">:</span><span>        </span><span class="dt">LICENSE</span><span>
</span><span class="va">author</span><span class="ot">:</span><span>              </span><span class="dt">Vaibhav</span><span> </span><span class="dt">Sagar</span><span>
</span><span class="va">maintainer</span><span class="ot">:</span><span>          </span><span class="va">vaibhavsagar</span><span class="ot">@</span><span class="va">gmail</span><span class="op">.</span><span class="va">com</span><span>
</span><span class="co">-- copyright:</span><span>
</span><span class="co">-- category:</span><span>
</span><span class="va">build</span><span class="op">-</span><span class="kw">type</span><span class="ot">:</span><span>          </span><span class="dt">Simple</span><span>
</span><span class="va">extra</span><span class="op">-</span><span class="va">source</span><span class="op">-</span><span class="va">files</span><span class="ot">:</span><span>  </span><span class="va">CHANGELOG.md</span><span>

</span><span class="va">executable</span><span> </span><span class="va">small</span><span class="op">-</span><span class="va">viz</span><span>
  </span><span class="va">main</span><span class="op">-</span><span class="va">is</span><span class="ot">:</span><span>             </span><span class="va">Main.hs</span><span>
  </span><span class="va">other</span><span class="op">-</span><span class="va">modules</span><span class="ot">:</span><span>       </span><span class="dt">Viz</span><span>
  </span><span class="co">-- other-extensions:</span><span>
  </span><span class="va">build</span><span class="op">-</span><span class="va">depends</span><span class="ot">:</span><span>       </span><span class="va">base</span><span> </span><span class="op">&gt;=</span><span class="dv">4.12</span><span> </span><span class="op">&amp;&amp;</span><span> </span><span class="op">&lt;</span><span class="dv">4.13</span><span>
                     </span><span class="ot">,</span><span> </span><span class="va">lens</span><span>
                     </span><span class="ot">,</span><span> </span><span class="va">jsaddle</span><span>
                     </span><span class="ot">,</span><span> </span><span class="va">reflex</span><span>
                     </span><span class="ot">,</span><span> </span><span class="va">reflex</span><span class="op">-</span><span class="va">dom</span><span>
  </span><span class="co">-- hs-source-dirs:</span><span>
  </span><span class="kw">default</span><span class="op">-</span><span class="va">language</span><span class="ot">:</span><span>    </span><span class="dt">Haskell2010</span><span>
  </span><span class="kw">if</span><span> </span><span class="va">impl</span><span class="ot">(</span><span class="va">ghcjs</span><span class="ot">)</span><span>
    </span><span class="va">ghc</span><span class="op">-</span><span class="va">options</span><span class="ot">:</span><span> </span><span class="op">-</span><span class="va">dedupe</span></code></pre></div>
</details>
<p><em>(<a href="https://gist.github.com/vaibhavsagar/24b1754b8a269fd8c54a89cb73e64fa8/28a629d19e14c9d55b950b5629761818d38a9881#file-small-viz-cabal">revision</a>)</em></p>
<p>I think this is a good stopping point. We’ve:</p>
<ol type="1">
<li>Built a frontend-only Reflex app</li>
<li>Integrated with a JavaScript library</li>
<li>Used the JSaddle FFI idiomatically</li>
<li>Deployed to Glitch</li>
</ol>
<p>and I hope I’ve convinced you to take a closer look at Haskell the next time
you want to write something that runs in the browser.</p>
<p><em>Thanks to <a href="https://github.com/ali-abrar">Ali Abrar</a>, <a href="https://twitter.com/itsfarseen">Farseen Abdul
Salam</a>, and <a href="https://github.com/tomsmalley">Tom
Smalley</a> for comments and feedback.</em></p>
</summary>
</entry>
<entry>
    <title>You Won’t Believe This One Weird CPU Instruction!</title>
    <link href="https://vaibhavsagar.com/blog/2019/09/08/popcount/" />
    <id>https://vaibhavsagar.com/blog/2019/09/08/popcount/index.html</id>
    <published>2019-09-08</published>
    <updated>2019-09-08T00:00:00Z</updated>
    <summary type="html"><div class="info">
    Posted on  8 September 2019
    
</div>
<div class="info">
    
        Tags: <a title="All pages tagged &#39;programming&#39;." href="/blog/tags/programming/index.html" rel="tag">programming</a>
    
</div>

<p><em>Translated to <a href="https://pngset.com/ru-weird-cpu-instruction">Russian</a> by <a href="https://pngset.com/">Babur
Muradov</a> and <a href="https://pngflare.com/uz-popcount">Uzbek</a> by
<a href="https://pngflare.com/">Leonid Popov</a>.</em></p>
<p><em>This is a pseudo-transcript of <a href="https://www.youtube.com/watch?v=bLFqLfz2Fmc">a presentation I did at !!Con
2019</a>.</em></p>
<p>Most CPU architectures in use today have an instruction called <code>popcount</code>,
short for “population count”. Here’s what it does: it counts the number of set
bits in a machine word. For example (assuming 8-bit words for simplicity),
<code>popcount(00100110)</code> is <code>3</code> and <code>popcount(01100000)</code> is <code>2</code>.</p>
<p>You might be wondering, like I was, if there’s more to this instruction, but
that’s all it does! This doesn’t seem very useful, right?</p>
<p>I thought this might be a recent addition for some hyperspecialised use case,
but it has in fact been present in CPU architectures since at least 1961:</p>
<ul>
<li>1961: <a href="https://en.wikipedia.org/wiki/IBM_7030_Stretch">IBM Stretch</a></li>
<li>1964: <a href="https://en.wikipedia.org/wiki/CDC_6000_series">CDC 6000</a></li>
<li>1975: <a href="https://en.wikipedia.org/wiki/Cray-1">Cray-1</a></li>
<li>2005: <a href="https://en.wikipedia.org/wiki/SPARC">SPARC</a></li>
<li>2005: <a href="https://en.wikipedia.org/wiki/ARM_architecture#Advanced_SIMD_(NEON)">ARM NEON</a></li>
<li>2007: <a href="https://en.wikipedia.org/wiki/AMD_10h">AMD K10</a></li>
<li>2008: <a href="https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)">Intel Nehalem</a></li>
</ul>
<p>So what’s going on?</p>
<h4 id="the-nsa-instruction">The NSA Instruction</h4>
<p><code>popcount</code> is also known as “The NSA Instruction”, and a <a href="https://groups.google.com/forum/#!msg/comp.arch/UXEi7G6WHuU/Z2z7fC7Xhr8J">very entertaining
thread on
<code>comp.arch</code></a>
discusses its uses inside and outside cryptography. It is rumoured that it was
originally added to CPU instructions at the behest of the NSA. As <a href="http://cryptome.org/jya/sadd.htm">this
archived email thread</a> puts it:</p>
<blockquote>
<p>It was almost a tradition that one of the first of any new faster CDC machine
was delivered to a “good customer” - picked up at the factory by an anonymous
truck, and never heard from again.</p>
</blockquote>
<p>This makes for a great story, but what were they using it for?</p>
<p>One measure of information content is the <a href="https://en.wikipedia.org/wiki/Hamming_weight">Hamming
weight</a>, which is the number of
symbols in a string that are different from the zero-symbol of the alphabet.
For a binary string, this is exactly <code>popcount</code>!</p>
<p><a href="http://www.talkchess.com/forum3/viewtopic.php?t=38521">As explained here</a>, the
NSA wanted to do cryptanalysis on intercepted messages, and since the CDC 6000
had 60-bit words, one word was enough to store most alphabets they were
interested in. They were able to:</p>
<ol type="1">
<li>Split a message into lines</li>
<li>Set a bit for each unique character they encountered per line</li>
<li>Use <code>popcount</code> to count the distinct characters</li>
<li>Use the count as a hash for further cryptanalysis</li>
</ol>
<p>Curiously, <code>popcount</code> seems to have disappeared from instruction sets between
the mid-1970s and the mid-2000s, so there has to be more to it than
cryptographic applications to explain its return. What else can it be used for?</p>
<h4 id="error-correction">Error Correction</h4>
<p>Related to the concept of Hamming weight is <a href="https://en.wikipedia.org/wiki/Hamming_distance">Hamming
distance</a>, which is the number
of differing positions between two strings of identical length. For two binary
strings <code>x</code> and <code>y</code>, this is just the <code>popcount</code> of them XORed together. For
example:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>00100110</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>01100000 ^</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>--------</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>01000110</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>popcount(01000110) = 3</span></code></pre></div>
<p>For telecommunications applications, this helps us calculate the signal
distance, where a known word is sent over the wire and the number of flipped
bits are counted to provide an estimate of the error introduced by transmission.</p>
<p>We can then design an <a href="https://en.wikipedia.org/wiki/Hamming_distance#Error_detection_and_error_correction">error-correcting
code</a>
accordingly, e.g. if we want to be robust against up to 2 flipped bits, our
code words need to differ in Hamming distance by at least 5.</p>
<h4 id="binary-convolutional-neural-networks">Binary Convolutional Neural Networks</h4>
<p>And now for something completely different: binary convolutional neural
networks! But first, what are they?</p>
<ul>
<li>Binary means that we’re using matrices consisting of only the values +1 (coded
as <code>1</code>) and -1 (coded as <code>0</code>), as opposed to 32-bit floating-point values.</li>
<li>Convolutional means matrix multiplication is involved?</li>
<li>Neural networks are systems inspired by animal brains (I’m a bit hazy on
this part).</li>
</ul>
<p>In summary, we have to do binary matrix multiplication. But what’s special
about binary matrices?</p>
<p>Ordinary matrix multiplication on 32-bit values is a good fit on desktop
computers with powerful CPUs and GPUs, but increasingly we also want to do
useful work on smaller and simpler devices, such as smartphones, routers,
smartwatches, etc. We can decompose these more complex matrices into layers of
binary matrices, and these resulting matrices are so much easier to store and
operate on that we are better off even though there are more layers.</p>
<p>Where does <code>popcount</code> come into play? It’s used to calculate the dot product of
two binary matrices:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>a = xnor(x, y)</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>b = popcount(a)</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>c = len(a)</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>dot(x, y) = 2 × b − c</span></code></pre></div>
<p>More details are available
<a href="https://sushscience.wordpress.com/2017/10/01/understanding-binary-neural-networks/">here</a>
and
<a href="https://developer.apple.com/documentation/metalperformanceshaders/mpscnnbinaryconvolution">here</a>.</p>
<h4 id="chess-programming">Chess Programming</h4>
<p>Many chess programs store data using a
<a href="https://www.chessprogramming.org/Bitboards">bitboard</a> representation, which
conveniently fits into a 64-bit word. <a href="https://www.chessprogramming.org/Population_Count">Population
Count</a> has been used to
perform meaningful operations with this representation, such as calculating the
<a href="https://www.chessprogramming.org/Mobility#Mobility_with_Bitboards">mobility</a>
of a piece.</p>
<h4 id="molecular-fingerprinting">Molecular Fingerprinting</h4>
<p>This is related to the notion of Hamming distance above: molecules are hashed
in some way and compared (with <code>popcount</code>) to determine how similar they are.
More details on that
<a href="http://www.dalkescientific.com/writings/diary/archive/2008/06/26/fingerprint_background.html">here</a>.</p>
<h4 id="hash-array-mapped-tries">Hash Array Mapped Tries</h4>
<p>This is where I first learned about <code>popcount</code>! The HAMT is a data structure
(<a href="https://lampwww.epfl.ch/papers/idealhashtrees.pdf">pioneered by Phil
Bagwell</a>) that can store a
very large number of values (usually 32 or 64) in an array at each node of the
trie. However, allocating memory for a 32 or 64-element array every time can be
incredibly wasteful, especially if the array only actually contains a handful
of elements. The solution is to add a bitmask in which the number of bits that
are set corresponds to the number of elements in the array, which allows the
array to grow and shrink as required. Calculating the index for a given element
efficiently can then be done using <code>popcount</code>. You can learn more about how
they work from <a href="/blog/2018/07/29/hamts-from-scratch/">this blog post</a>, where I
implement them myself.</p>
<h4 id="succinct-data-structures">Succinct Data Structures</h4>
<p>This is an exciting new area of research that focuses on how to store data in
as little space as possible, without having to decompress it in order to do
useful work. One technique is to think in terms of arrays of bits (bitvectors), which can be
queried using two operations:</p>
<ul>
<li><code>rank(i)</code> counts the number of bits set upto the <code>i</code>th index in the bitvector</li>
<li><code>select(i)</code> finds the index where the <code>i</code>th ranked bit is set</li>
</ul>
<p>Making these operations efficient on large bitvectors requires constructing an
index and using it effectively, both involving <code>popcount</code>. There’s <a href="https://alexbowe.com/rrr/">a good
overview of the RRR index here</a>, and as far as I can
tell the current state-of-the-art approach is described in <a href="http://www.cs.cmu.edu/~./dga/papers/zhou-sea2013.pdf">Space-Efficient,
High-Performance Rank &amp; Select Structures on Uncompressed Bit
Sequences</a>.</p>
<h4 id="compiler-optimisations">Compiler Optimisations</h4>
<p><code>popcount</code> has become so pervasive that both
<a href="https://godbolt.org/z/JUzmD8">GCC</a> and <a href="https://godbolt.org/z/AVqMGl">Clang</a>
will detect an implementation of <code>popcount</code> and replace it with the built-in
instruction. Imagine Clippy going “I see you are trying to implement
<code>popcount</code>, let me go ahead and fix that for you”! The relevant LLVM code is
<a href="https://github.com/llvm-mirror/llvm/blob/f36485f7ac2a8d72ad0e0f2134c17fd365272285/lib/Transforms/Scalar/LoopIdiomRecognize.cpp#L960">here</a>.
Daniel Lemire points to this as an example of <a href="https://lemire.me/blog/2016/05/23/the-surprising-cleverness-of-modern-compilers/">the surprising cleverness of
modern
compilers</a>.</p>
<h4 id="conclusion">Conclusion</h4>
<p>From beginnings shrouded in mystery, <code>popcount</code> has emerged as a generally
useful, if slightly unusual, CPU instruction. I love how it ties together such
different fields of computing, and I wonder how many other similarly weird
instructions are out there. If you have a favourite, I’d love to hear about it!</p>
</summary>
</entry>

</feed>
