<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <id>https://wickstrom.tech/</id>
 <title>Oskar Wickström</title>
 <subtitle>Software design, testing, functional programming, and other delightful things.</subtitle>
 <link href="https://wickstrom.tech/feed.xml" rel="self"/>
 <link href="https://wickstrom.tech/"/>
 <updated>2026-03-09T14:09:42+01:00</updated>
 <author><name>Oskar Wickström</name></author>
  <entry>
    <id>https://wickstrom.tech/2026-01-28-there-and-back-again-from-quickstrom-to-bombadil.html</id>
    <title>There and Back Again: From Quickstrom to Bombadil</title>
    <link href="https://wickstrom.tech/2026-01-28-there-and-back-again-from-quickstrom-to-bombadil.html"/>
    <published>2026-01-28T00:00:00+01:00</published>
    <updated>2026-01-28T00:00:00+01:00</updated>
    <summary>Today I’m announcing and open-sourcing the Bombadil project — a brand
new property-based browser testing framework. I started working on this
when joining Antithesis two months ago. While still in its infancy,
we’ve decided to build it in the open and share our progress from the
start.</summary>
    <content type="html"><![CDATA[
<p>Today I’m announcing and open-sourcing the <a href="https://github.com/antithesishq/bombadil">Bombadil</a> project — a brand new property-based browser testing framework. I started working on this when joining Antithesis two months ago. While still in its infancy, we’ve decided to build it in the open and share our progress from the start.</p> <p>We decided on the name Bombadil last week. A few days later, this exploded:</p> <blockquote class="twitter-tweet"> <p lang="en" dir="ltr"> It's going to be tough for startups when all the Lord of the Rings names are taken and the only thing left is something like Bombadil AI. </p> — Patrick Collison (<span class="citation" data-cites="patrickc">@patrickc</span>) <a href="https://twitter.com/patrickc/status/2015562569105465347?ref_src=twsrc%5Etfw">January 25, 2026</a> </blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <p>While <a href="https://github.com/quickstrom/quickstrom">Quickstrom</a> proved its worth, <a href="https://dl.acm.org/doi/10.1145/3519939.3523728">finding bugs in more than half of the TodoMVC</a> apps, Bombadil aims to improve on its shortcomings while also envisioning a more ambitious future for generative testing of web apps: faster and smarter state space exploration, a modern and usable specification language, better tools for reproducing and debugging test failures, and a better distribution story.</p> <p>I consider Bombadil the successor of Quickstrom. After years of trying to sustain Quickstrom through various models, I now have a much better answer: building it at Antithesis, making something valuable that is open and free to use, while also strengthening the company’s commercial offering. Bombadil can be used locally or in CI to test your web apps early. Power users can take it further and run Bombadil within Antithesis and its deterministic hypervisor. That gives you perfect reproductions of failed tests. You can even combine Bombadil with other workloads in Antithesis and test your entire stack deterministically. Isn’t that the holy grail of testing?</p> <p>Bombadil is built from scratch with a focus on accessibility: a new specification language and better tooling for writing and maintaining specs. Right now, I’m working on a specification DSL in TypeScript. It’s based on <a href="https://en.wikipedia.org/wiki/Linear_temporal_logic">linear temporal logic</a>, just as Quickstrom, but aims to be a lot more ergonomic. Here’s an example that verifies that error messages eventually disappear:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> errorMessage <span class="op">=</span> <span class="fu">extract</span>(</span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> (state) <span class="kw">=&gt;</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> state<span class="op">.</span><span class="at">document</span><span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="fu">querySelector</span>(<span class="st">&quot;.error&quot;</span>)</span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="op">?.</span><span class="at">textContent</span> </span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="op">??</span> <span class="kw">null</span></span> <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>)<span class="op">;</span></span> <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="kw">const</span> errorEventuallyDisappears <span class="op">=</span> <span class="fu">always</span>(</span> <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> <span class="fu">condition</span>(() <span class="kw">=&gt;</span> errorMessage <span class="op">!==</span> <span class="kw">null</span>)<span class="op">.</span><span class="fu">implies</span>(</span> <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> <span class="fu">eventually</span>(() <span class="kw">=&gt;</span> errorMessage <span class="op">===</span> <span class="kw">null</span>)</span> <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">within</span>(<span class="dv">5</span><span class="op">,</span> <span class="st">&quot;seconds&quot;</span>)</span> <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>)<span class="op">;</span></span></code></pre></div> <p>Both the original hacky PureScript DSL and the bespoke language <em>Specstrom</em> were huge obstacles to adoption. Today, TypeScript is a widely adopted language among quality-minded web developers, and if you’re not into that, Bombadil will work with plain JavaScript too.</p> <p>We’re writing Bombadil in Rust, leveraging its excellent ecosystem to ship a single statically-linked executable — download for your platform, point it at a Chromium-based browser, and off you go!</p> <p>Bombadil is early but usable. Check it out on <a href="https://github.com/antithesishq/bombadil">GitHub</a>, try it on your projects, and let us know what breaks. Also let us know what <em>it</em> breaks in your systems! Join us on <a href="https://discord.gg/antithesis">Discord</a> for help and discussion, or follow development on <a href="https://x.com/owickstrom">Twitter/X</a>. This is just the beginning — we’re actively seeking feedback and early adopters to help shape where this goes.</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2025-11-01-error-reporting-linear-temporal-logic.html</id>
    <title>Computer Says No: Error Reporting for LTL</title>
    <link href="https://wickstrom.tech/2025-11-01-error-reporting-linear-temporal-logic.html"/>
    <published>2025-11-01T00:00:00+01:00</published>
    <updated>2025-11-01T00:00:00+01:00</updated>
    <summary>Quickstrom is a property-based testing tool for web applications, using
QuickLTL for specifying the intended behavior. QuickLTL is a linear
temporal logic (LTL) over finite traces, especially suited for testing.
As with many other logic systems, when a formula evaluates to false —
like when a counterexample to a safety property is found or a liveness
property cannot be shown to hold — the computer says no. That is, you
get “false” or “test failed”, perhaps along with a trace. Understanding
complex bugs in stateful systems then comes down to staring at the
specification alongside the trace, hoping you can somehow pin down what
went wrong. It’s not great.</summary>
    <content type="html"><![CDATA[
<p><a href="https://quickstrom.io/">Quickstrom</a> is a property-based testing tool for web applications, using <a href="https://arxiv.org/abs/2203.11532">QuickLTL</a> for specifying the intended behavior. QuickLTL is a linear temporal logic (LTL) over finite traces, especially suited for testing. As with many other logic systems, when a formula evaluates to false — like when a counterexample to a safety property is found or a liveness property cannot be shown to hold — the computer says no. That is, you get “false” or “test failed”, perhaps along with a trace. Understanding complex bugs in stateful systems then comes down to staring at the specification alongside the trace, hoping you can somehow pin down what went wrong. It’s not great.</p> <p>Instead, we should have helpful error messages explaining <em>why</em> a property does not hold; which parts of the specification failed and which concrete values from the trace were involved. Not <code>false</code>, <code>unsat</code>, or even <code>assertion error: x != y</code>. We should get the full story. I started exploring this space a few years ago when I worked actively on Quickstrom, but for some reason it went on the shelf half-finished. Time to tie up the loose ends!</p> <p>The starting point was <em>Picostrom</em>, a minimal Haskell version of the checker in Quickstrom, and <a href="https://www.cs.cmu.edu/~cchristo/docs/jaspan-ASE08.pdf">Error Reporting Logic</a> (ERL), a paper introducing a way of rendering natural-language messages to explain propositional logic counterexamples. I ported it to Rust mostly to see what it turned into, and extended it with error reporting supporting temporal operators. The code is available at <a href="https://codeberg.org/owi/picostrom-rs">codeberg.org/owi/picostrom-rs</a> under the MIT license.</p> <p>Between the start of my work and picking it back up now, <a href="https://doi.org/10.4230/OASIcs.SLATE.2024.11">A Language for Explaining Counterexamples</a> was published, which looks closely related, although it’s focused on model checking with CTL. If you’re interested in other related work, check out <a href="https://arxiv.org/abs/2201.03061">A Systematic Literature Review on Counterexample Explanation in Model Checking</a>.</p> <p>All right, let’s dive in!</p> <h2 id="quickltl-and-picostrom">QuickLTL and Picostrom</h2> <p>A quick recap on QuickLTL is in order before we go into the Picostrom code. QuickLTL operates on <em>finite</em> traces, making it suitable for testing. It’s a four-valued logic, meaning that a formula evaluates to one of these values:</p> <ul> <li><span class="math inline">definitely true</span></li> <li><span class="math inline">definitely false</span></li> <li><span class="math inline">probably true</span></li> <li><span class="math inline">probably false</span></li> </ul> <p>It extends propositional logic with temporal operators, much like LTL:</p> <dl> <dt><span class="math inline">next<sub><em>d</em></sub>(<em>P</em>)</span></dt> <dd> <p><span class="math inline"><em>P</em></span> must hold in the next state, demanding a next state is available. This <em>forces</em> the evaluator to draw a next state.</p> </dd> <dt><span class="math inline">next<sub><em>f</em></sub>(<em>P</em>)</span></dt> <dd> <p><span class="math inline"><em>P</em></span> must hold in the next state, defaulting to <span class="math inline">definitely false</span> if no next state is available.</p> </dd> <dt><span class="math inline">next<sub><em>t</em></sub>(<em>P</em>)</span></dt> <dd> <p><span class="math inline"><em>P</em></span> must hold in the next state, defaulting to <span class="math inline">probably true</span> if no next state is available.</p> </dd> <dt><span class="math inline">eventually<sub><em>N</em></sub>(<em>P</em>)</span></dt> <dd> <p><span class="math inline"><em>P</em></span> must hold in the current or a future state. It demands at least <span class="math inline"><em>N</em></span> states, evaluating on all available states, finally defaulting to <span class="math inline">probably false</span>.</p> </dd> <dt><span class="math inline">always<sub><em>N</em></sub>(<em>P</em>)</span></dt> <dd> <p><span class="math inline"><em>P</em></span> must hold in the current and all future states. It demands at least <span class="math inline"><em>N</em></span> states, evaluating on all available states, finally defaulting to <span class="math inline">probably true</span>.</p> </dd> </dl> <p>You can think of <span class="math inline">eventually<sub><em>N</em></sub>(<em>P</em>)</span> as unfolding into a sequence of <span class="math inline"><em>N</em></span> nested <span class="math inline">next<sub><em>d</em></sub></span>, wrapping an infinite sequence of <span class="math inline">next<sub><em>f</em></sub></span>, all connected by <span class="math inline">∨</span>. Let’s define that inductively with a coinductive base case:</p> <p><span class="math display">$$ \begin{align} \text{eventually}_0(P) &amp; = P \lor \text{next}_F(\text{eventually}_0(P)) \\ \text{eventually}_(N + 1)(P) &amp; = P \lor \text{next}_D(\text{eventually}_N(P)) \\ \end{align} $$</span></p> <p>And similarly, <span class="math inline">always<sub><em>N</em></sub>(<em>P</em>)</span> can be defined as:</p> <p><span class="math display">$$ \begin{align} \text{always}_0(P) &amp; = P \land \text{next}_T(\text{always}_0(P)) \\ \text{always}_(N + 1)(P) &amp; = P \land \text{next}_D(\text{always}_N(P)) \\ \end{align} $$</span></p> <p>This is essentially how the evaluator expands these temporal operators, but for error reporting reasons, not exactly.</p> <p>Finally, there are <em>atoms</em>, which are domain-specific expressions embedded in the AST, evaluating to <span class="math inline">⊤</span> or <span class="math inline">⊥</span>. The AST is parameterized on the atom type, so you can plug in an atom language of choice. An atom type must implement the <code>Atom</code> trait, which in simplified form looks like this:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">trait</span> Atom <span class="op">{</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">type</span> State<span class="op">;</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> eval(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> state<span class="op">:</span> <span class="op">&amp;</span><span class="dt">Self</span><span class="pp">::</span>State) <span class="op">-&gt;</span> <span class="dt">bool</span><span class="op">;</span></span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> render(</span> <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> </span> <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> mode<span class="op">:</span> TextMode<span class="op">,</span> </span> <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> negated<span class="op">:</span> <span class="dt">bool</span><span class="op">,</span></span> <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> ) <span class="op">-&gt;</span> <span class="dt">String</span><span class="op">;</span></span> <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> render_actual(</span> <span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> </span> <span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> negated<span class="op">:</span> <span class="dt">bool</span><span class="op">,</span> </span> <span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> state<span class="op">:</span> <span class="op">&amp;</span><span class="dt">Self</span><span class="pp">::</span>State<span class="op">,</span></span> <span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> ) <span class="op">-&gt;</span> <span class="dt">String</span><span class="op">;</span></span> <span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <p>For testing the checker, and for this blog post, I’m using the following atom type:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> TestAtom <span class="op">{</span></span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> Literal(<span class="dt">u64</span>)<span class="op">,</span></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> Select(Identifier)<span class="op">,</span></span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> Equals(<span class="dt">Box</span><span class="op">&lt;</span>TestAtom<span class="op">&gt;,</span> <span class="dt">Box</span><span class="op">&lt;</span>TestAtom<span class="op">&gt;</span>)<span class="op">,</span></span> <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> LessThan(<span class="dt">Box</span><span class="op">&lt;</span>TestAtom<span class="op">&gt;,</span> <span class="dt">Box</span><span class="op">&lt;</span>TestAtom<span class="op">&gt;</span>)<span class="op">,</span></span> <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> GreaterThan(<span class="dt">Box</span><span class="op">&lt;</span>TestAtom<span class="op">&gt;,</span> <span class="dt">Box</span><span class="op">&lt;</span>TestAtom<span class="op">&gt;</span>)<span class="op">,</span></span> <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span> <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> Identifier <span class="op">{</span></span> <span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> A<span class="op">,</span></span> <span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> B<span class="op">,</span></span> <span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> C<span class="op">,</span></span> <span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <h2 id="evaluation">Evaluation</h2> <p>The first step, like in ERL, is transforming the formula into <a href="https://en.wikipedia.org/wiki/Negation_normal_form">negation normal form</a> (NNF), which means pushing down all negations into the atoms:</p> <div class="sourceCode" id="cb3"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> Formula<span class="op">&lt;</span>Atom<span class="op">&gt;</span> <span class="op">{</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> Atomic <span class="op">{</span></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> negated<span class="op">:</span> <span class="dt">bool</span><span class="op">,</span></span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> atom<span class="op">:</span> Atom<span class="op">,</span></span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> <span class="op">},</span></span> <span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> <span class="co">// There&#39;s no `Not` variant here!</span></span> <span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span></span> <span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <p>This makes it much easier to construct readable sentences, in addition to another important upside which I’ll get to in a second. The NNF representation is the one used by the evaluator internally.</p> <p>Next, the <code>eval</code> function takes an <code>Atom::State</code> and a <code>Formula</code>, and produces a <code>Value</code>:</p> <div class="sourceCode" id="cb4"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> Value<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">:</span> Atom<span class="op">&gt;</span> <span class="op">{</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> True<span class="op">,</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> False <span class="op">{</span> problem<span class="op">:</span> Problem<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;</span> <span class="op">},</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> Residual(Residual<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;</span>)<span class="op">,</span></span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <p>A value is either immediately true or false, meaning that we don’t need to evaluate on additional states, or a <em>residual</em>, which describes how to continue evaluating a formula when given a next state. Also note how the <code>False</code> variant holds a <code>Problem</code>, which is what we’d report as <span class="math inline">definitely false</span>. The <code>True</code> variant doesn’t need to hold any such information, because due to NNF, it can’t be negated and “turned into a problem.”</p> <p>I won’t cover every variant of the <code>Residual</code> type, but let’s take one example:</p> <div class="sourceCode" id="cb5"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> Residual<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">:</span> Atom<span class="op">&gt;</span> <span class="op">{</span></span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="co">// ...</span></span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> AndAlways <span class="op">{</span></span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> start<span class="op">:</span> Numbered<span class="op">&lt;&amp;</span><span class="ot">&#39;a</span> <span class="pp">A::</span>State<span class="op">&gt;,</span></span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> left<span class="op">:</span> <span class="dt">Box</span><span class="op">&lt;</span>Residual<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;&gt;,</span></span> <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> right<span class="op">:</span> <span class="dt">Box</span><span class="op">&lt;</span>Residual<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;&gt;,</span></span> <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> <span class="op">},</span></span> <span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> <span class="co">// ...</span></span> <span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <p>When such a value is returned, the evaluator checks if it’s possible to stop at this point, i.e. if there are no <em>demanding</em> operators in the residual. If not possible, it draws a new state and calls <code>step</code> on the residual. The <code>step</code> function is analogous to <code>eval</code>, also returning a <code>Value</code>, but it operates on a <code>Residual</code> rather than a <code>Formula</code>.</p> <p>The <code>AndAlways</code> variant describes an ongoing evaluation of the <span class="math inline">always</span> operator, where the <code>left</code> and <code>right</code> residuals are the operands of <span class="math inline">∧</span> in the inductive definition I described earlier. The <code>start</code> field holds the starting state, which is used when rendering error messages. Similarly, the <code>Residual</code> enum has variants for <span class="math inline">∨</span>, <span class="math inline">∧</span>, <span class="math inline">⟹</span>, <span class="math inline">next</span>, <span class="math inline">eventually</span>, and a few others.</p> <p>When the <code>stop</code> function deems it possible to stop evaluating, we get back a value of this type:</p> <div class="sourceCode" id="cb6"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> Stop<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">:</span> Atom<span class="op">&gt;</span> <span class="op">{</span></span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> True<span class="op">,</span></span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> False(Problem<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;</span>)<span class="op">,</span></span> <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <p>Those variants correspond to <span class="math inline">probably true</span> and <span class="math inline">probably false</span>. In the false case, we get a <code>Problem</code> which we can render. Recall how the <code>Value</code> type returned by <code>eval</code> and <code>step</code> also had <code>True</code> and <code>False</code> variants? Those are the definite cases.</p> <h2 id="rendering-problems">Rendering Problems</h2> <p>The <code>Problem</code> type is a tree structure, mirroring the structure of the evaluated formula, but only containing the parts of it that contributed to its falsity.</p> <div class="sourceCode" id="cb7"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> Problem<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">:</span> Atom<span class="op">&gt;</span> <span class="op">{</span></span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> And <span class="op">{</span></span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> left<span class="op">:</span> <span class="dt">Box</span><span class="op">&lt;</span>Problem<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;&gt;,</span></span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> right<span class="op">:</span> <span class="dt">Box</span><span class="op">&lt;</span>Problem<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;&gt;,</span></span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="op">},</span></span> <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> Or <span class="op">{</span></span> <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> left<span class="op">:</span> <span class="dt">Box</span><span class="op">&lt;</span>Problem<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;&gt;,</span></span> <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> right<span class="op">:</span> <span class="dt">Box</span><span class="op">&lt;</span>Problem<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;&gt;,</span></span> <span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> <span class="op">},</span></span> <span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> Always <span class="op">{</span></span> <span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> state<span class="op">:</span> Numbered<span class="op">&lt;&amp;</span><span class="ot">&#39;a</span> <span class="pp">A::</span>State<span class="op">&gt;,</span></span> <span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> problem<span class="op">:</span> <span class="dt">Box</span><span class="op">&lt;</span>Problem<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">&gt;&gt;,</span></span> <span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> <span class="op">},</span></span> <span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> Eventually <span class="op">{</span></span> <span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> state<span class="op">:</span> Numbered<span class="op">&lt;&amp;</span><span class="ot">&#39;a</span> <span class="pp">A::</span>State<span class="op">&gt;,</span></span> <span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> formula<span class="op">:</span> <span class="dt">Box</span><span class="op">&lt;</span>Formula<span class="op">&lt;</span>A<span class="op">&gt;&gt;,</span></span> <span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> <span class="op">},</span></span> <span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// A bunch of others...</span></span> <span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <p>I’ve written a simple renderer that walks the <code>Problem</code> tree, constructing English error messages. When hitting the atoms, it uses the <code>render</code> and <code>render_actual</code> methods from the <code>Atom</code> trait I showed you before.</p> <p>The <code>mode</code> is very much like in the ERL paper, i.e. whether it should be rendered in deontic (e.g. “x should equal 4”) or indicative (e.g. “x equals 4”) form:</p> <div class="sourceCode" id="cb8"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> TextMode <span class="op">{</span></span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> Deontic<span class="op">,</span></span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> Indicative<span class="op">,</span></span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <p>The <code>render</code> method should render the atom according to the mode, and <code>render_actual</code> should render relevant parts of the atom in a given state, like its variable assignments.</p> <p>With all these pieces in place, we can finally render some error messages! Let’s say we have this formula:</p> <p><span class="math display">eventually<sub>10</sub>(<em>B</em> = 3 ∧ <em>C</em> = 4)</span></p> <p>If we run a test and never see such a state, the rendered error would be:</p> <blockquote> <p><strong>Probably false:</strong> eventually B must equal 3 and C must equal 4, but it was not observed starting at state 0</p> </blockquote> <p>Neat! This is the kind of error reporting I want for my stateful tests.</p> <h2 id="implication">Implication</h2> <p>You can trace <em>why</em> some subformula is relevant by using implication. A common pattern in state machine specs and other safety properties is:</p> <p><span class="math display">precondition ⟹ before ∧ next<sub><em>t</em></sub>(after)</span></p> <p>So, let’s say we have this formula:</p> <p><span class="math display">always<sub><em>N</em></sub>((<em>A</em> &gt; 0) ⟹ (<em>B</em> &gt; 5 ∧ next<sub><em>t</em></sub>(<em>C</em> &lt; 10)))</span></p> <p>If <span class="math inline"><em>B</em></span> or <span class="math inline"><em>C</em></span> are false, the error includes the antecedent:</p> <blockquote> <p><strong>Definitely false:</strong> B must be greater than 5 and in the next state, C must be less than 10 since A is greater than 0, […]</p> </blockquote> <h2 id="small-errors-short-tests">Small Errors, Short Tests</h2> <p>Let’s consider a conjunction of two invariants. We could of course combine the two atomic propositions with conjunction inside a single <span class="math inline">always(...)</span>, but in this case we have the formula:</p> <p><span class="math display">always(<em>A</em> &lt; 3) ∧ always(<em>B</em> &lt; <em>C</em>)</span></p> <p>An error message, where both invariants fail, might look the following:</p> <blockquote> <p><strong>Definitely false:</strong> it must always be the case that A is less than 3 and it must always be the case that B is greater than C, but A=3 in state 3 and B=0 in state 3</p> </blockquote> <p>If only the second invariant (<span class="math inline"><em>B</em> &lt; <em>C</em></span>) fails, we get a smaller error:</p> <blockquote> <p><strong>Definitely false:</strong> it must always be the case that B is greater than C, but B=0 and C=0 in state 0</p> </blockquote> <p>And, crucially, if one of the invariants fail before the other we also get a smaller error, ignoring the other invariant. While single-state conjunctions evaluate both sides, possibly creating composite errors, conjunctions over time short-circuit in order to stop tests as soon as possible.</p> <h2 id="diagrams">Diagrams</h2> <p>Let’s say we have a failing safety property like the following:</p> <p><span class="math display">next<sub><em>d</em></sub>(always<sub>8</sub>(<em>B</em> &lt; <em>C</em>))</span></p> <p>The textual error might be:</p> <blockquote> <p><strong>Definitely false:</strong> in the next state, it must always be the case that B is greater than C, but B=13 and C=15 in state 6</p> </blockquote> <p>But with some tweaks we could also draw a diagram, using the <code>Problem</code> tree and the collected states:</p> <object data="/assets/ltl-error-reporting/always.svg" type="image/svg+xml" width="540px"> <img src="/assets/ltl-error-reporting/always.svg" width="540px" /> </object> <p>Or for a liveness property like <span class="math inline">next<sub><em>d</em></sub>(eventually<sub>8</sub>(<em>B</em> = <em>C</em>))</span>, where there is no counterexample at a particular state, we could draw a diagram showing how we give up after some time:</p> <object data="/assets/ltl-error-reporting/eventually.svg" type="image/svg+xml" width="540px"> <img src="/assets/ltl-error-reporting/eventually.svg" width="540px" /> </object> <p>These are only sketches, but I think they show how the <code>Problem</code> data structure can be used in many interesting ways. What other visualizations would be possible? An interactive state space explorer could show how problems evolve as you navigate across time. You could generate spreadsheets or HTML documents, or maybe even annotate the relevant source code of some system-under-test? I think it depends a lot on the domain this is applied to.</p> <h2 id="no-loose-ends">No Loose Ends</h2> <p>It’s been great to finally finish this work! I’ve had a lot of fun working through the various head-scratchers in the evaluator, getting strange combinations of temporal operators to render readable error messages. I also enjoyed drawing the diagrams, and <em>almost</em> nerd-sniped myself into automating that. Maybe another day. I hope this is interesting or even useful to someone out there. LTL is really cool and should be used more!</p> <p>The code, including many rendering tests cases, is available at <a href="https://codeberg.org/owi/picostrom-rs">codeberg.org/owi/picostrom-rs</a>.</p> <p><em>A special thanks goes to <a href="https://rdivyanshu.github.io/">Divyanshu Ranjan</a> for reviewing a draft of this post.</em></p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2025-10-10-programming-in-the-sun-a-year-with-the-daylight-computer.html</id>
    <title>Programming in the Sun: A Year with the Daylight Computer</title>
    <link href="https://wickstrom.tech/2025-10-10-programming-in-the-sun-a-year-with-the-daylight-computer.html"/>
    <published>2025-10-10T00:00:00+02:00</published>
    <updated>2025-10-10T00:00:00+02:00</updated>
    <summary>I’ve been hinting on X/Twitter about my use of the Daylight DC-1 as a
programming environment, and after about a year of use, it’s time to
write about it in longer form. This isn’t a full product review, but
rather an experience report on coding in sunlight. It’s also about the
Boox Tab Ultra – which has a different type of display – and how it
compares to the DC-1 for my use cases.</summary>
    <content type="html"><![CDATA[
<p>I’ve been <a href="https://x.com/owickstrom/status/1976247186632683564">hinting</a> <a href="https://x.com/owickstrom/status/1866979819961135555">on</a> <a href="https://x.com/owickstrom/status/1927349072693694743">X/Twitter</a> about my use of the Daylight DC-1 as a programming environment, and after about a year of use, it’s time to write about it in longer form. This isn’t a full product review, but rather an experience report on coding in sunlight. It’s also about the Boox Tab Ultra – which has a different type of display – and how it compares to the DC-1 for my use cases.</p> <p>This is <em>not</em> a sponsored post.</p> <figure> <img src="/assets/sun-light-mode/daylight-3.webp" alt="Neovim in Termux on the Daylight DC-1." /> <figcaption aria-hidden="true">Neovim in Termux on the Daylight DC-1.</figcaption> </figure> <p>Why do I even bother, you might ask? Sunlight makes me energetic and alert, which I need when I work. Living in the Nordics, 50% of the year is primarily dark, so any direct daylight I can get becomes really important. I usually run light mode on my Framework laptop during the day, but working in actual daylight with these displays, or plain old paper, is even better.</p> <h2 id="the-setup">The Setup</h2> <p>Here are the main components of this coding environment:</p> <ul> <li><a href="https://daylightcomputer.com/product">Daylight DC-1</a>: an Android-based tablet with a “Live Paper” display (Reflective LCD, not E-Ink)</li> <li><a href="https://www.8bitdo.com/retro-mechanical-keyboard/">8BitDo Retro Mechanical Keyboard</a>: a mechanical Bluetooth-enabled keyboard, with Kailh key switches and USB-C charging and optional connection</li> <li><a href="https://play.google.com/store/apps/details?id=com.termux&amp;hl=sv&amp;pli=1">Termux</a>: a terminal emulator for Android, with a package collection based on <code>apt</code></li> <li>SSH, tmux, and Neovim: nothing surprising here</li> </ul> <p>I use a slimmed-down version of my <a href="https://github.com/owickstrom/home-manager">regular dotfiles</a>, because this setup doesn’t use Nix. I’ve manually installed Neovim, tmux, and a few other essentials, using the package manager that comes with Termux. I’ve configured Termux to not show its virtual keyboard when a physical keyboard is connected (the Bluetooth keyboard). The Termux theme is “E-Ink” and the font is JetBrains Mono, all built into Termux. Neovim uses the built-in <code>quiet</code> colorscheme for maximum contrast.</p> <p>Certain work requires a more capable environment, and in those cases I connect to my workstation using SSH and run tmux in there. For writing or simpler programming projects (I’ve even done Rust work with Cargo, for instance), the local Termux environment is fine.</p> <p>Sometimes I want to go really minimalist, so I hide the tmux status bar and run <a href="https://github.com/junegunn/goyo.vim"><code>Goyo</code></a> in Neovim. <em>Deep breaths. Feel the fresh air in your lungs.</em> This is especially nice for writing blog posts like this one.</p> <figure> <img src="/assets/sun-light-mode/daylight-2.webp" alt="Minimalist typing with Goyo in Neovim." /> <figcaption aria-hidden="true">Minimalist typing with Goyo in Neovim.</figcaption> </figure> <p>My blog editing works locally in Termux, with a live reloading Chrome in a split window, here during an evening writing session with the warm backlight enabled:</p> <figure> <img src="/assets/sun-light-mode/daylight-6.webp" alt="Split-screen blogging locally on the Daylight." /> <figcaption aria-hidden="true">Split-screen blogging locally on the Daylight.</figcaption> </figure> <p>There’s the occasional Bluetooth connection problem with the 8BitDo keyboard. I also don’t love the layout, and I’m considering getting the <a href="https://kinesis-ergo.com/shop/freestyle2-blue-pc/">Kinesis Freestyle2 Blue</a> instead. I already have the wired version for my workstation, and the ergonomics are great.</p> <h2 id="daylight-dc-1-vs-boox-tab-ultra">Daylight DC-1 vs Boox Tab Ultra</h2> <p>What about the Boox? I’ve had this device for longer and I really like it too, but not for the same tasks. The E-Ink display is, quite frankly, a lot nicer to read on; EPUB books, research PDFs, web articles, etc. The 227 PPI instead of the Daylight’s 190 PPI makes a difference, and I like the look of E-Ink better overall.</p> <p>However, the refresh rate and ghosting make it a bit frustrating for typing. Same goes for drawing, which I’ve used the Daylight for a lot. Most of my home renovation blueprints are sketched on the Daylight. The refresh rate makes it possible.</p> <p>When reading at night with a more direct bedside lamp, often in combination with a subtle backlight, the Boox is much better. The Daylight screen can glare quite a bit, so the only option is backlight only. And at that point, a lot of the paperlike quality goes away.</p> <p>You can also get some glare when there’s direct sunlight at a particular angle:</p> <figure> <img src="/assets/sun-light-mode/daylight-1.webp" alt="You may get glare in direct sunlight or from lamps at some angles." /> <figcaption aria-hidden="true">You may get glare in direct sunlight or from lamps at some angles.</figcaption> </figure> <p>Even if I don’t write or program directly on the Boox, I’ve experimented with using it as a secondary display, like for the live reload blog preview:</p> <figure> <img src="/assets/sun-light-mode/boox-1.webp" alt="Using the Boox Tab Ultra as a secondary display by browsing the live reload HTTP server." /> <figcaption aria-hidden="true">Using the Boox Tab Ultra as a secondary display by browsing the live reload HTTP server.</figcaption> </figure> <p>To sum up, these devices are good for different things, in my experience. I’ve probably spent more time on the Boox, because I’ve had it for longer and I’ve read a lot on it, but the Daylight has been much better for typing and drawing.</p> <p>Another thing I’d like to try is a larger E-Ink monitor for my workstation, like <a href="https://x.com/zack_overflow/status/1952445830541291916">the one Zack is hacking on</a>. I’m hoping this technology continues to improve on refresh rate, because I love E-Ink. Until then, the Daylight is a good compromise.</p> <figure> <img src="/assets/sun-light-mode/daylight-4.webp" alt="Touch grass, as they say." /> <figcaption aria-hidden="true">Touch grass, as they say.</figcaption> </figure>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2025-08-28-findings-bugs-coding-agent-lightweight-dst.html</id>
    <title>Finding Bugs in a Coding Agent with Lightweight DST</title>
    <link href="https://wickstrom.tech/2025-08-28-findings-bugs-coding-agent-lightweight-dst.html"/>
    <published>2025-08-28T00:00:00+02:00</published>
    <updated>2025-08-28T00:00:00+02:00</updated>
    <summary>Amp is a coding agent which I’ve been working on the last six months at
Sourcegraph. And in the last couple of weeks, I’ve been building a
testing rig inspired by Deterministic Simulation Testing (DST) to test
the most crucial parts of the system. DST is closely related to fuzzing
and property-based testing.</summary>
    <content type="html"><![CDATA[
<p><a href="https://ampcode.com/">Amp</a> is a coding agent which I’ve been working on the last six months at Sourcegraph. And in the last couple of weeks, I’ve been building a testing rig inspired by <a href="https://github.com/ivanyu/awesome-deterministic-simulation-testing">Deterministic Simulation Testing</a> (DST) to test the most crucial parts of the system. DST is closely related to fuzzing and property-based testing.</p> <p>The goal is to get one of Amp’s most central pieces, the <em>ThreadWorker</em>, under heavy scrutiny. We’ve had a few perplexing bug reports, where users experienced corrupted <a href="https://ampcode.com/manual#threads">threads</a>, LLM API errors from invalid tool calls, and more vague issues like “it seems like it’s spinning forever.” Reproducing such problems manually is usually somewhere between impractical and impossible. I want to reproduce them deterministically, and in a way where we can debug and fix them. And beyond the known ones, I’d like to find the currently unknown ones before our users hit them.</p> <p>Generative testing to the rescue!</p> <h2 id="approach-lightweight-dst-in-typescript">Approach: Lightweight DST in TypeScript</h2> <p>Amp is written in TypeScript, which is an ecosystem currently not drowning in fuzzing tools. My starting point was using <a href="https://www.npmjs.com/package/jsfuzz"><code>jsfuzz</code></a>, which I hadn’t used before but it looked promising. However, I had a bunch of problems getting it to run together with our Bun stack. One could use fast-check, but as far as I can tell, the model-based testing they support doesn’t fit with our needs. We don’t have a model of the system, and we need to generate values in multiple places as the test runs. So, I decided to build something from scratch for our purposes.</p> <p>I borrowed an idea I got from <a href="https://tigerbeetle.com/blog/2023-03-28-random-fuzzy-thoughts/">matklad</a> last year: instead of passing a seeded PRNG to generate test input, we generate an entropy <a href="https://bun.com/docs/api/binary-data#buffer"><code>Buffer</code></a> with random contents, and track our position in that array with a cursor. Drawing a random byte <em>consumes</em> the byte at the current position and increments the cursor. We don’t know up-front how many bytes we need for a given fuzzer, so the entropy buffer grows dynamically when needed, appending more random bytes. This, together with a bunch of methods for drawing different types of values, is packaged up in an <code>Entropy</code> class:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Entropy {</span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">random</span>(count)<span class="op">:</span> UInt8Array { <span class="op">...</span> }</span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">randomRange</span>(minIncl<span class="op">:</span> <span class="dt">number</span><span class="op">,</span> maxExcl<span class="op">:</span> <span class="dt">number</span>)<span class="op">:</span> <span class="dt">number</span> { <span class="op">...</span> }</span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="co">// ... lots of other stuff</span></span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>A fuzzer is an ES module written in TypeScript, exporting a single function:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="kw">async</span> <span class="kw">function</span> <span class="fu">fuzz</span>(entropy<span class="op">:</span> Entropy) {</span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// test logic here</span></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>Any exception thrown by <code>fuzz</code> is considered a test failure. We use the <code>node:assert</code> module for our test assertions, but it could be anything.</p> <p>Another program, the fuzz runner, imports a built fuzzer module and runs as many tests it can before a given timeout. If it finds a failure, it prints out the command to reproduce that failure:</p> <pre><code>Fuzzing example.fuzzer.js iteration 1000... Fuzzing example.fuzzer.js iteration 2000... Fuzzer failed: AssertionError [ERR_ASSERTION]: 3 != 4 at [...] Reproduce with: bun --console-depth=10 scripts/fuzz.ts \ dist/example.fuzzer.js \ --verbose \ --reproduce=1493a513f88d0fd9325534c33f774831</code></pre> <p>Why use this <code>Entropy</code> rather than a seed? More about that at the end of the post!</p> <h2 id="the-threadworker-fuzzer">The ThreadWorker Fuzzer</h2> <p>In the fuzzer for our <code>ThreadWorker</code>, we stub out all IO and other nondeterministic components, and we install <a href="https://sinonjs.org/releases/latest/fake-timers/">fake timers</a> to control when and how asynchronous code is run. In effect, we have <em>determinism</em> and <em>simulation</em> to run tests in, so I guess it qualifies as DST.</p> <p>The test simulates a sequence of user actions (send message, cancel, resume, and wait). Similarly, it simulates responses from tool calls (like the agent reading a file) and from inference backends (like the Anthropic API). We inject faults and delays in both tool calls and inference requests to test our error handling and possible race conditions.</p> <p>After all user actions have been executed, we make sure to approve any pending tool calls that require confirmation. Next, we tell the fake timer to run all outstanding timers until the queue is empty; like fast-forwarding until there’s nothing left to do. Finally, we check that the thread is <em>idle</em>, i.e. that there’s no ongoing inference and that all tool calls have terminated. This is a liveness property.</p> <p>After the liveness property, we check a bunch of safety properties:</p> <ul> <li>all messages posted by the user are present in the thread</li> <li>all message pairs involving tools calls are valid according to Anthropic’s API specification</li> <li>all tool calls have settled in expected terminal states</li> </ul> <p>Some of these are targeted at specific known bugs, while some are more general but have found bugs we did not expect.</p> <p>Here’s a highly simplified version of the fuzzer:</p> <div class="sourceCode" id="cb4"><pre class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="kw">async</span> <span class="kw">function</span> <span class="fu">fuzz</span>(entropy<span class="op">:</span> Entropy) {</span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> clock <span class="op">=</span> sinon<span class="op">.</span><span class="fu">useFakeTimers</span>({</span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> loopLimit<span class="op">:</span> <span class="dv">1_000_000</span><span class="op">,</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> })</span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> worker <span class="op">=</span> <span class="fu">setup</span>() <span class="co">// including stubbing IO, etc</span></span> <span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span> {</span> <span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> resumed <span class="op">=</span> worker<span class="op">.</span><span class="fu">resume</span>()</span> <span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> clock<span class="op">.</span><span class="fu">runAllAsync</span>()</span> <span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> resumed</span> <span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">async</span> <span class="kw">function</span> <span class="fu">run</span>() {</span> <span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> (<span class="kw">let</span> round <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> round <span class="op">&lt;</span> entropy<span class="op">.</span><span class="fu">randomRange</span>(<span class="dv">1</span><span class="op">,</span> <span class="dv">50</span>)<span class="op">;</span> round<span class="op">++</span>) {</span> <span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> action <span class="op">=</span> <span class="cf">await</span> <span class="fu">generateNextAction</span>(entropy<span class="op">,</span> worker)</span> <span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span> (action<span class="op">.</span><span class="at">type</span>) {</span> <span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="st">&#39;user-message&#39;</span><span class="op">:</span></span> <span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> worker<span class="op">.</span><span class="fu">handle</span>({</span> <span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span>action<span class="op">,</span></span> <span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a> type<span class="op">:</span> <span class="st">&#39;user:message&#39;</span><span class="op">,</span></span> <span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a> })</span> <span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span></span> <span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="st">&#39;cancel&#39;</span><span class="op">:</span></span> <span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> worker<span class="op">.</span><span class="fu">cancel</span>()</span> <span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span></span> <span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="st">&#39;resume&#39;</span><span class="op">:</span></span> <span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> worker<span class="op">.</span><span class="fu">resume</span>()</span> <span id="cb4-27"><a href="#cb4-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span></span> <span id="cb4-28"><a href="#cb4-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="st">&#39;sleep&#39;</span><span class="op">:</span></span> <span id="cb4-29"><a href="#cb4-29" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> <span class="fu">sleep</span>(action<span class="op">.</span><span class="at">milliseconds</span>)</span> <span id="cb4-30"><a href="#cb4-30" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span></span> <span id="cb4-31"><a href="#cb4-31" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="st">&#39;approve&#39;</span><span class="op">:</span> {</span> <span id="cb4-32"><a href="#cb4-32" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> <span class="fu">approveTool</span>(action<span class="op">.</span><span class="at">threadID</span><span class="op">,</span> action<span class="op">.</span><span class="at">toolUseID</span>)</span> <span id="cb4-33"><a href="#cb4-33" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span></span> <span id="cb4-34"><a href="#cb4-34" aria-hidden="true" tabindex="-1"></a> }</span> <span id="cb4-35"><a href="#cb4-35" aria-hidden="true" tabindex="-1"></a> }</span> <span id="cb4-36"><a href="#cb4-36" aria-hidden="true" tabindex="-1"></a> }</span> <span id="cb4-37"><a href="#cb4-37" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-38"><a href="#cb4-38" aria-hidden="true" tabindex="-1"></a> <span class="co">// Approve any remaining tool uses to ensure termination into an </span></span> <span id="cb4-39"><a href="#cb4-39" aria-hidden="true" tabindex="-1"></a> <span class="co">// idle thread state</span></span> <span id="cb4-40"><a href="#cb4-40" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> blockedTools <span class="op">=</span> <span class="cf">await</span> <span class="fu">blockedToolUses</span>()</span> <span id="cb4-41"><a href="#cb4-41" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> <span class="bu">Promise</span><span class="op">.</span><span class="fu">all</span>(blockedTools<span class="op">.</span><span class="fu">map</span>(approve))</span> <span id="cb4-42"><a href="#cb4-42" aria-hidden="true" tabindex="-1"></a> }</span> <span id="cb4-43"><a href="#cb4-43" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-44"><a href="#cb4-44" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> done <span class="op">=</span> <span class="fu">run</span>()</span> <span id="cb4-45"><a href="#cb4-45" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> clock<span class="op">.</span><span class="fu">runAllAsync</span>()</span> <span id="cb4-46"><a href="#cb4-46" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> done</span> <span id="cb4-47"><a href="#cb4-47" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-48"><a href="#cb4-48" aria-hidden="true" tabindex="-1"></a> <span class="co">// check liveness and safety properties</span></span> <span id="cb4-49"><a href="#cb4-49" aria-hidden="true" tabindex="-1"></a> <span class="co">// ...</span></span> <span id="cb4-50"><a href="#cb4-50" aria-hidden="true" tabindex="-1"></a> } <span class="cf">finally</span> {</span> <span id="cb4-51"><a href="#cb4-51" aria-hidden="true" tabindex="-1"></a> sinon<span class="op">.</span><span class="fu">restore</span>()</span> <span id="cb4-52"><a href="#cb4-52" aria-hidden="true" tabindex="-1"></a> }</span> <span id="cb4-53"><a href="#cb4-53" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>Now, let’s dig into the findings!</p> <h2 id="results">Results</h2> <p>Given I’ve been working on this for about a week in total, I’m very happy with the outcome. Here are some issues the fuzzer found:</p> <dl> <dt><strong>Corrupted thread due to eagerly starting tool calls during streaming</strong></dt> <dd> <p>While streaming tool use blocks from the Anthropic API, we invoked tools eagerly, while not all of them were finished streaming. This, in combination with how state was managed, led to tool results being incorrectly split across messages. Anthropic’s API would reject any further requests, and the thread would essentially be corrupted. This was reported by a user and was the first issue we found and fixed using the fuzzer.</p> <p>Another variation, which the fuzzer also found, this was a race condition where user messages interfered at a particular timing with ongoing tool calls, splitting them up incorrectly.</p> </dd> <dt><strong>Subagent tool calls not terminating when subthread tool calls were rejected</strong></dt> <dd> <p>Due to a recent change in behavior, where we don’t run inference automatically after tool call rejection, subagents could end up never signalling their termination, which led to the main thread never reaching an idle state.</p> <p>I confirmed this in both VSCode and the CLI: infinite spinners, indeed.</p> </dd> <dt><strong>Tool calls blocked on user not getting cancelled after user message</strong></dt> <dd> <p>Due to how some tool calls require confirmation, like reading files outside the workspace or running some shell commands, in combination how we represent and track termination of tools, there’s a possibility for such tools to be resumed and then, after an immediate user cancellation, not be properly cancelled. This leads to incorrect mutations of the thread data.</p> <p>I’ve not yet found the cause of this issue, but it’s perfectly reproducible, so that’s a start.</p> </dd> </dl> <p>Furthermore, we were able to verify an older bug fix, where Anthropic’s API would send an invalid message with an empty tool use block array. That used to get the agent into an infinite loop. With the fuzzer, we verified and improved the old fix which had missed another case.</p> <p>How about number of test runs and timeouts? Most of these bugs were found almost immediately, i.e. within a second. The last one in the list above takes longer, around a minute normally. We run a short version of each fuzzer in every CI build, and longer runs on a nightly basis. This is up for a lot of tuning and experimentation.</p> <h2 id="why-the-entropy-buffer">Why the Entropy Buffer?</h2> <p>So why the entropy buffer instead of a seeded PRNG? The idea is to use that buffer to mutate the test input, instead of just bombarding with random data every time. If we can track which parts of the entropy was used where, we can make those slices “smaller” or “bigger.” We can use something like gradient descent or simulated annealing to optimize inputs, maximizing some objective function set by the fuzzer. Finally, we might be able to minimize inputs by manipulating the entropy.</p> <p>In case the JavaScript community gets some powerful fuzzing framework like AFL+, that could also just be plugged in. Who knows, but I find this an interesting approach that’s worth exploring. I believe the entropy buffer approach is also similar to how Hypothesis works under the hood. Someone please correct me if that’s not the case.</p> <p>Anyhow, that’s today’s report from the generative testing mines. Cheers!</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2025-02-11-machine-learning-human-unlearning.html</id>
    <title>Machine: Learning; Human: Unlearning;</title>
    <link href="https://wickstrom.tech/2025-02-11-machine-learning-human-unlearning.html"/>
    <published>2025-02-11T00:00:00+01:00</published>
    <updated>2025-02-11T00:00:00+01:00</updated>
    <summary>This last month has been fascinating. I guess LLMs have finally
resonated with me on a deeper level. It wasn’t like I woke up and
suddenly everything was different, but their impact is growing on me
non-linearly, forcing me to rewire my brain.</summary>
    <content type="html"><![CDATA[
<p>This last month has been fascinating. I guess LLMs have finally resonated with me on a deeper level. It wasn’t like I woke up and suddenly everything was different, but their impact is growing on me non-linearly, forcing me to rewire my brain.</p> <p>I know there are probably tons of blog posts by the newly converted. I’m not trying to offer any grand insights, I’m just documenting my process and current ideas.</p> <h2 id="gradually-then-suddenly">Gradually, Then Suddenly</h2> <p>I’ve been a typical sceptic of Copilot and similar tools. Sure, it’s nice to generate boilerplate and throw-away scripts, but that’s a minor part of what we do all day, right? I even took a break from using them for many months, and I’ve had serious qualms with their use in some areas outside coding.</p> <p>After messing around with <a href="https://github.com/zbirenbaum/copilot.lua">copilot.lua</a> in Neovim, I tried Cursor. Their vision and what they’ve already built opened my eyes, especially the shadow workspaces, Tab, and the rule files. At the same time, a critical mass of friends and peers were building new products on top of these models; things I highly respect and can see massive value in.</p> <p>Since then I’ve actively been looking for how I can use LLMs — beyond the auto-complete and chat interaction modes, beyond making me a slightly more productive developer. Don’t get me wrong, I love being alone coding for hours in a cozy room. It’s great. But I’m also curious to see how far I can push this myself, and of course how far and where the industry goes.</p> <h2 id="taking-on-new-projects">Taking on New Projects</h2> <p>One project that I’ve been working on goes under the working name <em>site2doc</em>. It’s a tool that converts entire web sites into EPUB and PDF books. Mostly because I want it for myself. I’d like to read online material, typeset minimalistically and beautifully, offline on my ebook reader. It turned out others want that too. There are great tools for converting single pages, but not for entire sites.</p> <p>My main problem is that the web is highly unstructured and diverse. To be frank, a lot of sites have really bad markup. No titles at all, identical titles across pages, <code>&lt;h1&gt;</code> elements used inconsistently. The list goes on. This makes it very difficult for <em>site2doc</em> to generate a useful table of contents.</p> <p>A friend suggested using LLMs to extract the information, and I experimented with using screenshots as input to classify pages. Both Claude 3.5 Sonnet and Gemini 2.0 Flash performed well, but I haven’t been able to generalize the approach across many sites. There’s just too much variability in how websites are structured, and I’m not sure how to handle it. I’m open to suggestions!</p> <p>The other project, temporarily named <em>converge</em>, is a bit closer to what everyone else is doing: using LLMs for programming. It’s an agent that, given some file or module, covers it with a generated test suite, and then goes on to optimize it. The key idea is that the original code is the source of truth. The particular optimization goal could be performance, resource usage, readability, safety, or robustness. So far I’ve focused only on performance, partly because evaluation is straightforward.</p> <p>Going beyond example-based test suites, I’ve been thinking about how property-based testing (PBT) might fit in. The obvious approach is to have the LLM generate property tests rather than examples. I don’t know how well this would work, if the LLM can generate meaningful properties.</p> <p>A more interesting way is to generate an <em>oracle property</em> that compares the behavior of new code generated by the LLM to the original code: <span class="math inline">∀<em>i</em>.old(<em>i</em>) = new(<em>i</em>)</span>, where <span class="math inline"><em>i</em></span> is some generated input. This provides a rigorous way to verify that optimizations preserve the original behavior. I’m curious to see how PBT’s shrinking could guide the LLM to iteratively fix the generated code.</p> <p>Another random idea: have the LLM explain existing code in natural language, then generate new code based only on the description. Run the old and new code side-by-side, and see how they differ, functionally and non-functionally.</p> <p>I’ve only run <em>converge</em> on toy examples and snippets so far. I’m sure there are major challenges in applying it to larger code bases. Here’s what it currently does to <a href="https://rosettacode.org/wiki/Achilles_numbers">Achilles numbers in Rosetta Code</a>:</p> <pre><code>» converge -input AchillesNumbers.java time=2025-02-11T09:35:44.877+01:00 level=INFO msg=&quot;test suite created&quot; time=2025-02-11T09:35:45.271+01:00 level=WARN msg=&quot;tests failed&quot; attempt=1 time=2025-02-11T09:35:53.308+01:00 level=INFO msg=&quot;test suite modified&quot; time=2025-02-11T09:35:53.709+01:00 level=WARN msg=&quot;tests failed&quot; attempt=2 time=2025-02-11T09:36:02.457+01:00 level=INFO msg=&quot;test suite modified&quot; time=2025-02-11T09:36:02.885+01:00 level=INFO msg=&quot;tests passed&quot; time=2025-02-11T09:36:09.508+01:00 level=INFO msg=&quot;increasing benchmark iterations&quot; duration=0.038339998573064804 attempt=0 time=2025-02-11T09:36:15.056+01:00 level=INFO msg=&quot;increasing benchmark iterations&quot; duration=0.2295980006456375 attempt=1 time=2025-02-11T09:36:21.225+01:00 level=INFO msg=&quot;increasing benchmark iterations&quot; duration=0.5998150110244751 attempt=2 time=2025-02-11T09:36:28.010+01:00 level=INFO msg=&quot;benchmark run&quot; time=2025-02-11T09:36:35.737+01:00 level=INFO msg=&quot;code optimized&quot; attempt=0 time=2025-02-11T09:38:19.094+01:00 level=INFO msg=&quot;tests failed&quot; ... time=2025-02-11T09:38:33.798+01:00 level=INFO msg=&quot;code optimized&quot; attempt=9 optimization succeeded: 1.870953s -&gt; 1.337585s (-28.51%)</code></pre> <p>That took about three minutes. It does all sorts of tricks to make it faster, but one that caught my eye was the conversion from <code>HashMap&lt;Integer, Boolean&gt;</code> to <code>HashSet&lt;Integer&gt;</code>, and finally to <code>BitSet</code>. Here’s part of the diff:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>...</span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> public class AchillesNumbers {</span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="st">-</span></span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="st">- private Map&lt;Integer, Boolean&gt; pps = new HashMap&lt;&gt;();</span></span> <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="va">+ private final BitSet pps = new BitSet();</span></span> <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="va">+ private final BitSet achillesCache = new BitSet();</span></span> <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="va">+ private static final byte[] SMALL_PRIMES = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47};</span></span> <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="va">+ private static final int[] POWERS_OF_TEN = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};</span></span> <span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="va">+ private static final short[] SQUARES = new short[317];</span></span> <span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="va">+ private static final int[] CUBES = new int[47];</span></span> <span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="va">+ private static final short[] TOTIENTS = new short[1000];</span></span> <span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> ...</span> <span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> }</span></code></pre></div> <h2 id="unlearning">Unlearning</h2> <p>I’m surprised to find myself as excited as I am now. I did not see it coming! Just during the last few days, I’ve realized how much I need to <em>unlearn</em> in order to make better use of what these models have learned.</p> <p>I was implementing control flow, using Claude to generate various bits of code for the <em>converge</em> tool. Then I realized that, hey, maybe it should be the other way around? Claude plans the control flow, and my tool just provides the ways of interacting with the environment (modifying source files, running tests, etc). It’s not a revelation, but an example of how one might need to think differently.</p> <p>Even more down to earth, things like saying “do X for me” rather than asking “how do I do X?”. Instead of asking for some chunk of code, I tell it to solve a problem for me. Of course, I still review the changes. Cursor and Cody have both been great ways of changing my thinking.</p> <p>What other habits and thought patterns might need to change? I don’t know how programming will look in the future, but I’m actively working on keeping an open mind and hopefully playing a small role in shaping it.</p> <p><em>Comment on <a href="https://news.ycombinator.com/item?id=43006419">Hacker News</a> or <a href="https://x.com/owickstrom/status/1889085500759187909">X</a>.</em></p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2024-09-26-how-i-built-the-monospace-web.html</id>
    <title>How I Built "The Monospace Web"</title>
    <link href="https://wickstrom.tech/2024-09-26-how-i-built-the-monospace-web.html"/>
    <published>2024-09-26T00:00:00+02:00</published>
    <updated>2024-09-26T00:00:00+02:00</updated>
    <summary>Recently, I published The Monospace Web, a minimalist design
exploration. It all started with this innocent post, yearning for a
simpler web. Perhaps too typewriter-nostalgic, but it was an interesting
starting point. After some hacking and sharing early screenshots,
@noteed asked for grid alignment, and down the rabbit hole I went.</summary>
    <content type="html"><![CDATA[
<p>Recently, I published <a href="https://owickstrom.github.io/the-monospace-web/">The Monospace Web</a>, a minimalist design exploration. It all started with <a href="https://x.com/owickstrom/status/1825200136873501136">this innocent post</a>, yearning for a simpler web. Perhaps too typewriter-nostalgic, but it was an interesting starting point. After some hacking and sharing early screenshots, <a href="https://x.com/noteed/status/1825439561041707100"><span class="citation" data-cites="noteed">@noteed</span> asked for grid alignment</a>, and down the rabbit hole I went.</p> <p>This morphed into a technical challenge, while still having that creative aspect that I started with. Subsequent screenshots with the fixed grid and responsive tables sparked a lot of interest. About a week later I published the source, and since then there’s been a lot of forks. People use it for their personal web sites, mostly, but also for apps.</p> <p>I’d like to explain how it works. Not everything, just focusing on the most interesting parts.</p> <h2 id="the-fixed-grid">The Fixed Grid</h2> <p>This design aligns everything, horizontally and vertically, to a fixed grid. Like a table with equal-size cells. Every text character should be exactly contained in a cell in that grid. Borders and other visual elements may span cells more freely.</p> <p>The big idea here is to use the <code>ch</code> unit in CSS. I actually did not know about it before this experiment. The <code>ch</code> unit is described in <a href="https://www.w3.org/TR/css-values-4/#ch">CSS Values and Units Module Level 4</a>:</p> <blockquote> <p>Represents the typical advance measure of European alphanumeric characters, and measured as the used advance measure of the “0” (ZERO, U+0030) glyph in the font used to render it.</p> </blockquote> <p>Further, it notes:</p> <blockquote> <p>This measurement is an approximation (and in monospace fonts, an exact measure) of a single narrow glyph’s advance measure, thus allowing measurements based on an expected glyph count.</p> </blockquote> <p>Fantastic! A cell is thus <code>1ch</code> wide. And the cell height is equal to the <code>line-height</code>. In order to refer to the line height in calculations, it’s extracted as a CSS variable:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="in">:root</span> {</span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="va">--line-height</span><span class="ch">:</span> <span class="dv">1.2</span><span class="dt">rem</span><span class="op">;</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>So far there’s no <em>actual</em> grid on the page. This is just a way of measuring and sizing elements based on the width and height of monospace characters. Every element must take up space evenly divisible by the dimensions of a cell; if it’s top left corner starts at a cell, then its bottom right must do so as well. That means that all elements, given that their individual sizes respect the grid, line up properly.</p> <h2 id="the-font">The Font</h2> <p>I’ve chosen <a href="https://www.jetbrains.com/lp/mono/">JetBrains Mono</a> for the font. It looks great, sure, but there’s a more important reason for this particular choice: it has good support for box-drawing characters at greater line heights. Most monospace fonts I tried broke at line heights above 110% or so. Lines and blocks were chopped up vertically. With JetBrains Mono, I can set it to 120% before it starts to become choppy.</p> <p>I suspect <a href="https://fsd.it/shop/fonts/pragmatapro/">Pragmata Pro</a> or <a href="https://berkeleygraphics.com/typefaces/berkeley-mono/">Berkeley Mono</a> might work in this regard, but I haven’t tried them yet.</p> <p>Also, if you want to use this design and with a greater line height, you can probably do so if you don’t need box-drawing characters. Then you may also consider pretty much any monospace font. Why not use web-safe ones, trimming down the page weight by about 600kB!</p> <p>To avoid alternate glyphs for numbers, keeping everything aligned to the grid, I set:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="in">:root</span> {</span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">font-variant-numeric</span><span class="ch">:</span> <span class="dv">tabular-nums</span> <span class="dv">lining-nums</span><span class="op">;</span></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>And, for a unified thickness of borders, text, and underlines:</p> <div class="sourceCode" id="cb3"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="in">:root</span> {</span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="va">--border-thickness</span><span class="ch">:</span> <span class="dv">2</span><span class="dt">px</span><span class="op">;</span></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">font-weight</span><span class="ch">:</span> <span class="dv">500</span><span class="op">;</span></span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">text-decoration-thickness</span><span class="ch">:</span> <span class="fu">var(</span><span class="va">--border-thickness</span><span class="fu">)</span><span class="op">;</span></span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>This gives the design that thick and sturdy feel.</p> <h2 id="the-body">The Body</h2> <p>The <code>body</code> element is the main container in the page. It is <em>at most</em> 80 characters wide. (Huh, I wonder where that number came from?)</p> <p>Now for one of the key tricks! I wanted this design to be reasonably responsive. For a viewport width smaller than <code>84ch</code> (<code>80ch</code> and <code>4ch</code> of padding), the body width needs to be some smaller width that is still evenly divisible by the cell dimensions. This can be accomplished with CSS rounding:</p> <div class="sourceCode" id="cb4"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>body {</span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">padding</span><span class="ch">:</span> <span class="fu">var(</span><span class="va">--line-height</span><span class="fu">)</span> <span class="dv">2</span><span class="dt">ch</span><span class="op">;</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">max-width</span><span class="ch">:</span> <span class="fu">calc(min(</span><span class="dv">80</span><span class="dt">ch</span><span class="op">,</span> <span class="fu">round(</span><span class="dv">down</span><span class="op">,</span> <span class="dv">100</span><span class="dt">%</span><span class="op">,</span> <span class="dv">1</span><span class="dt">ch</span><span class="fu">)))</span><span class="op">;</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>This way, the body shrinks in steps according to the grid.</p> <h2 id="the-horizontal-rule">The Horizontal Rule</h2> <p>Surprisingly, the custom horizontal rule styling was a fiddly enterprise. I wanted it to feel heavy, with double lines. The lines are vertically centered around the break between two cells:</p> <figure> <img src="/assets/monospace/hr.webp" width="353" alt="The horizontal ruler" /> <figcaption aria-hidden="true">The horizontal ruler</figcaption> </figure> <p>To respect the grid, the top and bottom spacing needs to be calculated. But padding won’t work, and margin interacts with adjacent elements’ margins, so this required two elements:</p> <div class="sourceCode" id="cb5"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>hr {</span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">position</span><span class="ch">:</span> <span class="dv">relative</span><span class="op">;</span></span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">display</span><span class="ch">:</span> <span class="dv">block</span><span class="op">;</span></span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">height</span><span class="ch">:</span> <span class="fu">var(</span><span class="va">--line-height</span><span class="fu">)</span><span class="op">;</span></span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">margin</span><span class="ch">:</span> <span class="fu">calc(var(</span><span class="va">--line-height</span><span class="fu">)</span> <span class="op">*</span> <span class="dv">1.5</span><span class="fu">)</span> <span class="dv">0</span><span class="op">;</span></span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">border</span><span class="ch">:</span> <span class="dv">none</span><span class="op">;</span></span> <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="fu">var(</span><span class="va">--text-color</span><span class="fu">)</span><span class="op">;</span></span> <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>}</span> <span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>hr<span class="in">:after</span> {</span> <span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">display</span><span class="ch">:</span> <span class="dv">block</span><span class="op">;</span></span> <span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">content</span><span class="ch">:</span> <span class="st">&quot;&quot;</span><span class="op">;</span></span> <span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">position</span><span class="ch">:</span> <span class="dv">absolute</span><span class="op">;</span></span> <span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">top</span><span class="ch">:</span> <span class="fu">calc(var(</span><span class="va">--line-height</span><span class="fu">)</span> <span class="op">/</span> <span class="dv">2</span> <span class="op">-</span> <span class="fu">var(</span><span class="va">--border-thickness</span><span class="fu">))</span><span class="op">;</span></span> <span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">left</span><span class="ch">:</span> <span class="dv">0</span><span class="op">;</span></span> <span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">width</span><span class="ch">:</span> <span class="dv">100</span><span class="dt">%</span><span class="op">;</span></span> <span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">border-top</span><span class="ch">:</span> <span class="fu">calc(var(</span><span class="va">--border-thickness</span><span class="fu">)</span> <span class="op">*</span> <span class="dv">3</span><span class="fu">)</span> <span class="dv">double</span> <span class="fu">var(</span><span class="va">--text-color</span><span class="fu">)</span><span class="op">;</span></span> <span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a> <span class="kw">height</span><span class="ch">:</span> <span class="dv">0</span><span class="op">;</span></span> <span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>The <code>hr</code> itself is just a container that takes up space; 4 lines in total. The <code>hr:after</code> pseudo-element draws the two lines, using <code>border-top-style</code>, at the vertical center of the <code>hr</code>.</p> <h2 id="the-table">The Table</h2> <p>Table styling was probably the trickiest. Recalling the principles from the beginning, every character must be perfectly aligned with the grid. But I wanted vertical padding of table cells to be <em>half</em> the line height. A full line height worth of padding is way too airy.</p> <p><img src="/assets/monospace/table.webp" alt="Table alignment" style="width: 796px"></p> <p>This requires the table being horizontally offset by half the width of a character, and vertically offset by half the line height.</p> <div class="sourceCode" id="cb6"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>table {</span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">position</span><span class="ch">:</span> <span class="dv">relative</span><span class="op">;</span></span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">top</span><span class="ch">:</span> <span class="fu">calc(var(</span><span class="va">--line-height</span><span class="fu">)</span> <span class="op">/</span> <span class="dv">2</span><span class="fu">)</span><span class="op">;</span></span> <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">width</span><span class="ch">:</span> <span class="fu">calc(round(</span><span class="dv">down</span><span class="op">,</span> <span class="dv">100</span><span class="dt">%</span><span class="op">,</span> <span class="dv">1</span><span class="dt">ch</span><span class="fu">))</span><span class="op">;</span></span> <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">border-collapse</span><span class="ch">:</span> <span class="dv">collapse</span><span class="op">;</span></span> <span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>Cell padding is calculated based on cell size and borders, to keep grid alignment:</p> <div class="sourceCode" id="cb7"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>th<span class="op">,</span> td {</span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">border</span><span class="ch">:</span> <span class="fu">var(</span><span class="va">--border-thickness</span><span class="fu">)</span> <span class="dv">solid</span> <span class="fu">var(</span><span class="va">--text-color</span><span class="fu">)</span><span class="op">;</span></span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">padding</span><span class="ch">:</span> </span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">calc(</span>(<span class="fu">var(</span><span class="va">--line-height</span><span class="fu">)</span> <span class="op">/</span> <span class="dv">2</span>)<span class="fu">)</span></span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">calc(</span><span class="dv">1</span><span class="dt">ch</span> <span class="op">-</span> <span class="fu">var(</span><span class="va">--border-thickness</span><span class="fu">)</span> <span class="op">/</span> <span class="dv">2</span><span class="fu">)</span></span> <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">calc(</span>(<span class="fu">var(</span><span class="va">--line-height</span><span class="fu">)</span> <span class="op">/</span> <span class="dv">2</span>) <span class="op">-</span> (<span class="fu">var(</span><span class="va">--border-thickness</span><span class="fu">)</span>)<span class="fu">)</span></span> <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="op">;</span></span> <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">line-height</span><span class="ch">:</span> <span class="fu">var(</span><span class="va">--line-height</span><span class="fu">)</span><span class="op">;</span></span> <span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>Finally, the first row must have slightly less vertical padding to compensate for the top border. This is hacky, and would be nicer to solve with some kind of negative margin on the table. But then I’d be back in margin interaction land, and I don’t like it there.</p> <div class="sourceCode" id="cb8"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>table tbody tr<span class="in">:first-child</span> <span class="op">&gt;</span> <span class="op">*</span> {</span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">padding-top</span><span class="ch">:</span> <span class="fu">calc(</span></span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> (<span class="fu">var(</span><span class="va">--line-height</span><span class="fu">)</span> <span class="op">/</span> <span class="dv">2</span>) <span class="op">-</span> <span class="fu">var(</span><span class="va">--border-thickness</span><span class="fu">)</span></span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">)</span><span class="op">;</span></span> <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>Another quirk is that columns need to have set sizes. All but one should use the <code>width-min</code> class, and the remaining should use <code>width-auto</code>. Otherwise, cells divide the available width in a way that doesn’t align with the grid.</p> <h2 id="the-layout-grid">The Layout Grid</h2> <p>I also included a <code>grid</code> class for showcasing how a grid layout helper could work. Much like the 12-column systems found in CSS frameworks, but funkier. To use it, you simply slap on a <code>grid</code> class on a container. It uses a glorious hack to count the children in pure CSS:</p> <div class="sourceCode" id="cb9"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span> <span class="op">&gt;</span> <span class="op">*</span> {</span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">flex</span><span class="ch">:</span> <span class="dv">0</span> <span class="dv">0</span> <span class="fu">calc(</span></span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">round(</span></span> <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="dv">down</span><span class="op">,</span> </span> <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">(</span><span class="dv">100</span><span class="dt">%</span> <span class="dv">- </span><span class="fu">(</span><span class="dv">1</span><span class="dt">ch</span> build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh <span class="fu">(var(</span><span class="va">--grid-cells</span><span class="fu">)</span> <span class="dv">- 1</span><span class="fu">)))</span> / <span class="fu">var(</span><span class="va">--grid-cells</span><span class="fu">)</span><span class="op">,</span></span> <span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> <span class="dv">1</span><span class="dt">ch</span></span> <span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">)</span></span> <span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">)</span><span class="op">;</span></span> <span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>}</span> <span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span><span class="in">:has(</span><span class="op">&gt;</span> <span class="in">:last-child:nth-child(</span><span class="dv">1</span><span class="in">))</span> { <span class="va">--grid-cells</span><span class="ch">:</span> <span class="dv">1</span><span class="op">;</span> }</span> <span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span><span class="in">:has(</span><span class="op">&gt;</span> <span class="in">:last-child:nth-child(</span><span class="dv">2</span><span class="in">))</span> { <span class="va">--grid-cells</span><span class="ch">:</span> <span class="dv">2</span><span class="op">;</span> }</span> <span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span><span class="in">:has(</span><span class="op">&gt;</span> <span class="in">:last-child:nth-child(</span><span class="dv">3</span><span class="in">))</span> { <span class="va">--grid-cells</span><span class="ch">:</span> <span class="dv">3</span><span class="op">;</span> }</span> <span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span><span class="in">:has(</span><span class="op">&gt;</span> <span class="in">:last-child:nth-child(</span><span class="dv">4</span><span class="in">))</span> { <span class="va">--grid-cells</span><span class="ch">:</span> <span class="dv">4</span><span class="op">;</span> }</span> <span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span><span class="in">:has(</span><span class="op">&gt;</span> <span class="in">:last-child:nth-child(</span><span class="dv">5</span><span class="in">))</span> { <span class="va">--grid-cells</span><span class="ch">:</span> <span class="dv">5</span><span class="op">;</span> }</span> <span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span><span class="in">:has(</span><span class="op">&gt;</span> <span class="in">:last-child:nth-child(</span><span class="dv">6</span><span class="in">))</span> { <span class="va">--grid-cells</span><span class="ch">:</span> <span class="dv">6</span><span class="op">;</span> }</span> <span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span><span class="in">:has(</span><span class="op">&gt;</span> <span class="in">:last-child:nth-child(</span><span class="dv">7</span><span class="in">))</span> { <span class="va">--grid-cells</span><span class="ch">:</span> <span class="dv">7</span><span class="op">;</span> }</span> <span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span><span class="in">:has(</span><span class="op">&gt;</span> <span class="in">:last-child:nth-child(</span><span class="dv">8</span><span class="in">))</span> { <span class="va">--grid-cells</span><span class="ch">:</span> <span class="dv">8</span><span class="op">;</span> }</span> <span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a><span class="fu">.grid</span><span class="in">:has(</span><span class="op">&gt;</span> <span class="in">:last-child:nth-child(</span><span class="dv">9</span><span class="in">))</span> { <span class="va">--grid-cells</span><span class="ch">:</span> <span class="dv">9</span><span class="op">;</span> }</span></code></pre></div> <p>Look at it go!</p> <figure> <img src="/assets/monospace/grid.gif" alt="The responsive layout grid in action" /> <figcaption aria-hidden="true">The responsive layout grid in action</figcaption> </figure> <p>Unlike with tables, the layout grid rows don’t have to fill the width. Depending on your viewport width, you’ll see a ragged right margin. However, by setting <code>flex-grow: 1;</code> on one of the children, that one grows to fill up the remaining width.</p> <h2 id="the-media-elements">The Media Elements</h2> <p>Images and video grow to fill the width. But they have their own proportions, making vertical alignment a problem. How many multiples of the line height should the height of the media be? I couldn’t figure out a way to calculate this with CSS alone.</p> <figure> <img src="/assets/monospace/media.webp" width="383" alt="Media element with adjusted bottom padding (the figcaption below lines up with the grid)" /> <figcaption aria-hidden="true">Media element with adjusted bottom padding (the figcaption below lines up with the grid)</figcaption> </figure> <p>One option was a preprocessor step that would calculate and set the ratio of every such element as a CSS variable, and then have CSS calculate a <code>padding-bottom</code> based on the ratio and the width:</p> <div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">img</span><span class="ot"> style</span><span class="op">=</span><span class="st">&quot;--ratio: 0.821377&quot;</span><span class="ot"> </span><span class="er">...</span><span class="ot"> </span><span class="dt">&gt;</span></span></code></pre></div> <p>However, I eventually settled for small chunk of JavaScript to calculate the difference, and set an appropriate <code>padding-bottom</code>. Ending up in JavaScript was inevitable, I suppose.</p> <h2 id="summary">Summary</h2> <p>There are many small things I haven’t shown in detail, including:</p> <ul> <li>the debug grid overlay, which you see in the screenshots</li> <li>ordered list numbering</li> <li>the tree-rendered list</li> <li>various form controls and buttons</li> <li>the custom <code>details</code> element</li> </ul> <p>But I think I’ve covered the most significant bits. For a full tour, have a look at <a href="https://github.com/owickstrom/the-monospace-web/blob/main/index.css">the source code</a>.</p> <p>There are still bugs, like alignment not working the same in all browsers, and not working at all in some cases. I’m not sure why yet, and I might try to fix it in at least Firefox and Chromium. Those are the ones I can test with easily.</p> <p>This has been a fun project, and I’ve learned a bunch of new CSS tricks. Also, the amount of positive feedback has been overwhelming. Of course, there’s been some negative feedback as well, not to be dismissed. I do agree with the concern about legibility. Monospace fonts can be beautiful and very useful, but they’re probably not the best for prose or otherwise long bodies of text.</p> <p>Some have asked for reusable themes or some form of published artifact. I won’t spend time maintaining such packages, but I encourage those who would. Fork it, tweak it, build new cool things with it!</p> <p>Finally, I’ll note that I’m happy with how the overall feel of this design turned out, even setting aside the monospace aspect. Maybe it would work with a proportional, or perhaps semi-proportional, font as well?</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2024-08-12-a-flexible-minimalist-neovim.html</id>
    <title>A Flexible Minimalist Neovim for 2024</title>
    <link href="https://wickstrom.tech/2024-08-12-a-flexible-minimalist-neovim.html"/>
    <published>2024-08-12T00:00:00+02:00</published>
    <updated>2024-08-12T00:00:00+02:00</updated>
    <summary>In the eternal search of a better text editor, I’ve recently gone back
to Neovim. I’ve taken the time to configure it myself, with as few
plugins and other cruft as possible. My goal is a minimalist editing
experience, tailored for exactly those tasks that I do regularly, and
nothing more. In this post, I’ll give a brief tour of my setup and its
motivations.</summary>
    <content type="html"><![CDATA[
<p><picture> <source type="image/webp" srcset="/assets/nvim.webp"> <img src="/assets/nvim.png" alt="Neovim welcome screen"> </picture></p> <p>In the eternal search of a better text editor, I’ve recently gone back to Neovim. I’ve taken the time to configure it myself, with as few plugins and other cruft as possible. My goal is a minimalist editing experience, tailored for exactly those tasks that I do regularly, and nothing more. In this post, I’ll give a brief tour of my setup and its motivations.</p> <p>Over the years, I’ve been through a bunch of editors. Here are most of them, in roughly chronological order:</p> <ul> <li>Adobe Dreamweaver</li> <li>Sublime Text</li> <li>Atom</li> <li>Vim/Neovim</li> <li>IntelliJ IDEA</li> <li>VS Code</li> <li>Emacs</li> </ul> <p>The majority were used specifically for the work I was doing at the time. VS Code for web development, IntelliJ IDEA for JVM languages, Emacs for Lisps. Vim and Emacs have been the most generally applicable, and the ones that I’ve enjoyed the most.</p> <p>I’ve also evaluated Zed recently, but it hasn’t quite stuck. The start-up time and responsiveness is impressive. However, it seems to insist on being a <em>global</em> application. I want my editor to be local to each project, with the correct <code>PATH</code> from direnv, and I want multiple isolated instances to run simultaneously. Maybe I’ll revisit it later.</p> <h2 id="returning-to-neovim">Returning to Neovim</h2> <p>Yeah, so I’m back in Neovim. I actually started with the <a href="http://www.lazyvim.org/">LazyVim</a> distribution based on recommendation. On the positive side, it got me motivated to use Neovim again. But I had some frustrations with the distribution.</p> <p>The start-up time wasn’t great. I guess it did some lazy loading of plugins to speed things up, but the experience was still that of an IDE taking its time to get ready. Not the Neovim experience I was hoping for.</p> <p>More importantly, it was full of distractions; popups, status messages, news, and plugins I didn’t need. I guess it takes a batteries-included approach. That might make sense for beginners and those just getting into Neovim, but I realized quickly that I wanted something different.</p> <p>Supposedly I could strip things out, but instead I decided to start from scratch and build the editor I wanted. One that I understand. Joran Dirk Greef talks about two different types of artists, <a href="https://www.youtube.com/watch?v=w3WYdYyjek4&amp;ab_channel=TigerBeetle">sculptors and painters</a>, and this is an exercise in painting.</p> <p>More concretely, my main goals are:</p> <dl> <dt>Plugins</dt> <dd> <p>I want to keep plugins to an absolute minimum. My editor is meant for coding and writing, and for what I work on right now. I might add or remove plugins and configuration over time, and that’s fine.</p> </dd> <dt>User interface</dt> <dd> <p>It should be as minimalist as I can make it. Visual distractions kept at a minimum. This includes busy colorschemes, which I find add little value. A basic set of typographical conventions in monochrome works well for me.</p> </dd> <dt>Start-up time</dt> <dd> <p>With the way I use Neovim, it needs to start fast. I quit and start it all the time, jumping between directories, working on different parts of the system or a project. Often I put it in the background with <kbd>C-z</kbd>, but not always. Making it faster seems to be mainly an exercise in minimizing plugins.</p> </dd> </dl> <h2 id="i-have-to-mention-nix-you-know">I have to mention Nix, you know?</h2> <p>I manage dotfiles and other personal configuration using Nix and home-manager. The Neovim configuration is no exception, so I’ll include some of the Nix bits as well.</p> <p>The <code>vim.nix</code> module declares that I want Neovim installed and exposed as <code>vim</code>:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>programs.neovim = <span class="op">{</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="va">enable</span> <span class="op">=</span> <span class="cn">true</span><span class="op">;</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="va">vimAlias</span> <span class="op">=</span> <span class="cn">true</span><span class="op">;</span></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> ...</span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div> <p>In that attribute set, there are two other important parts; the <code>plugins</code> list and the <code>extraConfig</code>.</p> <h2 id="plugins">Plugins</h2> <p>Let’s start with the plugins:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>plugins = <span class="kw">with</span> pkgs.vimPlugins<span class="op">;</span> <span class="op">[</span></span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> nvim-lspconfig</span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="op">(</span>nvim<span class="op">-</span>treesitter.withPlugins<span class="op">(</span><span class="va">p</span><span class="op">:</span> <span class="op">[</span></span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> p.bash</span> <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> p.json</span> <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> p.lua</span> <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> p.markdown</span> <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> p.nix</span> <span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> p.python</span> <span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> p.rust</span> <span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> p.zig</span> <span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> p.vimdoc</span> <span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> <span class="op">]))</span></span> <span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> conform-nvim</span> <span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> neogit</span> <span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> fzf-vim</span> <span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="op">]</span>;</span></code></pre></div> <p>Basically it’s five plugins, not counting the various treesitter parsers:</p> <dl> <dt>nvim-lspconfig</dt> <dd> LSP is included in Neovim nowadays, but it doesn’t know about specific language servers. The <code>lspconfig</code> plugins helps with configuring Neovim for use with various servers. </dd> <dt>nvim-treesitter</dt> <dd> This provides better highlighting for Neovim. I’ve only included the languages I use right now. No nice-to-haves. I could probably remove this plugin, but I haven’t tried yet. </dd> <dt>conform-nvim</dt> <dd> Auto-formatting is useful and I don’t want to think about it. Of course, I could run <code>:%!whatever-formatter</code>, but I’d rather have the editor do it for me. </dd> <dt>neogit</dt> <dd> Magit was the reason I clung to Emacs for so long. Neogit is, for my purposes, a worthy replacement. It enabled me to finally make the switch. </dd> <dt>fzf-vim</dt> <dd> A wrapper around the awesome <a href="https://github.com/junegunn/fzf">fzf</a> fuzzy finder. I use <code>:Files</code> and <code>:GFiles</code>, as quick jump-to-file commands (think <code>C-p</code> in VS Code or Zed). They are bound to <code>&lt;Leader&gt;ff</code> and <code>&lt;Leader&gt;gf</code>, respectively. This might be another plugin I could do without, writing a small helper around <code>fzf</code>, or just making do with <code>:find</code> and <code>**/</code> wildcards. On the other hand, I’m trying out <code>:Buffers</code> instead of stumbling around with <code>:bnext</code> and <code>:bprev</code>. </dd> </dl> <p>One great thing with the Nix setup is I don’t need a package manager in Neovim itself.</p> <h2 id="batteries-are-included">Batteries Are Included</h2> <p>Many things I don’t need plugins for. For instance, there are a ton of plugins for auto-completion, but Neovim has most of that built in, and I prefer triggering it manually:</p> <ul> <li>File name completions with <code>C-x C-f</code></li> <li>Omnicomplete (rebound to <code>C-Space</code> in my case)</li> <li>Buffer completions with <code>C-x C-n</code></li> <li>Spelling suggestions with <code>C-x C-s</code> or <code>z=</code></li> </ul> <p>I’ve tried various snippet engines many times, but not found them very useful. Most of my time is spent reading or modifying existing code, not churning out new boilerplate. Instead they tend to clutter the auto-completion list. Snippets might make more sense for things like HTML, but I don’t write HTML often, and in that case I’d prefer some emmet/zen-coding plugin.</p> <p>You can get great mileage from learning how to use the Quickfix list. I’m no expert, but I prefer investing in composable primitives that I can reuse in different ways. Project-wide search-and-replace is such an example:</p> <pre class="vim"><code>:grep whatever :cfdo s/whatever/something else/g | update :cfdo :bd</code></pre> <p>Here we search (<code>:grep</code>, which I’ve configured to use <code>rg</code>), substitute and save each file, and delete those buffers afterwards.</p> <p>I also use <code>:make</code> and <code>:compiler</code> a lot. Neovim is cool.</p> <h2 id="life-in-monochrome">Life in Monochrome</h2> <p>Maybe I’m just growing old, but I prefer a monochrome colorscheme. Right now I’m using the built-in <code>quiet</code> scheme with a few extra tweaks:</p> <pre class="vimscript"><code>set termguicolors set bg=dark colorscheme quiet highlight Keyword gui=bold highlight Comment gui=italic highlight Constant guifg=#999999 highlight NormalFloat guibg=#333333</code></pre> <p>It’s black-and-white, but keywords are bold, comments are darker and italic, and literals are light gray. Here’s how it looks with some Zig code:</p> <p><picture> <source type="image/webp" srcset="/assets/nvim-monochrome.webp"> <img src="/assets/nvim-monochrome.png" alt="Neovim in monochrome with the quiet colorscheme"> </picture></p> <p>Maybe I’m coming off as nostalgic or conservative, but I <em>do</em> find it more readable this way.</p> <p>Another thing I’m going to try soon is writing on the <a href="https://daylightcomputer.com/">Daylight Computer</a>, hopefully in Neovim. Being comfortable with a monochrome colorscheme should come in handy.</p> <h2 id="the-full-configuration">The Full Configuration</h2> <p>My config uses a Vimscript entrypoint (<code>extraConfig</code> in the Nix code). This part is based on my near-immortal config from the good old Vim days. Early on, it calls <code>vim.loader.enable()</code> to improve startup time. I use Lua scripts for configuring the plugins and related keymap bindings. Maybe everything could be Lua, but I haven’t gotten that far yet. However, it’s nice to have the base config somewhat portable; I can just copy-paste it onto a server or some other temporary environment and have a decent <code>/usr/bin/vim</code> experience.</p> <p>You’ll find the full configuration in <a href="https://github.com/owickstrom/home-manager/blob/master/vim.nix">nix.vim</a> and the Lua bits inside the <a href="https://github.com/owickstrom/home-manager/blob/master/vim/">vim/</a> directory.</p> <p>That’s about it! I’m really happy with how fast and minimalistic it is. It starts in just above 100ms. And I can understand all of my configuration (even if I don’t understand all of Neovim.) Perhaps I’ve <a href="https://xkcd.com/1205/">spent more time on it than I’ve saved</a>, but at least I’m happy so far.</p> <p>I’m writing and publishing this on my birthday. What a treat to find time to blog on such an occasion!</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2024-05-23-statically-typed-functional-programming-python-312.html</id>
    <title>Statically Typed Functional Programming with Python 3.12</title>
    <link href="https://wickstrom.tech/2024-05-23-statically-typed-functional-programming-python-312.html"/>
    <published>2024-05-23T00:00:00+02:00</published>
    <updated>2024-05-23T00:00:00+02:00</updated>
    <summary>Lately I’ve been messing around with Python 3.12, discovering new
features around typing and pattern matching. Combined with dataclasses,
they provide support for a style of programming that I’ve employed in
Kotlin and Typescript at work. That style in turn is based on what I’d
do in OCaml or Haskell, like modelling data with algebraic data types.
However, the more advanced concepts from Haskell — and OCaml too, I
guess — don’t transfer that well to mainstream languages.</summary>
    <content type="html"><![CDATA[
<p>Lately I’ve been messing around with Python 3.12, discovering new features around typing and pattern matching. Combined with dataclasses, they provide support for a style of programming that I’ve employed in Kotlin and Typescript at work. That style in turn is based on what I’d do in OCaml or Haskell, like modelling data with algebraic data types. However, the more advanced concepts from Haskell — and OCaml too, I guess — don’t transfer that well to mainstream languages.</p> <p>What I’m describing in this post is a trade-off that I find comfortable to use in Python, especially with the new features that I’ll describe. Much of this works nicely in Kotlin and Typescript, too, with minor adaptions.</p> <p>The principles I try to use are:</p> <dl> <dt>Declarative rather than imperative operations on data</dt> <dd> Transform or fold data using <code>map</code>, <code>reduce</code>, or for-comprehensions instead of for-loops </dd> <dt>Functions with destructuring pattern-matching to dispatch based on data</dt> <dd> Use <code>when</code> statements rather than <code>if instanceof(...)</code> or inheritance and dynamic dispatch </dd> <dt>Programs structured mainly around data and functions</dt> <dd> Programs are trees of function invocations on data, rather than class hierarchies, dependency injection, and overuse of exceptions </dd> <dt>Effects pushed to the outer layers of the program (hexagonal architecture)</dt> <dd> Within reasonable bounds, functions in the guts of programs are <em>pure</em> and return data, whereas the outer layers interpret that data and manage effects (IO, non-determinism, etc) </dd> </dl> <p>This list is not exhaustive, but I’m trying to keep this focused. Also, I’m intentionally not taking this in the direction of Haskell, with typeclass hierarchies, higher-kinded types, and so on. I don’t believe cramming such constructs in would benefit Python programs in practice. Furthermore, I won’t be talking about effects and hexagonal architecture in this post.</p> <p>The examples are all type-annotated and checked with Pyright. You could do all of this without static type-checking, as far as I know.</p> <p>Finally, note that I consider myself a Python rookie, as I mostly use it for small tools and scripts. The largest program I’ve written in Python is <a href="https://github.com/quickstrom/quickstrom">Quickstrom</a>. This post is meant to inspire and trigger new ideas, not to tell anyone how to write Python code.</p> <p>All right, let’s get started and see what’s possible!</p> <h2 id="preliminaries">Preliminaries</h2> <p>First, let’s get some boilerplate stuff out of the way. I’m running Python 3.12. Some of the things I’ll show can be done with earlier versions, but not all.</p> <p>We’ll not be using any external packages, only modules from the standard library:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> <span class="op">*</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> dataclasses <span class="im">import</span> dataclass</span></code></pre></div> <p>You might not want to use wildcard imports in more serious programs, but it’s acceptable for these examples.</p> <h2 id="pattern-matching">Pattern Matching</h2> <p>Let’s start with a classic example from the functional programming world: an evaluator for a simple expression-based language. It only supports a few operations in order to keep it simple. First, we the different types of expressions there are using dataclasses and a union type:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="bu">type</span> Expr <span class="op">=</span> <span class="bu">int</span> <span class="op">|</span> <span class="bu">bool</span> <span class="op">|</span> <span class="bu">str</span> <span class="op">|</span> BinOp <span class="op">|</span> Let <span class="op">|</span> If</span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span> <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> BinOp:</span> <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> op: Literal[<span class="st">&quot;&lt;&quot;</span>] <span class="op">|</span> Literal[<span class="st">&quot;&gt;&quot;</span>]</span> <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> lhs: Expr</span> <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> rhs: Expr</span> <span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span> <span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Let:</span> <span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> name: <span class="bu">str</span></span> <span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> value: Expr</span> <span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> body: Expr</span> <span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span> <span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> If:</span> <span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a> cond: Expr</span> <span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> t: Expr</span> <span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a> f: Expr</span></code></pre></div> <p>The syntax for type aliases and the union type operator (<code>|</code>) are both new additions. You could create type aliases before using regular top-level bindings, but mutually recursive types required some types to be enclosed in strings. Otherwise, Python would complain that the second type (e.g. <code>BinOp</code> in the code above) wasn’t defined. It’s a bit cleaner now.</p> <p>Note that we existing primitive types from Python (<code>int</code>, <code>bool</code>, and <code>str</code>), combined with dataclasses for complex expressions. The <code>str</code> is interpreted as a reference to a name bound in the lexical scope, not as a string literal, as we’ll see in the following snippet.</p> <p>The evaluator tracks bindings in the <code>Env</code>. Evaluating an expression results in a value that is either an integer or a boolean.</p> <div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="bu">type</span> Env <span class="op">=</span> <span class="bu">dict</span>[<span class="bu">str</span>, Value]</span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="bu">type</span> Value <span class="op">=</span> <span class="bu">int</span> <span class="op">|</span> <span class="bu">bool</span></span></code></pre></div> <p>Now, let’s look at the <code>eval</code> function. Here we pattern-match on the expression, which is a union. For literals, we just return the value:</p> <div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> <span class="bu">eval</span>(env: Env, expr: Expr) <span class="op">-&gt;</span> Value:</span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> expr:</span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="bu">int</span>() <span class="op">|</span> <span class="bu">bool</span>():</span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> expr</span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> ...</span></code></pre></div> <p>References are looked up in the environment:</p> <div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="cf">case</span> <span class="bu">str</span>():</span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> env[expr]</span></code></pre></div> <p>Let-bindings create a new environment with the new binding:</p> <div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="cf">case</span> Let(name, value, body):</span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> new_env <span class="op">=</span> env <span class="op">|</span> {name: <span class="bu">eval</span>(env, value)}</span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">eval</span>(new_env, body)</span></code></pre></div> <p>Finally, the <code>BinOp</code> and <code>If</code> branches pattern-match on the evaluated nested expressions to make sure they’re of the correct types:</p> <div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="cf">case</span> BinOp(op, lhs, rhs):</span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> l <span class="op">=</span> <span class="bu">eval</span>(env, lhs)</span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> r <span class="op">=</span> <span class="bu">eval</span>(env, rhs)</span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> op, l, r:</span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="st">&quot;&lt;&quot;</span>, <span class="bu">int</span>(), <span class="bu">int</span>():</span> <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> l <span class="op">&lt;</span> r</span> <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="st">&quot;&gt;&quot;</span>, <span class="bu">int</span>(), <span class="bu">int</span>():</span> <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> l <span class="op">&gt;</span> r</span> <span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> _:</span> <span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">ValueError</span>(</span> <span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Invalid binary operation </span><span class="sc">{</span>op<span class="sc">}</span><span class="ss"> on </span><span class="sc">{</span>lhs<span class="sc">}</span><span class="ss"> and </span><span class="sc">{</span>rhs<span class="sc">}</span><span class="ss">&quot;</span></span> <span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a></span> <span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a><span class="cf">case</span> If(cond, t, f):</span> <span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> <span class="bu">eval</span>(env, cond):</span> <span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="va">True</span>:</span> <span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">eval</span>(env, t)</span> <span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="va">False</span>:</span> <span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">eval</span>(env, f)</span> <span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> c:</span> <span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="ss">f&quot;Expected bool condition, got: </span><span class="sc">{</span>c<span class="sc">}</span><span class="ss">&quot;</span>)</span></code></pre></div> <p>All right, let’s try it out:</p> <div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> example <span class="op">=</span> Let(<span class="st">&quot;x&quot;</span>, <span class="dv">1</span>, If(BinOp(<span class="st">&quot;&lt;&quot;</span>, <span class="st">&quot;x&quot;</span>, <span class="dv">2</span>), <span class="dv">42</span>, <span class="dv">0</span>))</span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> <span class="bu">eval</span>({}, example)</span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="dv">42</span></span></code></pre></div> <p>Nice! But this is far from a robust evaluator. If we run it with a deep enough expression, we’d get a <code>RecursionError</code> saying that the maximum recursion depth was exceeded. This is a commonly occurring problem when writing recursive functions in Python.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> The <code>eval</code> function could be rewritten with an explicit stack for operations and operands, but it’s a bit fiddly.</p> <p>In some cases, you can restructure a recursive function as tail-recursive, and then manually convert it to a loop. Perhaps you could automatically optimize tail-calls, or use a <a href="https://pypi.org/project/trampoline/">trampoline</a>. Some solutions avoid stack overflows, at the expense of increased heap memory usage. In simpler cases, combinators like <code>map</code> and <code>reduce</code> might suffice, instead of explicit recursion.</p> <p>Either way, recursive functions and stack overflow is something to watch out for.</p> <h2 id="generics">Generics</h2> <p>Since Python 3.12, it’s also much nicer to work with generic types. Previously, you had to define type variables before using them in type signatures. This felt very awkward to me.</p> <p>Let’s look at an example that models a rose tree data type. To spice it up a little, I’m including a <code>map</code> function for both types of the tree nodes. This is equivalent to <a href="https://hackage.haskell.org/package/base-4.20.0.0/docs/Data-Functor.html#v:fmap"><code>fmap</code></a> in Haskell, but without the typeclass and higher-order types.</p> <div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="bu">type</span> RoseTree[T] <span class="op">=</span> Branch[T] <span class="op">|</span> Leaf[T]</span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span> <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Branch[A]:</span> <span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> branches: <span class="bu">list</span>[RoseTree[A]]</span> <span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="bu">map</span>[B](<span class="va">self</span>, f: Callable[[A], B]) <span class="op">-&gt;</span> Branch[B]:</span> <span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> Branch([b.<span class="bu">map</span>(f) <span class="cf">for</span> b <span class="kw">in</span> <span class="va">self</span>.branches])</span> <span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span> <span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Leaf[A]:</span> <span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a> value: A</span> <span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="bu">map</span>[B](<span class="va">self</span>, f: Callable[[A], B]) <span class="op">-&gt;</span> Leaf[B]:</span> <span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> Leaf(f(<span class="va">self</span>.value))</span></code></pre></div> <p>Let’s print these trees using pattern matching. Here’s a function that’s written as a loop, maintaining a list of remaining sub-trees to print:</p> <div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> print_tree[T](tree: RoseTree[T]):</span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> trees <span class="op">=</span> [(tree, <span class="dv">0</span>)]</span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> trees:</span> <span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> trees.pop(<span class="dv">0</span>):</span> <span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> Branch(branches), level:</span> <span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; &quot;</span> <span class="op">*</span> level <span class="op">*</span> <span class="dv">2</span> <span class="op">+</span> <span class="st">&quot;*&quot;</span>)</span> <span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> trees <span class="op">=</span> [(branch, level <span class="op">+</span> <span class="dv">1</span>) <span class="cf">for</span> branch <span class="kw">in</span> branches] <span class="op">+</span> trees</span> <span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> Leaf(value), level:</span> <span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; &quot;</span> <span class="op">*</span> level <span class="op">*</span> <span class="dv">2</span> <span class="op">+</span> <span class="st">&quot;- &quot;</span> <span class="op">+</span> <span class="bu">repr</span>(value))</span></code></pre></div> <p>It could be even simpler using plain recursion, but then we could run into stack depth issues again. Anyway, let’s try it out:</p> <div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>example <span class="op">=</span> Branch(</span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> [</span> <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> Leaf(<span class="dv">1</span>),</span> <span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> Leaf(<span class="dv">2</span>),</span> <span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> Branch([Leaf(<span class="dv">3</span>), Leaf(<span class="dv">4</span>)]),</span> <span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> Branch([Leaf(<span class="dv">5</span>), Leaf(<span class="dv">6</span>)]),</span> <span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> Leaf(<span class="dv">7</span>),</span> <span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> ]</span> <span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>)</span> <span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> print_tree(example.<span class="bu">map</span>(<span class="bu">str</span>))</span> <span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a><span class="op">*</span></span> <span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a> <span class="op">-</span> <span class="st">&#39;1&#39;</span></span> <span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a> <span class="op">-</span> <span class="st">&#39;2&#39;</span></span> <span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a> <span class="op">*</span></span> <span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a> <span class="op">-</span> <span class="st">&#39;3&#39;</span></span> <span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a> <span class="op">-</span> <span class="st">&#39;4&#39;</span></span> <span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a> <span class="op">*</span></span> <span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a> <span class="op">-</span> <span class="st">&#39;5&#39;</span></span> <span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a> <span class="op">-</span> <span class="st">&#39;6&#39;</span></span> <span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a> <span class="op">-</span> <span class="st">&#39;7&#39;</span></span></code></pre></div> <p>As you can see from the <code>repr</code> being printed, all the values are mapped to strings.</p> <h2 id="protocols">Protocols</h2> <p>As a last example, I’d like to show how you can do basic structural subtyping using <em>protocols</em>. This is useful in cases where you don’t want to define all variants of a union in a single place. For instance, you might have many types of events that can be emitted in an application. Centrally defining each type of event adds unwanted coupling. Breaking apart a base class for events and the code that later on consumes the events decreases cohesion. In such cases, a protocol might be a better option.</p> <p>We’ll need a new import:</p> <div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> datetime <span class="im">import</span> datetime</span></code></pre></div> <p>Now, consider the following events in module A:</p> <div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span> <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Increment[Time]:</span> <span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">id</span>: <span class="bu">str</span></span> <span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a> time: Time</span> <span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> description(<span class="va">self</span>: Self) <span class="op">-&gt;</span> <span class="bu">str</span>:</span> <span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="st">&quot;Incremented counter&quot;</span></span> <span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span> <span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Reset[Time]:</span> <span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a> <span class="bu">id</span>: <span class="bu">str</span></span> <span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a> time: Time</span> <span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a></span> <span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> description(<span class="va">self</span>: Self) <span class="op">-&gt;</span> <span class="bu">str</span>:</span> <span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="st">&quot;Reset counter&quot;</span></span></code></pre></div> <p>We made them generic just to showcase the combination of protocols and generics. The <code>Time</code> type parameter isn’t instantiated in any other way than <code>datetime</code> in this example.</p> <p>In another module B — that doesn’t depend on A, and isn’t depended upon by A — the protocol is defined, along with the <code>log_event</code> function:</p> <div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Event[Time](Protocol):</span> <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> <span class="bu">id</span>: <span class="bu">str</span></span> <span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> time: Time</span> <span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> description(<span class="va">self</span>: Self) <span class="op">-&gt;</span> <span class="bu">str</span>: ...</span> <span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> log_event(event: Event[datetime]):</span> <span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Got </span><span class="sc">{</span>event<span class="sc">.</span><span class="bu">id</span><span class="sc">}</span><span class="ss"> at </span><span class="sc">{</span>event<span class="sc">.</span>time<span class="sc">}</span><span class="ss">: </span><span class="sc">{</span>event<span class="sc">.</span>description()<span class="sc">}</span><span class="ss">&quot;</span>)</span></code></pre></div> <p><code>Increment</code> and <code>Decrement</code> both implement the <code>Event</code> protocol by virtue of being structurally compatible. They can both be passed to <code>log_event</code>:</p> <div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>log_event(</span> <span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> Increment(<span class="st">&quot;foo&quot;</span>, datetime.now()),</span> <span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>)</span></code></pre></div> <p>If we annotate the <code>Event</code> protocol with <code>@runtime_checkable</code>, we can check it with <code>isinstance</code> and use it in match cases:</p> <div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="at">@runtime_checkable</span></span> <span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Event[Time](Protocol):</span> <span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> ...</span> <span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> log(x: Any):</span> <span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> x:</span> <span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> Event() <span class="cf">if</span> <span class="bu">isinstance</span>(datetime, x.time):</span> <span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a> log_event(x)</span> <span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> _:</span> <span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(x)</span></code></pre></div> <p>Pretty neat!</p> <p>That’s all I have for now. Maybe more Python hacking and blog posts will pop up if there’s interest. I’m positive to the evolution of Python and functional programming, as it’s something I use quite regularly.</p> <p>Join the discussion on <a href="https://x.com/owickstrom/status/1793749093900284059">Twitter</a>, <a href="https://news.ycombinator.com/item?id=40459944">Hacker News</a>, or <a href="https://lobste.rs/s/hphovk/statically_typed_functional">Lobsters</a>.</p> <p><em>Thank you <a href="https://x.com/tusharisanerd"><span class="citation" data-cites="tusharisanerd">@tusharisanerd</span></a> for reviewing a draft of this post.</em></p> <section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"> <hr /> <ol> <li id="fn1"><p>See <a href="https://eli.thegreenplace.net/2017/on-recursion-continuations-and-trampolines/">On Recursion, Continuations and Trampolines</a> for more in-depth explanations of various solutions to recursive functions and the stack.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li> </ol> </section>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2021-05-03-specifying-state-machines-with-temporal-logic.html</id>
    <title>Specifying State Machines with Temporal Logic</title>
    <link href="https://wickstrom.tech/2021-05-03-specifying-state-machines-with-temporal-logic.html"/>
    <published>2021-05-05T00:00:00+02:00</published>
    <updated>2021-05-05T00:00:00+02:00</updated>
    <summary>Quickstrom uses linear temporal logic (LTL) for specifying web
applications. When explaining how it works, I’ve found that the basics
of LTL are intuitive to newcomers. On the other hand, it’s not obvious
how to specify real-world systems using LTL. That’s why I’m sharing some
of my learnings and ideas from the past year in the form of blog posts.</summary>
    <content type="html"><![CDATA[
<p><a href="https://quickstrom.io">Quickstrom</a> uses linear temporal logic (LTL) for specifying web applications. When explaining how it works, I’ve found that the basics of LTL are intuitive to newcomers. On the other hand, it’s not obvious how to specify real-world systems using LTL. That’s why I’m sharing some of my learnings and ideas from the past year in the form of blog posts.</p> <p>This post focuses on how to use LTL to specify systems in terms of state machines. It’s a brief overview that avoids going into too much detail. For more information on how to test web applications using such specifications, see <a href="https://docs.quickstrom.io">the Quickstrom documentation</a>.</p> <p>To avoid possible confusion, I want to start by pointing out that a state machine specification in this context is not the same as a model in TLA+ (or similar modeling languages.) We’re not building a model to prove or check properties against. Rather, we’re defining properties in terms of state machine transitions, and the end goal is to test actual system behavior (e.g. web applications, desktop applications, APIs) by checking that recorded traces match our specifications.</p> <h2 id="linear-temporal-logic">Linear Temporal Logic</h2> <p>In this post, we’ll be using an LTL language. It’s a sketch of a future specification language for Quickstrom.</p> <p>A <em>formula</em> (plural <em>formulae</em>) is a logical expression that evaluates to true or false. We have the constants:</p> <ul> <li><code class="specstrom">true</code></li> <li><code class="specstrom">false</code></li> </ul> <p>We combine formulae using the logical connectives, e.g:</p> <ul> <li><code class="specstrom">&amp;&amp;</code></li> <li><code class="specstrom">||</code></li> <li><code class="specstrom">not</code></li> <li><code class="specstrom">==&gt;</code></li> </ul> <p>The <code class="specstrom">==&gt;</code> operator is <em>implication</em>. So far we have propositional logic, but we need a few more things.</p> <h2 id="temporal-operators">Temporal Operators</h2> <p>At the core of our language we have the notion of state. Systems change state over time, and we’d like to express that in our specifications. But the formulae we’ve seen so far do not deal with time. For that, we use temporal operators.</p> <p>To illustrate how the temporal operators work, I’ll use diagrams to visualize <em>traces</em> (sequences of states). A filled circle (●) denotes a state in which the formula is true, and an empty circle (○) denotes a state where the formula is false.</p> <p>For example, let’s say we have two formulae, <code class="specstrom">P</code> and <code class="specstrom">Q</code>, where:</p> <ul> <li><code class="specstrom">P</code> is true in the first and second state</li> <li><code class="specstrom">Q</code> is true in the second state</li> </ul> <p>Both formulae are false in all other states. The formulae and trace would be visualized as follows:</p> <pre class="specstrom"><code>P ●───●───○ Q ○───●───○</code></pre> <p>Note that in these diagrams, we assume that the last state repeats forever. This might seem a bit weird, but drawing an infinite number of states is problematic.</p> <p>All of the examples explaining operators have links to the <a href="https://quickstrom.github.io/ltl-visualizer/">Linear Temporal Logic Visualizer</a>, in which you can interactively experiment with the formulae. The syntax is not the same as in the article, but hopefully that’s not a problem.</p> <h3 id="next">Next</h3> <p>The <code class="specstrom">next</code> operator takes a formula as an argument and evaluates it in the next state.</p> <figure> <pre class="specstrom"><code>next P ●───○───○ P ○───●───○</code></pre> <figcaption> <a href="https://quickstrom.github.io/ltl-visualizer/?formulae=next%28P%29&amp;trace=%7CP%7C%7C" target="_blank">Open in LTL Visualizer</a> </figcaption> </figure> <p>The <code class="specstrom">next</code> operator is relative to the current state, not the first state in the trace. This means that we can nest <code class="specstrom">next</code>s to reach further into the future.</p> <figure> <pre class="specstrom"><code>next (next P) ●───●───○───○───○ next P ○───●───●───○───○ P ○───○───●───●───○</code></pre> <figcaption> <a href="https://quickstrom.github.io/ltl-visualizer/?formulae=next%28P%29%7Cnext%28next%28P%29%29&amp;trace=%7C%7CP%7CP%7C" target="_blank">Open in LTL Visualizer</a> </figcaption> </figure> <h3 id="next-for-state-transitions">Next for State Transitions</h3> <p>All right, time for a more concrete example, something we’ll evolve throughout this post. Let’s say we have a formula <code class="specstrom">gdprConsentIsVisible</code> which is true when the GDPR consent screen is visible. We specify that the screen should be visible in the current and next state like so:</p> <pre class="specstrom"><code>gdprConsentIsVisible &amp;&amp; next gdprConsentIsVisible</code></pre> <p>A pair of consecutive states is called a <em>step</em>. When specifying state machines, we use the <code class="specstrom">next</code> operator to describe state transitions. A state transition formula is a logical predicate on a step.</p> <p>In the GDPR example above, we said that the consent screen should stay visible in both states of the step. If we want to describe a state change in the consent screen’s visibility, we can say:</p> <pre class="specstrom"><code>gdprConsentIsVisible &amp;&amp; next (not gdprConsentIsVisible)</code></pre> <p>The formula describes a state transition from a visible to a hidden consent screen.</p> <h3 id="always">Always</h3> <p>But interesting state machines usually have more than one possible transition, and interesting behaviors likely contain multiple steps.</p> <p>While we could nest formulae containing the <code class="specstrom">next</code> operator, we’d be stuck with specifications only describing a finite number of transitions.</p> <p>Consider the following, where we like to state that the GDPR consent screen should always be visible:</p> <pre class="specstrom"><code>gdprConsentIsVisible &amp;&amp; next (gdprConsentIsVisible &amp;&amp; next ...)</code></pre> <p>This doesn’t work for state machines with cycles, i.e. with possibly infinite traces, because we can only nest a finite number of <code class="specstrom">next</code> operators. We want state machine specifications that describe any number of transitions.</p> <p>This is where we pick up the <code class="specstrom">always</code> operator. It takes a formula as an argument, and it’s true if the given formula is true in the current state and in all future states.</p> <figure> <pre class="specstrom"><code>always P ●───●───●───●───● P ●───●───●───●───● always Q ○───○───●───●───● Q ●───○───●───●───●</code></pre> <figcaption> <a href="https://quickstrom.github.io/ltl-visualizer/?formulae=always%28P%29%7Calways%28Q%29&amp;trace=PQ%7CP%7CPQ%7CPQ%7CPQ" target="_blank">Open in LTL Visualizer</a> </figcaption> </figure> <p>Note how <code class="specstrom">always Q</code> is true in the third state and onwards, because that’s when <code class="specstrom">Q</code> becomes true in the current and all future states.</p> <p>Let’s revisit the always-visible consent screen specification. Instead of trying to nest an infinite amount of <code class="specstrom">next</code> formulae, we instead say:</p> <pre class="specstrom"><code>always gdprConsentIsVisible</code></pre> <p>Neat! This is called an <em>invariant property</em>. Invariants are assertions on individual states, and an invariant property says that it must hold for every state in the trace.</p> <h3 id="always-for-state-machines">Always for State Machines</h3> <p>Now, let’s up our game. To specify the system as a state machine, we can combine transitions with disjunction (<code class="specstrom">||</code>) and the <code class="specstrom">always</code> operator. First, we define the individual transition formulae <code class="specstrom">open</code> and <code class="specstrom">close</code>:</p> <pre class="specstrom"><code>let open = not gdprConsentIsVisible &amp;&amp; next gdprConsentIsVisible; let close = gdprConsentIsVisible &amp;&amp; next (not gdprConsentIsVisible);</code></pre> <p>Our state machine formula says that it always transitions as described by <code class="specstrom">open</code> or <code>close</code>:</p> <pre class="specstrom"><code>always (open || close)</code></pre> <p>We have a state machine specification! Note that this specification only allows for transitions where the visibility of the consent screen changes back and forth.</p> <p>So far we’ve only seen examples of <em>safety properties</em>. Those are properties that specify that “nothing bad happens.” But we also want to specify that systems somehow make progress. The following two temporal operators let us specify <em>liveness properties</em>, i.e. “good things eventually happen.”</p> <p>Quickstrom does not support liveness properties yet.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p> <h3 id="eventually">Eventually</h3> <p>We’ve used <code class="specstrom">next</code> to specify transitions, and <code class="specstrom">always</code> to specify invariants and state machines. But we might also want to use liveness properties in our specifications. In this case, we are not talking about specific steps, but rather goals.</p> <p>The temporal operator <code class="specstrom">eventually</code> takes a formula as an argument, and it’s true if the given formula is true in the current or any future state.</p> <figure> <pre class="specstrom"><code>eventually P ○───○───○───○───○ P ○───○───○───○───○ eventually Q ●───●───●───●───○ Q ○───○───○───●───○</code></pre> <figcaption> <a href="https://quickstrom.github.io/ltl-visualizer/?formulae=eventually%28P%29%7Ceventually%28Q%29&amp;trace=%7C%7C%7CQ%7C" target="_blank">Open in LTL Visualizer</a> </figcaption> </figure> <p>For instance, we could say that the consent screen should initially be visible and eventually be hidden:</p> <pre class="specstrom"><code>gdprConsentIsVisible &amp;&amp; eventually (not gdprConsentIsVisible)</code></pre> <p>This doesn’t say that it <em>stays</em> hidden. It may become visible again, and our specification would allow that. To specify that it should stay hidden, we use a combination of <code class="specstrom">eventually</code> and <code class="specstrom">always</code>:</p> <pre class="specstrom"><code>gdprConsentIsVisible &amp;&amp; eventually (always (not gdprConsentIsVisible))</code></pre> <p>Let’s look at a diagram to understand this combination of temporal operators better:</p> <figure> <pre class="specstrom"><code>eventually (always P) ○───○───○───○───○ P ○───○───●───●───○ eventually (always Q) ●───●───●───●───● Q ○───○───●───●───●</code></pre> <figcaption> <a href="https://quickstrom.github.io/ltl-visualizer/?formulae=eventually%28always%28P%29%29%7Ceventually%28always%28Q%29%29&amp;trace=%7C%7CPQ%7CPQ%7CQ" target="_blank">Open in LTL Visualizer</a> </figcaption> </figure> <p>The formula <code class="specstrom">eventually (always P)</code> is not true in any state, because <code>P</code> never starts being true forever. The other formula, <code class="specstrom">eventually (always Q)</code>, is true in all states because <code>Q</code> becomes true forever in the third state.</p> <h3 id="until">Until</h3> <p>The last temporal operator I want to discuss is <code class="specstrom">until</code>. For <code class="specstrom">P until Q</code> to be true, <code>P</code> must be true until <code>Q</code> becomes true.</p> <figure> <pre class="specstrom"><code>P until Q ●───●───●───●───○ P ●───●───○───○───○ Q ○───○───●───●───○</code></pre> <figcaption> <a href="https://quickstrom.github.io/ltl-visualizer/?formulae=until%28P%2C+Q%29&amp;trace=P%7CP%7CQ%7CQ%7C" target="_blank">Open in LTL Visualizer</a> </figcaption> </figure> <p>Just as with the <code class="specstrom">eventually</code> operator, the stop condition (<code>Q</code>) doesn’t have to stay true forever, but it has to be true at least once.</p> <p>The <code class="specstrom">until</code> operator is more expressive than <code class="specstrom">always</code> and <code class="specstrom">eventually</code>, and they can both be defined using <code class="specstrom">until</code>.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p> <p>Anyway, let’s get back to our running example. Suppose we have another formula <code>supportChatVisible</code> that is true when the support chat button is shown. We want to make sure it doesn’t show up until after the GDPR consent screen is closed:</p> <pre class="specstrom"><code>not supportChatVisible until not gdprConsentIsVisible</code></pre> <p>The negations make it a bit harder to read, but it’s equivalent to the informal statement: “the support chat button is hidden at least until the GDPR consent screen is hidden.” It doesn’t demand that the support chat button is ever visible, though. For that, we instead say:</p> <pre class="specstrom"><code>gdprConsentIsVisible until (supportChatVisible &amp;&amp; not gdprConsentIsVisible)</code></pre> <p>In this formula, <code>supportChatVisible</code> has to become true eventually, and at that point the consent screen must be hidden.</p> <h3 id="until-for-state-machines">Until for State Machines</h3> <p>We can use the <code class="specstrom">until</code> operator to define a state machine formula where the final transition is more explicit.</p> <p>Let’s say we want to specify the GDPR consent screen more rigorously. Suppose we already have the possible state transition formulae defined:</p> <ul> <li><code>allowCollectedData</code></li> <li><code>disallowCollectedData</code></li> <li><code>submit</code></li> </ul> <p>We can then put together the state machine formula:</p> <pre class="specstrom"><code>let gdprConsentStateMachine = gdprConsentIsVisible &amp;&amp; (allowCollectedData || disallowCollectedData) until (submit &amp;&amp; next (not gdprConsentIsVisible));</code></pre> <p>In this formula we allow any number of <code>allowCollectedData</code> or <code>disallowCollectedData</code> transitions, until the final <code>submit</code> resulting in a closed consent screen.</p> <h2 id="whats-next">What’s next?</h2> <p>We’ve looked at some temporal operators in LTL, and how to use them to specify state machines. I’m hoping this post has given you some ideas and inspiration!</p> <p>Another blog post worth checking out is <a href="https://hillelwayne.com/post/action-properties/">TLA+ Action Properties</a> by Hillel Wayne. It’s written specifically for TLA+, but most of the concepts are applicable to LTL and Quickstrom-style specifications.</p> <p>I intend to write follow-ups, covering atomic propositions, queries, actions, and events. If you want to comment, there are threads on <a href="https://github.com/quickstrom/quickstrom/discussions/101">GitHub</a>, <a href="https://twitter.com/owickstrom/status/1389158259815235584">Twitter</a>, and on <a href="https://lobste.rs/s/uifucu/specifying_state_machines_with_temporal">Lobsters</a>. You may also want to <a href="https://github.com/sponsors/owickstrom">sponsor my work</a>.</p> <p><em>Thank you <a href="https://twitter.com/vitorenesduarte">Vitor Enes</a>, <a href="https://twitter.com/andreymokhov">Andrey Mokhov</a>, <a href="https://twitter.com/pascalpoizat">Pascal Poizat</a>, and <a href="https://twitter.com/kamatsu8">Liam O’Connor</a> for reviewing drafts of this post.</em></p> <h2 id="edits">Edits</h2> <ul> <li><strong>2021-05-28</strong>: Added links to the <a href="https://quickstrom.github.io/ltl-visualizer/">Linear Temporal Logic Visualizer</a> matching the relevant examples. Note that the syntax is different in the visualizer.</li> </ul> <h2 id="footnotes">Footnotes</h2> <!-- Future stuff: build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh Formulae build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh Atomic propositions build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh Implicit lifting build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh State-dependence build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh State selectors build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh Objects build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh Actions build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh Preconditions build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh Events build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh They are actions build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh Postconditions build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh What happened? build-feed.sh build-index.sh flake.lock flake.nix generate-redirects.sh LICENSE Makefile README.md src target watch.sh The `happened`{.specstrom} binding is a list of actions or events that happened between the last and the current state --> <section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"> <hr /> <ol> <li id="fn1"><p>A future version of Quickstrom will use a different flavor of LTL tailored for testing, and that way support liveness properties.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li> <li id="fn2"><p>We can define <code class="specstrom">eventually P = true until P</code>, and perhaps a bit harder to grasp, <code class="specstrom">always P = not (true until not P)</code>. Or we could say <code class="specstrom">always P = not (eventually not P)</code>.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li> </ol> </section>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2020-08-27-introducing-quickstrom-high-confidence-browser-testing.html</id>
    <title>Introducing Quickstrom: High-confidence browser testing</title>
    <link href="https://wickstrom.tech/2020-08-27-introducing-quickstrom-high-confidence-browser-testing.html"/>
    <published>2020-08-27T00:00:00+02:00</published>
    <updated>2020-08-27T00:00:00+02:00</updated>
    <summary>In the last post I shared the results from testing TodoMVC
implementations using WebCheck. The project has since been renamed
Quickstrom (thank you, Tom) and is now released as open source.</summary>
    <content type="html"><![CDATA[
<p>In <a href="https://wickstrom.tech/2020-07-02-the-todomvc-showdown-testing-with-webcheck.html">the last post</a> I shared the results from testing TodoMVC implementations using WebCheck. The project has since been renamed <em>Quickstrom</em> (thank you, <a href="https://twitter.com/am_i_tom/">Tom</a>) and is now released as open source.</p> <h2 id="what-is-quickstrom">What is Quickstrom?</h2> <p>Quickstrom is a new autonomous testing tool for the web. It can find problems in any type of web application that renders to the DOM. Quickstrom automatically explores your application and presents minimal failing examples. Focus your effort on understanding and specifying your system, and Quickstrom can test it for you.</p> <h2 id="past-and-future">Past and future</h2> <p>I started writing Quickstrom on April 2, 2020, about a week after our first child was born. Somehow that code compiled, and evolved into a capable testing tool. I’m now happy and excited to share it with everyone!</p> <p>In the future, when Quickstrom is more robust and has a greater mileage, I might build a commercial product on top of it. This one of the reasons I’ve chosen an AGPL-2.0 license for the code, and why contributors must sign a CLA before pull requests can be merged. The idea is to keep the CLI test runner AGPL forever, but I might need a license exception if I build a closed-source SaaS product later on.</p> <h2 id="learning-more">Learning more</h2> <p>Interested in Quickstrom? Start by checking out any of these resources:</p> <ul> <li><a href="https://quickstrom.io">Main website</a></li> <li><a href="https://docs.quickstrom.io">Project documentation</a>, including installation instructions and usage guides</li> <li><a href="https://github.com/quickstrom/quickstrom">Source code</a></li> </ul> <p>And keep an eye out for updates by signing up for <a href="https://buttondown.email/quickstrom">the newsletter</a>, or by following <a href="https://twitter.com/owickstrom">me on Twitter</a>. Documentation should be significantly improved soon.</p> <h2 id="comments">Comments</h2> <p>If you have any comments or questions, please reply to any of the following threads:</p> <ul> <li><a href="https://twitter.com/owickstrom/status/1299064145736798208">Twitter</a></li> <li><a href="https://lobste.rs/s/zrusmd/introducing_quickstrom_high_confidence">Lobste.rs</a></li> </ul>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2020-07-02-the-todomvc-showdown-testing-with-webcheck.html</id>
    <title>The TodoMVC Showdown: Testing with WebCheck</title>
    <link href="https://wickstrom.tech/2020-07-02-the-todomvc-showdown-testing-with-webcheck.html"/>
    <published>2020-07-02T00:00:00+02:00</published>
    <updated>2020-07-02T00:00:00+02:00</updated>
    <summary>In this post I’ll share the results from testing TodoMVC implementations
using my new testing tool named WebCheck. I’ll explain how the
specification works, what problems were found in the TodoMVC
implementations, and what this means for my project.</summary>
    <content type="html"><![CDATA[
<p>In this post I’ll share the results from testing <a href="http://todomvc.com/">TodoMVC</a> implementations using my new testing tool named <em>WebCheck</em>. I’ll explain how the specification works, what problems were found in the TodoMVC implementations, and what this means for my project.</p> <h2 id="webcheck">WebCheck</h2> <p>During the last three months I’ve used my spare time to build WebCheck. It’s a browser testing framework combining ideas from:</p> <ul> <li>property-based testing (PBT)</li> <li>TLA+ and linear temporal logic</li> <li>functional programming</li> </ul> <p>In WebCheck, you write a <em>specification</em> for your web application, instead of manually writing test cases. The specification describes the intended behavior as a finite-state machine and invariants, using logic formulae written in a language inspired by the <a href="https://www.google.com/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=&amp;ved=2ahUKEwj344KGga7qAhXlsosKHSXnAQUQFjAEegQIARAB&amp;url=https%3A%2F%2Flamport.azurewebsites.net%2Fpubs%2Flamport-actions.pdf&amp;usg=AOvVaw2BwOrtWoq4Z2Y129hhj3WV">temporal logic of actions</a> (PDF) from TLA+. WebCheck generates hundreds or thousands of tests and runs them to verify if your application is accepted by the specification.</p> <p>The tests generated by WebCheck are sequences of possible actions, determined from the current state of the DOM at the time of each action. You can think of WebCheck as exploring the state space automatically, based on DOM introspection. It can find user behaviors and problems that we, the biased and lazy humans, are unlikely to think of and to write tests for. Our job is instead to think about the requirements and to improve the specification over time.</p> <h3 id="specifications-vs-models">Specifications vs Models</h3> <p>In property-based testing, when testing state machines using models, the model should capture the essential complexity of the system under test (SUT). It needs to be functionally complete to be a useful oracle. For a system that is conceptually simple, e.g. a key-value database engine, this is not a problem. But for a system that is inherently complex, e.g. a business application with a big pile of rules, a useful model tends to grow <em>as complex</em> as the system itself.</p> <p>In WebCheck, the specification is not like such a model. You don’t have to implement a complete functional model of your system. You can leave out details and specify only the most important aspects of your application. As an example, I wrote a specification that states that “there should at no time be more than one spinner on a page”, and nothing else. Again, this is possible to specify in PBT in general, but not with <em>model-based</em> PBT, from what I’ve seen.</p> <h3 id="todomvc-as-a-benchmark">TodoMVC as a Benchmark</h3> <p>Since the start of this project, I’ve used the TodoMVC implementations as a benchmark of WebCheck, and developed a general specification for TodoMVC implementations. The TodoMVC contribution documentation has <a href="https://github.com/tastejs/todomvc/blob/master/app-spec.md#functionality">a high-level feature specification</a>, and the project has a Cypress test suite, but I was curious if I could find anything new using WebCheck.</p> <p>Early on, checking the mainstream framework implementations, I found that both the Angular and Mithril implementations were rejected by my specification, and I submitted <a href="https://github.com/tastejs/todomvc/issues/2116">an issue</a> in the TodoMVC issue tracker. Invigorated by the initial success, I decided to check the remaining implementations and gradually improve my specification.</p> <p>I’ve generalized the specification to work on nearly all the implementations listed on the TodoMVC website. Some of them use the old markup, which uses IDs instead of classes for most elements, so I had to support both variants.</p> <h2 id="the-specification">The Specification</h2> <p>Before looking at the tests results, you might want to have a look at the WebCheck specification that I’ve published as a gist:</p> <p><a href="https://gist.github.com/owickstrom/1a0698ef6a47df07dfc1fe59eda12983">TodoMVC.spec.purs</a></p> <p>The gist includes a brief introduction to the WebCheck specification language and how to write specifications. I’ll write proper documentation for the specification language eventually, but this can give you a taste of how it works, at least. I’ve excluded support for the old TodoMVC markup to keep the specification as simple as possible.</p> <p>The specification doesn’t cover all features of TodoMVC yet. Most notably, it leaves out the <a href="https://github.com/tastejs/todomvc/blob/master/app-spec.md#editing">editing mode</a> entirely. Further, it doesn’t cover the usage of local storage in TodoMVC, and local storage is disabled in WebCheck for now.</p> <p>I might refine the specification later, but I think I’ve found enough to motivate using WebCheck on TodoMVC applications. Further, this is likely how WebCheck would be used in other projects. You specify some things and you leave out others.</p> <p>The astute reader might have noticed that the specification language looks like PureScript. And it pretty much is PureScript, with some WebCheck-specific additions for temporal modalities and DOM queries. I decided not to write a custom DSL, and instead write a PureScript interpreter. That way, specification authors can use the tools and packages from the PureScript ecosystem. It works great so far!</p> <h2 id="test-results">Test Results</h2> <p>Below are the test results from running WebCheck and my TodoMVC specification on the examples listed on the TodoMVC website. I’ll use short descriptions of the problems (some of which are recurring), and explain in more detail further down.</p> <table style="width: 100%;" class="todomvc-results"> <thead> <tr> <th style="width: 1%;"> </th> <th> Example </th> <th> Problems/Notes </th> </tr> </thead> <tbody> <tr class="divider"> <td colspan="3"> Pure JavaScript </td> </tr> <tr> <td> ✓ </td> <td> Backbone.js </td> <td> </td> </tr> <tr> <td> ❌ </td> <td> AngularJS </td> <td> <ul> <li> Clears input field on filter change </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> Ember.js </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Dojo </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Knockback </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> CanJS </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Polymer </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> React </td> <td> </td> </tr> <tr> <td> ❌ </td> <td> Mithril </td> <td> <ul> <li> Clears input field on filter change </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> Vue </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> MarionetteJS </td> <td> </td> </tr> <tr class="divider"> <td colspan="3"> Compiled to JavaScript </td> </tr> <tr> <td> ✓ </td> <td> Kotlin + React </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Spine </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Dart </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> GWT </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Closure </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Elm </td> <td> </td> </tr> <tr> <td> ❌ </td> <td> AngularDart </td> <td> <ul> <li> Race condition on initialization </li> <li> Filters not implemented </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> TypeScript + Backbone.js </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> TypeScript + AngularJS </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> TypeScript + React </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Reagent </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Scala.js + React </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Scala.js + Binding.scala </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> js_of_ocaml </td> <td> </td> </tr> <tr> <td> – </td> <td> Humble + GopherJS </td> <td> <ul> <li> Missing/broken link </li> </ul> </td> </tr> <tr class="divider"> <td colspan="3"> Under evaluation by TodoMVC </td> </tr> <tr> <td> ✓ </td> <td> Backbone.js + RequireJS </td> <td> </td> </tr> <tr> <td> ❌ </td> <td> KnockoutJS + RequireJS </td> <td> <ul> <li> Inconsistent first render </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> AngularJS + RequireJS </td> <td> <ul> <li> Needs a custom <code>readyWhen</code> condition </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> CanJS + RequireJS </td> <td> </td> </tr> <tr> <td> ❌ </td> <td> Lavaca + RequireJS </td> <td> <ul> <li> Clears input field on filter change </li> </ul> </td> </tr> <tr> <td> ❌ </td> <td> cujoJS </td> <td> <ul> <li> Race condition on initialization </li> <li> Filters not implemented </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> Sammy.js </td> <td> </td> </tr> <tr> <td> – </td> <td> soma.js </td> <td> <ul> <li> Missing/broken link </li> </ul> </td> </tr> <tr> <td> ❌ </td> <td> DUEL </td> <td> <ul> <li> Clears input field on filter change </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> Kendo UI </td> <td> </td> </tr> <tr> <td> ❌ </td> <td> Dijon </td> <td> <ul> <li> Filters not implemented </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> Enyo + Backbone.js </td> <td> </td> </tr> <tr> <td> ❌ </td> <td> SAPUI5 </td> <td> <ul> <li> No input field </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> Exoskeleton </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Ractive.js </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> React + Alt </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> React + Backbone.js </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> Aurelia </td> <td> </td> </tr> <tr> <td> ❌ </td> <td> Angular 2.0 </td> <td> <ul> <li> Filters not implemented </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> Riot </td> <td> </td> </tr> <tr> <td> ✓ </td> <td> JSBlocks </td> <td> </td> </tr> <tr class="divider"> <td colspan="3"> Real-time </td> </tr> <tr> <td> – </td> <td> SocketStream </td> <td> <ul> <li> State cannot be cleared </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> Firebase + AngularJS </td> <td> </td> </tr> <tr class="divider"> <td colspan="3"> Node.js </td> </tr> <tr> <td> – </td> <td> Express + gcloud-node </td> <td> <ul> <li> Missing/broken link </li> </ul> </td> </tr> <tr class="divider"> <td colspan="3"> Non-framework implementations </td> </tr> <tr> <td> ❌ </td> <td> VanillaJS </td> <td> <ul> <li> Adds pending item on other interaction </li> </ul> </td> </tr> <tr> <td> ❌ </td> <td> VanillaJS ES6 </td> <td> <ul> <li> Adds pending item on other interaction </li> <li> <code>.todo-count strong</code> is missing </li> </ul> </td> </tr> <tr> <td> ✓ </td> <td> jQuery </td> <td> </td> </tr> </tbody> </table> <p><em>✓ Passed, ❌ Failed, – Not testable</em></p> <dl> <dt>Filters not implemented</dt> <dd> <p>There’s no way of switching between “All”, “Active”, and “Completed” items. This is specified in the TodoMVC documentation under <a href="https://github.com/tastejs/todomvc/blob/master/app-spec.md#routing">Routing</a>.</p> </dd> <dt>Race condition during initialization</dt> <dd> <p>The event listeners are attached some time after the <code>.new-todo</code> form is rendered. Although unlikely, if you’re quick enough you can focus the input, press <kbd>Return</kbd>, and post the form. This will navigate the user agent to the same page but with a query parameter, e.g. <code>index.html?text=</code>. In TodoMVC it’s not the end of the world, but there are systems where you do not want this to happen.</p> </dd> <dt>Inconsistent first render</dt> <dd> <p>The application briefly shows an inconsistent view, then renders the valid initial state. <em>KnockoutJS + RequireJS</em> shows an empty list items and “0 left” in the bottom, even though the footer <a href="https://github.com/tastejs/todomvc/blob/master/app-spec.md#no-todos">should be hidden when there are no items</a>.</p> </dd> <dt>Needs a custom <code>readyWhen</code> condition</dt> <dd> <p>The specification awaits an element matching <code>.todoapp</code> (or <code>#todoapp</code> for the old markup) in the DOM before taking any action. In this case, the implementation needs a modified specification that instead awaits a framework-specific class, e.g. <code>.ng-scope</code>. This is an inconvenience in testing the implementation using WebCheck, rather than an error.</p> </dd> <dt>No input field</dt> <dd> <p>There’s no input field to enter TODO items in. I’d argue this defeats the purpose of a TODO list application, and it’s <a href="https://github.com/tastejs/todomvc/blob/master/app-spec.md#new-todo">indeed specified in the official documentation</a>.</p> </dd> <dt>Adds pending item on other interaction</dt> <dd> <p>When there’s a pending item in the input field, and another action is taken (toggle all, change filter, etc), the pending item is submitted automatically without a <kbd>Return</kbd> key press.</p> </dd> <dt><code>.todo-count strong</code> element is missing</dt> <dd> <p>An element matching the selector <code>.todo-count strong</code> must be present in the DOM when there are items, showing the number of active items, as described <a href="https://github.com/tastejs/todomvc/blob/master/app-spec.md#counter">in the TodoMVC documentation</a>.</p> </dd> <dt>State cannot be cleared</dt> <dd> <p>This is not an implementation error, but an issue where the test implementation makes it hard to perform repeated isolated testing. State cannot (to my knowledge) be cleared between tests, and so isolation is broken. This points to a key requirement currently placed by WebCheck: the SUT must be stateless, with respect to a new private browser window. In future versions of WebCheck, I’ll add hooks to let the tester clear the system state before each test is run.</p> </dd> <dt>Missing/broken link</dt> <dd> <p>The listed implementation seems to be moved or decommissioned.</p> </dd> </dl> <p>Note that I can’t decide which of these problems are <em>bugs</em>. That’s up to the TodoMVC project maintainers. I see them as problems, or disagreements between the implementations and my specification. A good chunk of humility is in order when testing systems designed and built by others.</p> <h2 id="the-future-is-bright">The Future is Bright</h2> <p>I’m happy with how effective WebCheck has been so far, after only a few months of spare-time prototyping. Hopefully, I’ll have something more polished that I can make available soon. An open source tool that you can run yourself, a SaaS version with a subscription model, or maybe both. When that time comes, maybe WebCheck can be part of the TodoMVC project’s testing. And perhaps in your project?</p> <p>If you’re interested in WebCheck, please <a href="https://buttondown.email/quickstrom">sign up for the newsletter</a>. I’ll post regular project updates, and definitely no spam. You can also follow me <a href="https://twitter.com/owickstrom">on Twitter</a>.</p> <h2 id="comments">Comments</h2> <p>If you have any comments or questions, please reply to any of the following threads:</p> <ul> <li><a href="https://twitter.com/owickstrom/status/1278645144166825984">Twitter</a></li> <li><a href="https://lobste.rs/s/pqo6ou/todomvc_showdown_testing_with_webcheck">Lobste.rs</a></li> </ul> <p><em>Thanks to <a href="https://www.hillelwayne.com/">Hillel Wayne</a>, <a href="https://twitter.com/felixhgren">Felix Holmgren</a>, <a href="https://twitter.com/janiczek">Martin Janiczek</a>, <a href="https://twitter.com/am_i_tom">Tom Harding</a>, and <a href="https://twitter.com/MartinClausen8">Martin Clausen</a> for reviewing drafts of this post.</em></p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2019-11-17-time-travelling-and-fixing-bugs-with-property-based-testing.html</id>
    <title>Time Travelling and Fixing Bugs with Property-Based Testing</title>
    <link href="https://wickstrom.tech/2019-11-17-time-travelling-and-fixing-bugs-with-property-based-testing.html"/>
    <published>2019-11-17T00:00:00+01:00</published>
    <updated>2019-11-17T00:00:00+01:00</updated>
    <summary>Property-based testing (PBT) is a powerful testing technique that helps
us find edge cases and bugs in our software. A challenge in applying PBT
in practice is coming up with useful properties. This tutorial is based
on a simple but realistic system under test (SUT), aiming to show some
ways you can test and find bugs in such logic using PBT. It covers
refactoring, dealing with non-determinism, testing generators
themselves, number of examples to run, and coupling between tests and
implementation. The code is written in Haskell and the testing framework
used is Hedgehog.</summary>
    <content type="html"><![CDATA[
<p>Property-based testing (PBT) is a powerful testing technique that helps us find edge cases and bugs in our software. A challenge in applying PBT in practice is coming up with useful properties. This tutorial is based on a simple but realistic system under test (SUT), aiming to show some ways you can test and find bugs in such logic using PBT. It covers refactoring, dealing with non-determinism, testing generators themselves, number of examples to run, and coupling between tests and implementation. The code is written in Haskell and the testing framework used is <a href="http://hackage.haskell.org/package/hedgehog">Hedgehog</a>.</p> <p>This tutorial was originally written as a book chapter, and later extracted as a standalone piece. Since I’m not expecting to finish the PBT book any time soon, I decided to publish the chapter here.</p> <h2 id="system-under-test-user-signup-validation">System Under Test: User Signup Validation</h2> <p>The business logic we’ll test is the validation of a website’s user signup form. The website requires users to sign up before using the service. When signing up, a user must pick a valid username. Users must be between 18 and 150 years old.</p> <p>Stated formally, the validation rules are:</p> <p><span id="eq:validation-rules"><span class="math display">$$ \begin{aligned} 0 \leq \text{length}(\text{name}) \leq 50 \\ 18 \leq \text{age} \leq 150 \end{aligned} \qquad(1)$$</span></span></p> <p>The signup and its validation is already implemented by previous programmers. There have been user reports of strange behaviour, and we’re going to locate and fix the bugs using property tests.</p> <p>Poking around the codebase, we find the data type representing the form:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">SignupForm</span> <span class="ot">=</span> <span class="dt">SignupForm</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> {<span class="ot"> formName ::</span> <span class="dt">Text</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> formAge ::</span> <span class="dt">Int</span></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> } <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>)</span></code></pre></div> <p>And the existing validation logic, defined as <code>validateSignup</code>. We won’t dig into to the implementation yet, only its type signature:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>validateSignup</span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">SignupForm</span> <span class="ot">-&gt;</span> <span class="dt">Validation</span> (<span class="dt">NonEmpty</span> <span class="dt">SignupError</span>) <span class="dt">Signup</span></span></code></pre></div> <p>It’s a pure function, taking <code>SignupForm</code> data as an argument, and returning a <code>Validation</code> value. In case the form data is valid, it returns a <code>Signup</code> data structure. This data type resembles <code>SignupForm</code> in its structure, but refines the age as a <code>Natural</code> when valid:</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Signup</span> <span class="ot">=</span> <span class="dt">Signup</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> {<span class="ot"> name ::</span> <span class="dt">Text</span></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> age ::</span> <span class="dt">Natural</span></span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> } <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>)</span></code></pre></div> <p>In case the form data is invalid, <code>validateSignup</code> returns a non-empty list of <code>SignupError</code> values. <code>SignupError</code> is a union type of the possible validation errors:</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">SignupError</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">NameTooShort</span> <span class="dt">Text</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">NameTooLong</span> <span class="dt">Text</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">InvalidAge</span> <span class="dt">Int</span></span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>)</span></code></pre></div> <h3 id="the-validation-type">The Validation Type</h3> <p>The <code>Validation</code> type comes from the <a href="https://hackage.haskell.org/package/validation">validation</a> package. It’s parameterized by two types:</p> <ol type="1"> <li>the type of validation failures</li> <li>the type of a successfully validated value</li> </ol> <p>The <code>Validation</code> type is similar to the <a href="https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Either.html#t:Either">Either</a> type. The major difference is that it <em>accumulates</em> failures, rather than short-circuiting on the first failure. Failures are accumulated when combining multiple <code>Validation</code> values using <code>Applicative</code>.</p> <p>Using a non-empty list for failures in the <code>Validation</code> type is common practice. It means that if the validation fails, there’s at least one error value.</p> <h2 id="validation-property-tests">Validation Property Tests</h2> <p>Let’s add some property tests for the form validation, and explore the existing implementation. We begin in a new test module, and we’ll need a few imports:</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.List.NonEmpty</span> (<span class="dt">NonEmpty</span> (..))</span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Text</span> (<span class="dt">Text</span>)</span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Validation</span></span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Hedgehog</span></span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Hedgehog.Gen</span> <span class="kw">as</span> <span class="dt">Gen</span></span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Hedgehog.Range</span> <span class="kw">as</span> <span class="dt">Range</span></span></code></pre></div> <p>Also, we’ll need to import the implementation module:</p> <div class="sourceCode" id="cb6"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Validation</span></span></code></pre></div> <p>We’re now ready to define some property tests.</p> <h3 id="a-positive-property-test">A Positive Property Test</h3> <p>The first property test we’ll add is a <em>positive</em> test. That is, a test using only valid input data. This way, we know the form validation should always be successful. We define <code>prop_valid_signup_form_succeeds</code>:</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>prop_valid_signup_form_succeeds <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> genForm <span class="ot">=</span> <span class="dt">SignupForm</span> <span class="op">&lt;$&gt;</span> validName <span class="op">&lt;*&gt;</span> validAge ➊</span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> form <span class="ot">&lt;-</span> forAll genForm ➋</span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> validateSignup form <span class="kw">of</span> ➌</span> <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">Success</span>{} <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> failure&#39; <span class="ot">-&gt;</span> <span class="kw">do</span></span> <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> annotateShow failure&#39;</span> <span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> failure</span></code></pre></div> <p>First, we define <code>genForm</code> (➊), a generator producing form data with valid names and ages. Next, we generate <code>form</code> values from our defined generator (➋). Finally, we apply the <code>validateSignup</code> function and pattern match on the result (➌):</p> <ul> <li>In case it’s successful, we have the test pass with <code>pure ()</code></li> <li>In case it fails, we print the <code>failure'</code> and fail the test</li> </ul> <p>The <code>validName</code> and <code>validAge</code> generators are defined as follows:</p> <div class="sourceCode" id="cb8"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">validName ::</span> <span class="dt">Gen</span> <span class="dt">Text</span></span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>validName <span class="ot">=</span> Gen.text (Range.linear <span class="dv">1</span> <span class="dv">50</span>) Gen.alphaNum</span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="ot">validAge ::</span> <span class="dt">Gen</span> <span class="dt">Int</span></span> <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>validAge <span class="ot">=</span> Gen.integral (Range.linear <span class="dv">18</span> <span class="dv">150</span>)</span></code></pre></div> <p>Recall the validation rules (eq. 1). The ranges in these generators yielding valid form data are defined precisely in terms of the validation rules.</p> <p>The character generator used for names is <code>alphaNum</code>, meaning we’ll only generate names with alphabetic letters and numbers. If you’re comfortable with regular expressions, you can think of <code>genValidName</code> as producing values matching <code>[a-zA-Z0-9]+</code>.</p> <p>Let’s run some tests:</p> <pre class="hedgehog"><code>λ&gt; check prop_valid_signup_form_succeeds ✓ &lt;interactive&gt; passed 100 tests.</code></pre> <p>Hooray, it works.</p> <h3 id="negative-property-tests">Negative Property Tests</h3> <p>In addition to the positive test, we’ll add <em>negative</em> tests for the name and age, respectively. Opposite to positive tests, our negative tests will only use invalid input data. We can then expect the form validation to always fail.</p> <p>First, let’s test invalid names.</p> <div class="sourceCode" id="cb10"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>prop_invalid_name_fails <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> genForm <span class="ot">=</span> <span class="dt">SignupForm</span> <span class="op">&lt;$&gt;</span> invalidName <span class="op">&lt;*&gt;</span> validAge ➊</span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> form <span class="ot">&lt;-</span> forAll genForm</span> <span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> validateSignup form <span class="kw">of</span> ➋</span> <span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">NameTooLong</span>{} <span class="op">:|</span> []) <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">NameTooShort</span>{} <span class="op">:|</span> []) <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> other <span class="ot">-&gt;</span> <span class="kw">do</span> ➌</span> <span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> annotateShow other</span> <span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a> failure</span></code></pre></div> <p>Similar to our the positive property test, we define a generator <code>genForm</code> (➊). Note that we use <code>invalidName</code> instead of <code>validName</code>.</p> <p>Again, we pattern match on the result of applying <code>validateSignup</code> (➋). In this case we expect failure. Both <code>NameTooLong</code> and <code>NameTooShort</code> are expected failures. If we get anything else, the test fails (➌).</p> <p>The test for invalid age is similar, expect we use the <code>invalidAge</code> generator, and expect only <code>InvalidAge</code> validation failures:</p> <div class="sourceCode" id="cb11"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>prop_invalid_age_fails <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> genForm <span class="ot">=</span> <span class="dt">SignupForm</span> <span class="op">&lt;$&gt;</span> validName <span class="op">&lt;*&gt;</span> invalidAge</span> <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> form <span class="ot">&lt;-</span> forAll genForm</span> <span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> validateSignup form <span class="kw">of</span></span> <span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">InvalidAge</span>{} <span class="op">:|</span> []) <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> other <span class="ot">-&gt;</span> <span class="kw">do</span></span> <span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> annotateShow other</span> <span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> failure</span></code></pre></div> <p>The <code>invalidName</code> and <code>invalidAge</code> generators are also defined in terms of the validation rules (eq. 1), but with ranges ensuring no overlap with valid data:</p> <div class="sourceCode" id="cb12"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ot">invalidName ::</span> <span class="dt">Gen</span> <span class="dt">Text</span></span> <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>invalidName <span class="ot">=</span></span> <span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a> Gen.choice [<span class="fu">mempty</span>, Gen.text (Range.linear <span class="dv">51</span> <span class="dv">100</span>) Gen.alphaNum]</span> <span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="ot">invalidAge ::</span> <span class="dt">Gen</span> <span class="dt">Int</span></span> <span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>invalidAge <span class="ot">=</span> Gen.integral (Range.linear <span class="fu">minBound</span> <span class="dv">17</span>)</span></code></pre></div> <p>Let’s run our new property tests:</p> <pre class="hedgehog"><code>λ&gt; check prop_invalid_name_fails ✓ &lt;interactive&gt; passed 100 tests. λ&gt; check prop_invalid_age_fails ✓ &lt;interactive&gt; passed 100 tests.</code></pre> <p>All good? Maybe not. The astute reader might have noticed a problem with one of our generators. We’ll get back to that later.</p> <h3 id="accumulating-all-failures">Accumulating All Failures</h3> <p>When validating the form data, we want <em>all</em> failures returned to the user posting the form, rather than returning only one at a time. The <code>Validation</code> type accumulates failures when combined with <code>Applicative</code>, which is exactly what we want. Yet, while the hard work is handled by <code>Validation</code>, we still need to test that we’re correctly combining validations in <code>validateSignup</code>.</p> <p>We define a property test generating form data, where all fields are invalid (➊). It expects the form validation to fail, returning two failures (➋).</p> <div class="sourceCode" id="cb14"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>prop_two_failures_are_returned <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> genForm <span class="ot">=</span> <span class="dt">SignupForm</span> <span class="op">&lt;$&gt;</span> invalidName <span class="op">&lt;*&gt;</span> invalidAge ➊</span> <span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> form <span class="ot">&lt;-</span> forAll genForm</span> <span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> validateSignup form <span class="kw">of</span></span> <span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> failures <span class="op">|</span> <span class="fu">length</span> failures <span class="op">==</span> <span class="dv">2</span> <span class="ot">-&gt;</span> <span class="fu">pure</span> () ➋</span> <span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a> other <span class="ot">-&gt;</span> <span class="kw">do</span></span> <span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> annotateShow other</span> <span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> failure</span></code></pre></div> <p>This property is weak. It states nothing about <em>which</em> failures should be returned. We could assert that the validation failures are equal to some expected list. But how do we know if the name is too long or too short? I’m sure you’d be less thrilled if we replicated all of the validation logic in this test.</p> <p>Let’s define a slightly stronger property. We pattern match, extract the two failures (➊), and check that they’re not equal (➋).</p> <div class="sourceCode" id="cb15"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>prop_two_different_failures_are_returned <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> genForm <span class="ot">=</span> <span class="dt">SignupForm</span> <span class="op">&lt;$&gt;</span> invalidName <span class="op">&lt;*&gt;</span> invalidAge</span> <span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a> form <span class="ot">&lt;-</span> forAll genForm</span> <span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> validateSignup form <span class="kw">of</span></span> <span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (failure1 <span class="op">:|</span> [failure2]) <span class="ot">-&gt;</span> ➊</span> <span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a> failure1 <span class="op">/==</span> failure2 ➋</span> <span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a> other <span class="ot">-&gt;</span> <span class="kw">do</span></span> <span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a> annotateShow other</span> <span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a> failure</span></code></pre></div> <p>We’re still not being specific about which failures should be returned. But unlike <code>prop_two_failures_are_returned</code>, this property at least makes sure there are no duplicate failures.</p> <h2 id="the-value-of-a-property">The Value of a Property</h2> <p>Is there a faulty behaviour that would slip past <code>prop_two_different_failures_are_returned</code>? Sure. The implementation could have a typo or copy-paste error, and always return <code>NameTooLong</code> failures, even if the name is too short. Does this mean our property is bad? Broken? Useless?</p> <p>In itself, this property doesn’t give us strong confidence in the correctness of <code>validateSignup</code>. In conjunction with our other properties, however, it provides value. Together they make up a stronger test suite.</p> <p>Let’s look at it in another way. What are the <em>benefits</em> of weaker properties over stronger ones? In general, weak properties are beneficial in that they are:</p> <ol type="1"> <li>easier to define</li> <li>likely to catch simple mistakes early</li> <li>less coupled to the SUT</li> </ol> <p>A small investment in a set of weak property tests might catch a lot of mistakes. While they won’t precisely specify your system and catch the trickiest of edge cases, their power-to-weight ratio is compelling. Moreover, a set of weak properties is better than no properties at all. If you can’t formulate the strong property you’d like, instead start simple. Lure out some bugs, and improve the strength and specificity of your properties over time.</p> <p>Coming up with good properties is a skill. Practice, and you’ll get better at it.</p> <h2 id="testing-generators">Testing Generators</h2> <p>Remember how in <a href="#negative-property-tests">Negative Property Tests</a> we noted that there’s a problem? The issue is, we’re not covering all validation rules in our tests. But the problem is not in our property definitions. It’s in one of our <em>generators</em>, namely <code>genInvalidAge</code>. We’re now in a peculiar situation: we need to test our tests.</p> <p>One way to test a generator is to define a property specifically testing the values it generates. For example, if we have a generator <code>positive</code> that is meant to generate only positive integers, we can define a property that asserts that all generated integers are positive:</p> <div class="sourceCode" id="cb16"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="ot">positive ::</span> <span class="dt">Gen</span> <span class="dt">Int</span></span> <span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>positive <span class="ot">=</span> Gen.integral (Range.linear <span class="dv">1</span> <span class="fu">maxBound</span>)</span> <span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>prop_integers_are_positive <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a> n <span class="ot">&lt;-</span> forAll positive</span> <span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a> assert (n <span class="op">&gt;=</span> <span class="dv">1</span>)</span></code></pre></div> <p>We could use this technique to check that all values generated by <code>validAge</code> are valid. How about <code>invalidAge</code>? Can we check that it generates values such that all boundaries of our validation function are hit? No, not using this technique. Testing the correctness of a generator using a property can only find problems with <em>individual</em> generated values. It can’t perform assertions over <em>all</em> generated values. In that sense, it’s a <em>local</em> assertion.</p> <p>Instead, we’ll find the generator problem by capturing statistics on the generated values and performing <em>global</em> assertions. Hedgehog, and a few other PBT frameworks, can measure the occurrences of user-defined <em>labels</em>. A label in Hedgehog is a <code>Text</code> value, declared with an associated condition. When Hedgehog runs the tests, it records the percentage of tests in which the condition evaluates to <code>True</code>. After the test run is complete, we’re presented with a listing of percentages per label.</p> <p>We can even have Hedgehog fail the test unless a certain percentage is met. This way, we can declare minimum coverage requirements for the generators used in our property tests.</p> <h3 id="adding-coverage-checks">Adding Coverage Checks</h3> <p>Let’s check that we generate values covering enough cases, based on the validation rules in eq. 1 . In <code>prop_invalid_age_fails</code>, we use <code>cover</code> to ensure we generate values outside the boundaries of valid ages. 5% is enough for each, but realistically they could both get close to 50%.</p> <div class="sourceCode" id="cb17"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>prop_invalid_age_fails <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> genForm <span class="ot">=</span> <span class="dt">SignupForm</span> <span class="op">&lt;$&gt;</span> validName <span class="op">&lt;*&gt;</span> invalidAge</span> <span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> form <span class="ot">&lt;-</span> forAll genForm</span> <span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">5</span> <span class="st">&quot;too young&quot;</span> (formAge form <span class="op">&lt;=</span> <span class="dv">17</span>)</span> <span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">5</span> <span class="st">&quot;too old&quot;</span> (formAge form <span class="op">&gt;=</span> <span class="dv">151</span>)</span> <span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> validateSignup form <span class="kw">of</span></span> <span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">InvalidAge</span>{} <span class="op">:|</span> []) <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a> other <span class="ot">-&gt;</span> <span class="kw">do</span></span> <span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a> annotateShow other</span> <span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a> failure</span></code></pre></div> <p>Let’s run some tests again.</p> <pre class="hedgehog"><code>λ&gt; check prop_invalid_age_fails ✗ &lt;interactive&gt; failed after 100 tests. too young 100% ████████████████████ ✓ 5% ⚠ too old 0% ···················· ✗ 5% ┏━━ test/Validation/V1Test.hs ━━━ 63 ┃ prop_invalid_age_fails = property $ do 64 ┃ let genForm = SignupForm &lt;$&gt; validName &lt;*&gt; invalidAge 65 ┃ form &lt;- forAll genForm 66 ┃ cover 5 &quot;too young&quot; (formAge form &lt;= 17) 67 ┃ cover 5 &quot;too old&quot; (formAge form &gt;= 151) ┃ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ┃ │ Failed (0% coverage) 68 ┃ case validateSignup form of 69 ┃ Failure (InvalidAge{} :| []) -&gt; pure () 70 ┃ other -&gt; do 71 ┃ annotateShow other 72 ┃ failure Insufficient coverage.</code></pre> <p>100% too young and 0% too old. The <code>invalidAge</code> generator is clearly not good enough. Let’s have a look at its definition again:</p> <div class="sourceCode" id="cb19"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="ot">invalidAge ::</span> <span class="dt">Gen</span> <span class="dt">Int</span></span> <span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>invalidAge <span class="ot">=</span> Gen.integral (Range.linear <span class="fu">minBound</span> <span class="dv">17</span>)</span></code></pre></div> <p>We’re only generating invalid ages between the minimum bound of <code>Int</code> and <code>17</code>. Let’s fix that, by using <code>Gen.choice</code> and another generator for ages greater than 150:</p> <div class="sourceCode" id="cb20"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="ot">invalidAge ::</span> <span class="dt">Gen</span> <span class="dt">Int</span></span> <span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>invalidAge <span class="ot">=</span> Gen.choice</span> <span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> [ Gen.integral (Range.linear <span class="fu">minBound</span> <span class="dv">17</span>)</span> <span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a> , Gen.integral (Range.linear <span class="dv">151</span> <span class="fu">maxBound</span>)</span> <span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div> <p>Running tests again, the coverage check stops complaining. But there’s another problem:</p> <pre class="hedgehog"><code>λ&gt; check prop_invalid_age_fails ✗ &lt;interactive&gt; failed at test/Validation/V1Test.hs:75:7 after 3 tests and 2 shrinks. too young 67% █████████████▎······ ✓ 5% ⚠ too old 0% ···················· ✗ 5% ┏━━ test/Validation/V1Test.hs ━━━ 66 ┃ prop_invalid_age_fails = property $ do 67 ┃ let genForm = SignupForm &lt;$&gt; validName &lt;*&gt; invalidAge 68 ┃ form &lt;- forAll genForm ┃ │ SignupForm { formName = &quot;a&quot; , formAge = 151 } 69 ┃ cover 5 &quot;too young&quot; (formAge form &lt;= 17) 70 ┃ cover 5 &quot;too old&quot; (formAge form &gt;= 151) 71 ┃ case validateSignup form of 72 ┃ Failure (InvalidAge{} :| []) -&gt; pure () 73 ┃ other -&gt; do 74 ┃ annotateShow other ┃ │ Success Signup { name = &quot;a&quot; , age = 151 } 75 ┃ failure ┃ ^^^^^^^</code></pre> <p>OK, we have an actual bug. When the age is 151 or greater, the form is deemed valid. It should cause a validation failure. Looking closer at the implementation, we see that a pattern guard is missing the upper bound check:</p> <div class="sourceCode" id="cb22"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a> validateAge age&#39; <span class="op">|</span> age&#39; <span class="op">&gt;=</span> <span class="dv">18</span> <span class="ot">=</span> <span class="dt">Success</span> (<span class="fu">fromIntegral</span> age&#39;)</span> <span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> <span class="dt">Failure</span> (<span class="fu">pure</span> (<span class="dt">InvalidAge</span> age&#39;))</span></code></pre></div> <p>If we change it to <code class="sourceCode haskell">age&#39; <span class="op">&gt;=</span> <span class="dv">18</span> <span class="op">&amp;&amp;</span> age&#39; <span class="op">&lt;=</span> <span class="dv">150</span></code>, and rerun the tests, they pass.</p> <pre class="hedgehog"><code>λ&gt; check prop_invalid_age_fails ✓ &lt;interactive&gt; passed 100 tests. too young 53% ██████████▌········· ✓ 5% too old 47% █████████▍·········· ✓ 5%</code></pre> <p>We’ve fixed the bug.</p> <p>Measuring and declaring requirements on coverage is a powerful tool in Hedgehog. It gives us visibility into the generative tests we run, making it practical to debug generators. It ensures our tests meet our coverage requirements, even as implementation and tests evolve over time.</p> <h2 id="from-ages-to-birth-dates">From Ages to Birth Dates</h2> <p>So far, our efforts have been successful. We’ve fixed real issues in both implementation and tests. Management is pleased. They’re now asking us to modify the signup system, and use our testing skills to ensure quality remains high.</p> <p>Instead of entering their age, users will enter their birth date. Let’s suppose this information is needed for something important, like sending out birthday gifts. The form validation function must be modified to check, based on the supplied birth date date, if the user signing up is old enough.</p> <p>First, we import the <code>Calendar</code> module from the <code>time</code> package:</p> <div class="sourceCode" id="cb24"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Time.Calendar</span></span></code></pre></div> <p>Next, we modify the <code>SignupForm</code> data type to carry a <code>formBirthDate</code> of type <code>Date</code>, rather than an <code>Int</code>.</p> <div class="sourceCode" id="cb25"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">SignupForm</span> <span class="ot">=</span> <span class="dt">SignupForm</span></span> <span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a> {<span class="ot"> formName ::</span> <span class="dt">Text</span></span> <span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> formBirthDate ::</span> <span class="dt">Day</span></span> <span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a> } <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>)</span></code></pre></div> <p>And we make the corresponding change to the <code>Signup</code> data type:</p> <div class="sourceCode" id="cb26"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Signup</span> <span class="ot">=</span> <span class="dt">Signup</span></span> <span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a> {<span class="ot"> name ::</span> <span class="dt">Text</span></span> <span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> birthDate ::</span> <span class="dt">Day</span></span> <span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a> } <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>)</span></code></pre></div> <p>We’ve also been requested to improve the validation errors. Instead of just <code>InvalidAge</code>, we define three constructors for various invalid birthdates:</p> <div class="sourceCode" id="cb27"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">SignupError</span></span> <span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">NameTooShort</span> <span class="dt">Text</span></span> <span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">NameTooLong</span> <span class="dt">Text</span></span> <span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">TooYoung</span> <span class="dt">Day</span></span> <span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">TooOld</span> <span class="dt">Day</span></span> <span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">NotYetBorn</span> <span class="dt">Day</span></span> <span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>)</span></code></pre></div> <p>Finally, we need to modify the <code>validateSignup</code> function. Here, we’re faced with an important question. How should the validation function obtain <em>today’s date</em>?</p> <h3 id="keeping-things-deterministic">Keeping Things Deterministic</h3> <p>We could make <code>validateSignup</code> a non-deterministic action, which in Haskell would have the following type signature:</p> <div class="sourceCode" id="cb28"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a>validateSignup</span> <span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">SignupForm</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> (<span class="dt">Validation</span> (<span class="dt">NonEmpty</span> <span class="dt">SignupError</span>) <span class="dt">Signup</span>)</span></code></pre></div> <p>Note the use of <code>IO</code>. It means we could retrieve the current time from the system clock, and extract the <code>Day</code> value representing today’s date. But this approach has severe drawbacks.</p> <p>If <code>validateSignup</code> uses <code>IO</code> to retrieve the current date, we can’t test it with other dates. What it there’s a bug that causes validation to behave incorrectly only on a particular date? We’d have to run the tests on that specific date to trigger it. If we introduce a bug, we want to know about it <em>immediately</em>. Not weeks, months, or even years after the bug was introduced. Furthermore, if we find such a bug with our tests, we can’t easily reproduce it on another date. We’d have to rewrite the implementation code to trigger the bug again.</p> <p>Instead of using <code>IO</code>, we’ll use a simply technique for keeping our function pure: take all the information the function needs as arguments. In the case of <code>validateSignup</code>, we’ll pass today’s date as the first argument:</p> <div class="sourceCode" id="cb29"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>validateSignup</span> <span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">SignupForm</span> <span class="ot">-&gt;</span> <span class="dt">Validation</span> (<span class="dt">NonEmpty</span> <span class="dt">SignupError</span>) <span class="dt">Signup</span></span></code></pre></div> <p>Again, let’s not worry about the implementation just yet. We’ll focus on the tests.</p> <h3 id="generating-dates">Generating Dates</h3> <p>In order to test the new <code>validateSignup</code> implementation, we need to generate <code>Day</code> values. We’re going to use a few functions from a separate module called <code>Data.Time.Gen</code>, previously written by some brilliant developer in our team. Let’s look at their type signatures. The implementations are not very interesting.</p> <p>The generator, <code>day</code>, generates a day within the given range:</p> <div class="sourceCode" id="cb30"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="ot">day ::</span> <span class="dt">Range</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">Gen</span> <span class="dt">Day</span></span></code></pre></div> <p>A day range is constructed with <code>linearDay</code>:</p> <div class="sourceCode" id="cb31"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="ot">linearDay ::</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">Range</span> <span class="dt">Day</span></span></code></pre></div> <p>Alternatively, we might use <code>exponentialDay</code>:</p> <div class="sourceCode" id="cb32"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="ot">exponentialDay ::</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">Range</span> <span class="dt">Day</span></span></code></pre></div> <p>The <code>linearDay</code> and <code>exponentialDay</code> range functions are analogous to Hedgehog’s <code>linear</code> and <code>exponential</code> ranges for integral numbers.</p> <p>To use the generator functions from <code>Data.Time.Gen</code>, we first add an import, qualified as <code>Time</code>:</p> <div class="sourceCode" id="cb33"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Time.Gen</span> <span class="kw">as</span> <span class="dt">Time</span></span></code></pre></div> <p>Next, we define a generator <code>anyDay</code>:</p> <div class="sourceCode" id="cb34"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="ot">anyDay ::</span> <span class="dt">Gen</span> <span class="dt">Day</span></span> <span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a>anyDay <span class="ot">=</span></span> <span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> low <span class="ot">=</span> fromGregorian <span class="dv">1900</span> <span class="dv">1</span> <span class="dv">1</span></span> <span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a> high <span class="ot">=</span> fromGregorian <span class="dv">2100</span> <span class="dv">12</span> <span class="dv">31</span></span> <span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span> Time.day (Time.linearDay low high)</span></code></pre></div> <p>The date range <span class="math inline">[1900-01-01, 2100-12-31]</span> is arbitrary. We could pick any centuries we like, provided the <code>time</code> package supports the range. But why not make it somewhat realistic?</p> <h3 id="rewriting-existing-properties">Rewriting Existing Properties</h3> <p>Now, it’s time to rewrite our existing property tests. Let’s begin with the one testing that validating a form with all valid data succeeds:</p> <div class="sourceCode" id="cb35"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a>prop_valid_signup_form_succeeds <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a> today <span class="ot">&lt;-</span> forAll anyDay ➊</span> <span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> genForm <span class="ot">=</span> <span class="dt">SignupForm</span> <span class="op">&lt;$&gt;</span> validName <span class="op">&lt;*&gt;</span> validBirthDate today</span> <span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a> form <span class="ot">&lt;-</span> forAll genForm ➋</span> <span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> validateSignup today form <span class="kw">of</span></span> <span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">Success</span>{} <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> failure&#39; <span class="ot">-&gt;</span> <span class="kw">do</span></span> <span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a> annotateShow failure&#39;</span> <span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a> failure</span></code></pre></div> <p>A few new things are going on here. We’re generating a date representing today (➊), and generating a form with a birth date based on today’s date (➋). Generating today’s date, we’re effectively time travelling and running the form validation on that date. This means our <code>validBirthDate</code> generator must know which date is today, in order to pick a valid birth date. We pass today’s date as a parameter, and generate a date within the range of 18 to 150 years earlier:</p> <div class="sourceCode" id="cb36"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="ot">validBirthDate ::</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">Gen</span> <span class="dt">Day</span></span> <span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a>validBirthDate today <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a> n <span class="ot">&lt;-</span> Gen.integral (Range.linear <span class="dv">18</span> <span class="dv">150</span>)</span> <span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (n <span class="ot">`yearsBefore`</span> today)</span></code></pre></div> <p>We define the helper function <code>yearsBefore</code> in the test suite. It offsets a date backwards in time by a given number of years:</p> <div class="sourceCode" id="cb37"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="ot">yearsBefore ::</span> <span class="dt">Integer</span> <span class="ot">-&gt;</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">Day</span></span> <span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a>yearsBefore years <span class="ot">=</span> addGregorianYearsClip (<span class="fu">negate</span> years)</span></code></pre></div> <p>The <code>Data.Time.Calendar</code> module exports the <code>addGregorianYearsClip</code> function. It adds a number of years, clipping February 29th (leap days) to February 28th where necessary.</p> <p>Let’s run tests:</p> <pre class="hedgehog"><code>λ&gt; check prop_valid_signup_form_succeeds ✓ &lt;interactive&gt; passed 100 tests.</code></pre> <p>Let’s move on to the next property, checking that invalid birth dates do <em>not</em> pass validation. Here, we use the same pattern as before, generating today’s date, but use <code>invalidBirthDate</code> instead:</p> <div class="sourceCode" id="cb39"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a>prop_invalid_age_fails <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a> today <span class="ot">&lt;-</span> forAll anyDay</span> <span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a> form <span class="ot">&lt;-</span> forAll (<span class="dt">SignupForm</span> <span class="op">&lt;$&gt;</span> validName <span class="op">&lt;*&gt;</span> invalidBirthDate today)</span> <span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb39-5"><a href="#cb39-5" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">5</span> <span class="st">&quot;not yet born&quot;</span> (formBirthDate form <span class="op">&gt;</span> today)</span> <span id="cb39-6"><a href="#cb39-6" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">5</span> <span class="st">&quot;too young&quot;</span> (formBirthDate form <span class="op">&gt;</span> <span class="dv">18</span> <span class="ot">`yearsBefore`</span> today)</span> <span id="cb39-7"><a href="#cb39-7" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">5</span> <span class="st">&quot;too old&quot;</span> (formBirthDate form <span class="op">&lt;</span> <span class="dv">150</span> <span class="ot">`yearsBefore`</span> today)</span> <span id="cb39-8"><a href="#cb39-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb39-9"><a href="#cb39-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> validateSignup today form <span class="kw">of</span></span> <span id="cb39-10"><a href="#cb39-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">TooYoung</span>{} <span class="op">:|</span> []) <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb39-11"><a href="#cb39-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">NotYetBorn</span>{} <span class="op">:|</span> []) <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb39-12"><a href="#cb39-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">TooOld</span>{} <span class="op">:|</span> []) <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb39-13"><a href="#cb39-13" aria-hidden="true" tabindex="-1"></a> other <span class="ot">-&gt;</span> <span class="kw">do</span></span> <span id="cb39-14"><a href="#cb39-14" aria-hidden="true" tabindex="-1"></a> annotateShow other</span> <span id="cb39-15"><a href="#cb39-15" aria-hidden="true" tabindex="-1"></a> failure</span></code></pre></div> <p>Notice that we’ve also adjusted the coverage checks. There’s a new label, “not born yet,” for birth dates in the future. Running tests, we see the label in action:</p> <pre class="hedgehog"><code>λ&gt; check prop_invalid_age_fails ✓ &lt;interactive&gt; passed 100 tests. not yet born 18% ███▌················ ✓ 5% too young 54% ██████████▊········· ✓ 5% too old 46% █████████▏·········· ✓ 5%</code></pre> <p>Good coverage, all tests passing. We’re not quite done, though. There’s a particular set of dates that we should be sure to cover: “today” dates and birth dates that are close to, or exactly, 18 years apart.</p> <p>Within our current property test for invalid ages, we’re only sure that generated birth dates include at least 5% too old, and at least 5% too young. We don’t know how far away from the “18 years” validation boundary they are.</p> <p>We could tweak our existing generators to produce values close to that boundary. Given a date <span class="math inline"><em>T</em></span>, exactly 18 years before today’s date, then:</p> <ul> <li><code>invalidBirthDate</code> would need to produce birth dates just after but not equal to <span class="math inline"><em>T</em></span></li> <li><code>validBirthDate</code> would need to produce birth dates just before or equal to <span class="math inline"><em>T</em></span></li> </ul> <p>There’s another option, though. Instead of defining separate properties for valid and invalid ages, we’ll use a <em>single</em> property for all cases. This way, we only need a single generator.</p> <h2 id="a-single-validation-property">A Single Validation Property</h2> <p>In <a href="https://www.youtube.com/watch?v=NcJOiQlzlXQ">Building on developers’ intuitions to create effective property-based tests</a>, John Hughes talks about “one property to rule them all.” Similarly, we’ll define a single property <code>prop_validates_age</code> for birth date validation. We’ll base our new property on <code>prop_invalid_age_fails</code>, but generalize to cover both positive and negative tests:</p> <div class="sourceCode" id="cb41"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>prop_validates_age <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a> today <span class="ot">&lt;-</span> forAll anyDay</span> <span id="cb41-3"><a href="#cb41-3" aria-hidden="true" tabindex="-1"></a> form <span class="ot">&lt;-</span> forAll (<span class="dt">SignupForm</span> <span class="op">&lt;$&gt;</span> validName <span class="op">&lt;*&gt;</span> anyBirthDate today) ➊</span> <span id="cb41-4"><a href="#cb41-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb41-5"><a href="#cb41-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> tooYoung <span class="ot">=</span> formBirthDate form <span class="op">&gt;</span> <span class="dv">18</span> <span class="ot">`yearsBefore`</span> today ➋</span> <span id="cb41-6"><a href="#cb41-6" aria-hidden="true" tabindex="-1"></a> notYetBorn <span class="ot">=</span> formBirthDate form <span class="op">&gt;</span> today</span> <span id="cb41-7"><a href="#cb41-7" aria-hidden="true" tabindex="-1"></a> tooOld <span class="ot">=</span> formBirthDate form <span class="op">&lt;</span> <span class="dv">150</span> <span class="ot">`yearsBefore`</span> today</span> <span id="cb41-8"><a href="#cb41-8" aria-hidden="true" tabindex="-1"></a> oldEnough <span class="ot">=</span> formBirthDate form <span class="op">&lt;=</span> <span class="dv">18</span> <span class="ot">`yearsBefore`</span> today</span> <span id="cb41-9"><a href="#cb41-9" aria-hidden="true" tabindex="-1"></a> exactly age <span class="ot">=</span> formBirthDate form <span class="op">==</span> age <span class="ot">`yearsBefore`</span> today</span> <span id="cb41-10"><a href="#cb41-10" aria-hidden="true" tabindex="-1"></a> closeTo age <span class="ot">=</span></span> <span id="cb41-11"><a href="#cb41-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> diff&#39; <span class="ot">=</span></span> <span id="cb41-12"><a href="#cb41-12" aria-hidden="true" tabindex="-1"></a> diffDays (formBirthDate form) (age <span class="ot">`yearsBefore`</span> today)</span> <span id="cb41-13"><a href="#cb41-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span> <span class="fu">abs</span> diff&#39; <span class="ot">`elem`</span> [<span class="dv">0</span> <span class="op">..</span> <span class="dv">2</span>]</span> <span id="cb41-14"><a href="#cb41-14" aria-hidden="true" tabindex="-1"></a></span> <span id="cb41-15"><a href="#cb41-15" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">10</span> <span class="st">&quot;too young&quot;</span> tooYoung</span> <span id="cb41-16"><a href="#cb41-16" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">1</span> <span class="st">&quot;not yet born&quot;</span> notYetBorn</span> <span id="cb41-17"><a href="#cb41-17" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">1</span> <span class="st">&quot;too old&quot;</span> tooOld</span> <span id="cb41-18"><a href="#cb41-18" aria-hidden="true" tabindex="-1"></a></span> <span id="cb41-19"><a href="#cb41-19" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">20</span> <span class="st">&quot;old enough&quot;</span> oldEnough ➌</span> <span id="cb41-20"><a href="#cb41-20" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">1</span> <span class="st">&quot;exactly 18&quot;</span> (exactly <span class="dv">18</span>)</span> <span id="cb41-21"><a href="#cb41-21" aria-hidden="true" tabindex="-1"></a> cover <span class="dv">5</span> <span class="st">&quot;close to 18&quot;</span> (closeTo <span class="dv">18</span>)</span> <span id="cb41-22"><a href="#cb41-22" aria-hidden="true" tabindex="-1"></a></span> <span id="cb41-23"><a href="#cb41-23" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> validateSignup today form <span class="kw">of</span> ➍</span> <span id="cb41-24"><a href="#cb41-24" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">NotYetBorn</span>{} <span class="op">:|</span> []) <span class="op">|</span> notYetBorn <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb41-25"><a href="#cb41-25" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">TooYoung</span>{} <span class="op">:|</span> []) <span class="op">|</span> tooYoung <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb41-26"><a href="#cb41-26" aria-hidden="true" tabindex="-1"></a> <span class="dt">Failure</span> (<span class="dt">TooOld</span>{} <span class="op">:|</span> []) <span class="op">|</span> tooOld <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb41-27"><a href="#cb41-27" aria-hidden="true" tabindex="-1"></a> <span class="dt">Success</span>{} <span class="op">|</span> oldEnough <span class="ot">-&gt;</span> <span class="fu">pure</span> ()</span> <span id="cb41-28"><a href="#cb41-28" aria-hidden="true" tabindex="-1"></a> other <span class="ot">-&gt;</span> annotateShow other <span class="op">&gt;&gt;</span> failure</span></code></pre></div> <p>There are a few new things going on here:</p> <ol type="1"> <li>Instead of generating exclusively invalid or valid birth dates, we’re now generating <em>any</em> birth date based on today’s date</li> <li>The boolean expressions are used both in coverage checks and in asserting, so we separate them in a <code>let</code> binding</li> <li>We add three new labels for the valid cases</li> <li>Finally, we assert on both valid and invalid cases, based on the same expressions used in coverage checks</li> </ol> <p>Note that our assertions are more specific than in <code>prop_invalid_age_fails</code>. The failure cases only pass if the corresponding label expressions are true. The <code>oldEnough</code> case covers all valid birth dates. Any result other than the four expected cases is considered incorrect.</p> <p>The <code>anyBirthDate</code> generator is based on today’s date:</p> <div class="sourceCode" id="cb42"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a><span class="ot">anyBirthDate ::</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">Gen</span> <span class="dt">Day</span></span> <span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a>anyBirthDate today <span class="ot">=</span></span> <span id="cb42-3"><a href="#cb42-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> ➊</span> <span id="cb42-4"><a href="#cb42-4" aria-hidden="true" tabindex="-1"></a> inPast <span class="fu">range</span> <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb42-5"><a href="#cb42-5" aria-hidden="true" tabindex="-1"></a> years <span class="ot">&lt;-</span> Gen.integral <span class="fu">range</span></span> <span id="cb42-6"><a href="#cb42-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (years <span class="ot">`yearsBefore`</span> today)</span> <span id="cb42-7"><a href="#cb42-7" aria-hidden="true" tabindex="-1"></a> inFuture <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb42-8"><a href="#cb42-8" aria-hidden="true" tabindex="-1"></a> years <span class="ot">&lt;-</span> Gen.integral (Range.linear <span class="dv">1</span> <span class="dv">5</span>)</span> <span id="cb42-9"><a href="#cb42-9" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (addGregorianYearsRollOver years today)</span> <span id="cb42-10"><a href="#cb42-10" aria-hidden="true" tabindex="-1"></a> daysAroundEighteenthYearsAgo <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb42-11"><a href="#cb42-11" aria-hidden="true" tabindex="-1"></a> days <span class="ot">&lt;-</span> Gen.integral (Range.linearFrom <span class="dv">0</span> (<span class="op">-</span><span class="dv">2</span>) <span class="dv">2</span>)</span> <span id="cb42-12"><a href="#cb42-12" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (addDays days (<span class="dv">18</span> <span class="ot">`yearsBefore`</span> today))</span> <span id="cb42-13"><a href="#cb42-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span> ➋</span> <span id="cb42-14"><a href="#cb42-14" aria-hidden="true" tabindex="-1"></a> Gen.frequency</span> <span id="cb42-15"><a href="#cb42-15" aria-hidden="true" tabindex="-1"></a> [ (<span class="dv">5</span>, inPast (Range.exponential <span class="dv">1</span> <span class="dv">150</span>))</span> <span id="cb42-16"><a href="#cb42-16" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">1</span>, inPast (Range.exponential <span class="dv">151</span> <span class="dv">200</span>))</span> <span id="cb42-17"><a href="#cb42-17" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">2</span>, inFuture)</span> <span id="cb42-18"><a href="#cb42-18" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">2</span>, daysAroundEighteenthYearsAgo)</span> <span id="cb42-19"><a href="#cb42-19" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div> <p>We defines helper functions (➊) for generating dates in the past, in the future, and close to 18 years ago. Using those helper functions, we combine four generators, with different date ranges, using a <code>Gen.frequency</code> distribution (➋). The weights we use are selected to give us a good coverage.</p> <p>Let’s run some tests:</p> <pre class="hedgehog"><code>λ&gt; check prop_validates_age ✓ &lt;interactive&gt; passed 100 tests. too young 62% ████████████▍······· ✓ 10% not yet born 20% ████················ ✓ 1% too old 4% ▊··················· ✓ 1% old enough 38% ███████▌············ ✓ 20% exactly 18 16% ███▏················ ✓ 1% close to 18 21% ████▏··············· ✓ 5%</code></pre> <p>Looks good! We’ve gone from testing positive and negative cases separately, to instead have a single property covering all cases, based on a single generator. It’s now easier to generate values close to the valid/invalid boundary of our SUT, i.e. around 18 years from today’s date.</p> <h2 id="february-29th">February 29th</h2> <p>For the fun of it, let’s run some more tests. We’ll crank it up to 20000.</p> <pre class="hedgehog numbers"><code>λ&gt; check (withTests 20000 prop_validates_age) ✗ &lt;interactive&gt; failed at test/Validation/V3Test.hs:141:64 after 17000 tests and 25 shrinks. too young 60% ████████████········ ✓ 10% not yet born 20% ███▉················ ✓ 1% too old 9% █▉·················· ✓ 1% old enough 40% ███████▉············ ✓ 20% exactly 18 14% ██▊················· ✓ 1% close to 18 21% ████▎··············· ✓ 5% ┏━━ test/Validation/V3Test.hs ━━━ 114 ┃ prop_validates_age = property $ do 115 ┃ today &lt;- forAll anyDay ┃ │ 1956 - 02 - 29 116 ┃ form &lt;- forAll (SignupForm &lt;$&gt; validName &lt;*&gt; anyBirthDate today) ➊ ┃ │ SignupForm { formName = &quot;aa&quot; , formBirthDate = 1938 - 03 - 01 } 117 ┃ 118 ┃ let tooYoung = formBirthDate form &gt; 18 `yearsBefore` today ➋ 119 ┃ notYetBorn = formBirthDate form &gt; today 120 ┃ tooOld = formBirthDate form &lt; 150 `yearsBefore` today 121 ┃ oldEnough = formBirthDate form &lt;= 18 `yearsBefore` today 122 ┃ exactlyEighteen = formBirthDate form == 18 `yearsBefore` today 123 ┃ closeToEighteen = 124 ┃ let diff&#39; = 125 ┃ diffDays (formBirthDate form) (18 `yearsBefore` today) 126 ┃ in abs diff&#39; `elem` [0 .. 2] 127 ┃ 128 ┃ cover 10 &quot;too young&quot; tooYoung 129 ┃ cover 1 &quot;not yet born&quot; notYetBorn 130 ┃ cover 1 &quot;too old&quot; tooOld 131 ┃ 132 ┃ cover 20 &quot;old enough&quot; oldEnough ➌ 133 ┃ cover 1 &quot;exactly 18&quot; exactlyEighteen 134 ┃ cover 5 &quot;close to 18&quot; closeToEighteen 135 ┃ 136 ┃ case validateSignup today form of ➍ 137 ┃ Failure (NotYetBorn{} :| []) | notYetBorn -&gt; pure () 138 ┃ Failure (TooYoung{} :| []) | tooYoung -&gt; pure () 139 ┃ Failure (TooOld{} :| []) | tooOld -&gt; pure () 140 ┃ Success{} | oldEnough -&gt; pure () 141 ┃ other -&gt; annotateShow other &gt;&gt; failure ┃ │ Success Signup { name = &quot;aa&quot; , birthDate = 1938 - 03 - 01 } ┃ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</code></pre> <p>Failure! Chaos! What’s going on here? Let’s examine the test case:</p> <ul> <li>Today’s date is 1956-02-29</li> <li>The birth date is 1938-03-01</li> <li>The validation function considers this <em>valid</em> (it returns a <code>Success</code> value)</li> <li>The test does considers this <em>invalid</em> (<code>oldEnough</code> is <code>False</code>)</li> </ul> <p>This means that when the validation runs on a leap day, <a href="https://en.wikipedia.org/wiki/February_29#Born_on_February_29">February 29th</a>, and the person would turn 18 years old the day after (on March 1st), the validation function incorrectly considers the person old enough. We’ve found a bug.</p> <h3 id="test-count-and-coverage">Test Count and Coverage</h3> <p>Two things led us to find this bug:</p> <ol type="1"> <li>Most importantly, that we generate today’s date and pass it as a parameter. Had we used the actual date, retrieved with an IO action, we’d only be able to find this bug every 1461 days. Pure functions are easier to test.</li> <li>That we ran more tests than the default of 100. We might not have found this bug until much later, when the generated dates happened to trigger this particular bug. In fact, running 20000 tests does not always trigger the bug.</li> </ol> <p>Our systems are often too complex to be tested exhaustively. Let’s use our form validation as an example. Between 1900-01-01 and 2100-12-31 there are 73,413 days. Selecting today’s date and the birth date from that range, we have more than five billion combinations. Running that many Hedgehog tests in GHCi on my laptop (based on some quick benchmarks) would take about a month. And this is a simple pure validation function!</p> <p>To increase coverage, even if it’s not going to be exhaustive, we can increase the number of tests we run. But how many should we run? On a continuous integration server we might be able to run more than we do locally, but we still want to keep a tight feedback loop. And what if our generators never produce inputs that reveal existing bugs, regardless of the number of tests we run?</p> <p>If we can’t test exhaustively, we need to ensure our generators cover interesting combinations of inputs. We need to carefully design and measure our tests and generators, based on the edge cases we already know of, as well as the ones that we discover over time. PBT without measuring coverage easily turns into a false sense of security.</p> <p>In the case of our leap day bug, we can catch it with fewer tests, and on every test run. We need to make sure we cover leap days, used both as today’s date and as the birth date, even with a low number of tests.</p> <h3 id="covering-leap-days">Covering Leap Days</h3> <p>To generate inputs that cover certain edge cases, we combine specific generators using <code>Gen.frequency</code>:</p> <div class="sourceCode" id="cb45"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a>(today, birthDate&#39;) <span class="ot">&lt;-</span> forAll</span> <span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a> (Gen.frequency</span> <span id="cb45-3"><a href="#cb45-3" aria-hidden="true" tabindex="-1"></a> [ (<span class="dv">5</span>, anyDayAndBirthDate) ➊</span> <span id="cb45-4"><a href="#cb45-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb45-5"><a href="#cb45-5" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">2</span>, anyDayAndBirthDateAroundYearsAgo <span class="dv">18</span>) ➋</span> <span id="cb45-6"><a href="#cb45-6" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">2</span>, anyDayAndBirthDateAroundYearsAgo <span class="dv">150</span>)</span> <span id="cb45-7"><a href="#cb45-7" aria-hidden="true" tabindex="-1"></a></span> <span id="cb45-8"><a href="#cb45-8" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">1</span>, leapDayAndBirthDateAroundYearsAgo <span class="dv">18</span>) ➌</span> <span id="cb45-9"><a href="#cb45-9" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">1</span>, leapDayAndBirthDateAroundYearsAgo <span class="dv">150</span>)</span> <span id="cb45-10"><a href="#cb45-10" aria-hidden="true" tabindex="-1"></a></span> <span id="cb45-11"><a href="#cb45-11" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">1</span>, commonDayAndLeaplingBirthDateAroundYearsAgo <span class="dv">18</span>) ➍</span> <span id="cb45-12"><a href="#cb45-12" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">1</span>, commonDayAndLeaplingBirthDateAroundYearsAgo <span class="dv">150</span>)</span> <span id="cb45-13"><a href="#cb45-13" aria-hidden="true" tabindex="-1"></a> ]</span> <span id="cb45-14"><a href="#cb45-14" aria-hidden="true" tabindex="-1"></a> )</span></code></pre></div> <p>Arbitrary values for today’s date and the birth date are drawn most frequently (➊), with a weight of 5. Next, with weights of 2, are generators for cases close to the boundaries of the validation function (➋). Finally, with weights of 1, are generators for special cases involving leap days as today’s date (➌) and leap days as birth date (➍).</p> <p>Note that these generators return pairs of dates. For most of these generators, there’s a strong relation between today’s date and the birth date. For example, we can’t first generate <em>any</em> today’s date, pass that into a generator function, and expect it to always generate a leap day that occurred 18 years ago. Such a generator would have to first generate the leap day and then today’s date.</p> <p>Let’s define the generators. The first one, <code>anyDayAndBirthDate</code>, picks any today’s date within a wide date range. It also picks a birth date from an even wider date range, resulting in some future birth dates and some ages above 150.</p> <div class="sourceCode" id="cb46"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a><span class="ot">anyDayAndBirthDate ::</span> <span class="dt">Gen</span> (<span class="dt">Day</span>, <span class="dt">Day</span>)</span> <span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>anyDayAndBirthDate <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a> today <span class="ot">&lt;-</span> Time.day</span> <span id="cb46-4"><a href="#cb46-4" aria-hidden="true" tabindex="-1"></a> (Time.linearDay (fromGregorian <span class="dv">1900</span> <span class="dv">1</span> <span class="dv">1</span>)</span> <span id="cb46-5"><a href="#cb46-5" aria-hidden="true" tabindex="-1"></a> (fromGregorian <span class="dv">2020</span> <span class="dv">12</span> <span class="dv">31</span>)</span> <span id="cb46-6"><a href="#cb46-6" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb46-7"><a href="#cb46-7" aria-hidden="true" tabindex="-1"></a> birthDate&#39; <span class="ot">&lt;-</span> Time.day</span> <span id="cb46-8"><a href="#cb46-8" aria-hidden="true" tabindex="-1"></a> (Time.linearDay (fromGregorian <span class="dv">1850</span> <span class="dv">1</span> <span class="dv">1</span>)</span> <span id="cb46-9"><a href="#cb46-9" aria-hidden="true" tabindex="-1"></a> (fromGregorian <span class="dv">2050</span> <span class="dv">12</span> <span class="dv">31</span>)</span> <span id="cb46-10"><a href="#cb46-10" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb46-11"><a href="#cb46-11" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (today, birthDate&#39;)</span></code></pre></div> <p>Writing automated tests with a hard-coded year 2020 might scare you. Won’t these tests fail when run in the future? No, not these tests. Remember, the validation function is deterministic. We control today’s date. The <em>actual</em> date on which we run these tests doesn’t matter.</p> <p>Similar to the previous generator is <code>anyDayAndBirthDateAroundYearsAgo</code>. First, it generates any date as today’s date (➊). Next, it generates an arbitrary date approximately some number of years ago (➋), where the number of years is an argument of the generator.</p> <div class="sourceCode" id="cb47"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a><span class="ot">anyDayAndBirthDateAroundYearsAgo ::</span> <span class="dt">Integer</span> <span class="ot">-&gt;</span> <span class="dt">Gen</span> (<span class="dt">Day</span>, <span class="dt">Day</span>)</span> <span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a>anyDayAndBirthDateAroundYearsAgo years <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb47-3"><a href="#cb47-3" aria-hidden="true" tabindex="-1"></a> today <span class="ot">&lt;-</span> Time.day ➊</span> <span id="cb47-4"><a href="#cb47-4" aria-hidden="true" tabindex="-1"></a> (Time.linearDay (fromGregorian <span class="dv">1900</span> <span class="dv">1</span> <span class="dv">1</span>)</span> <span id="cb47-5"><a href="#cb47-5" aria-hidden="true" tabindex="-1"></a> (fromGregorian <span class="dv">2020</span> <span class="dv">12</span> <span class="dv">31</span>)</span> <span id="cb47-6"><a href="#cb47-6" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb47-7"><a href="#cb47-7" aria-hidden="true" tabindex="-1"></a> birthDate&#39; <span class="ot">&lt;-</span> addingApproxYears (<span class="fu">negate</span> years) today ➋</span> <span id="cb47-8"><a href="#cb47-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (today, birthDate&#39;)</span></code></pre></div> <p>The <code>addingApproxYearsAgo</code> generator adds a number of years to a date, and offsets it between two days back and two days forward in time.</p> <div class="sourceCode" id="cb48"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a><span class="ot">addingApproxYears ::</span> <span class="dt">Integer</span> <span class="ot">-&gt;</span> <span class="dt">Day</span> <span class="ot">-&gt;</span> <span class="dt">Gen</span> <span class="dt">Day</span></span> <span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a>addingApproxYears years today <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a> days <span class="ot">&lt;-</span> Gen.integral (Range.linearFrom <span class="dv">0</span> (<span class="op">-</span><span class="dv">2</span>) <span class="dv">2</span>)</span> <span id="cb48-4"><a href="#cb48-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (addDays days (addGregorianYearsRollOver years today))</span></code></pre></div> <p>The last two generators used in our <code>frequency</code> distribution cover leap day edge cases. First, let’s define the <code>leapDayAndBirthDateAroundYearsAgo</code> generator. It generates a leap day used as today’s date, and a birth date close to the given number of years ago.</p> <div class="sourceCode" id="cb49"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a><span class="ot">leapDayAndBirthDateAroundYearsAgo ::</span> <span class="dt">Integer</span> <span class="ot">-&gt;</span> <span class="dt">Gen</span> (<span class="dt">Day</span>, <span class="dt">Day</span>)</span> <span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a>leapDayAndBirthDateAroundYearsAgo years <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb49-3"><a href="#cb49-3" aria-hidden="true" tabindex="-1"></a> today <span class="ot">&lt;-</span> leapDay (Range.linear <span class="dv">1904</span> <span class="dv">2020</span>)</span> <span id="cb49-4"><a href="#cb49-4" aria-hidden="true" tabindex="-1"></a> birthDate&#39; <span class="ot">&lt;-</span> addingApproxYears (<span class="fu">negate</span> years) today</span> <span id="cb49-5"><a href="#cb49-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (today, birthDate&#39;)</span></code></pre></div> <p>The <code>leapDay</code> generator uses <code>mod</code> to only generate years divisible by 4 and constructs dates on February 29th. That alone isn’t enough to only generate valid leap days, though. Years divisible by 100 but not by 400 are not leap years. To keep the generator simple, we discard those years using the already existing <code>isLeapDay</code> predicate as a filter.</p> <div class="sourceCode" id="cb50"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a><span class="ot">leapDay ::</span> <span class="dt">Range</span> <span class="dt">Integer</span> <span class="ot">-&gt;</span> <span class="dt">Gen</span> <span class="dt">Day</span></span> <span id="cb50-2"><a href="#cb50-2" aria-hidden="true" tabindex="-1"></a>leapDay yearRange <span class="ot">=</span> Gen.filter isLeapDay <span class="op">$</span> <span class="kw">do</span></span> <span id="cb50-3"><a href="#cb50-3" aria-hidden="true" tabindex="-1"></a> year <span class="ot">&lt;-</span> Gen.integral yearRange</span> <span id="cb50-4"><a href="#cb50-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (fromGregorian (year <span class="op">-</span> year <span class="ot">`mod`</span> <span class="dv">4</span>) <span class="dv">2</span> <span class="dv">29</span>)</span></code></pre></div> <p>In general, we should be careful about discarding generated values using <code>filter</code>. If we discard too much, Hedgehog gives up and complains loudly. In this particular case, discarding a few generated dates is fine. Depending on the year range we pass it, we might not discard any date.</p> <p>Finally, we define the <code>commonDayAndLeaplingBirthDateAroundYearsAgo</code> generator. It first generates a leap day used as the birth date, and then a today’s date approximately the given number of years after the birth date.</p> <div class="sourceCode" id="cb51"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a><span class="ot">commonDayAndLeaplingBirthDateAroundYearsAgo ::</span> <span class="dt">Integer</span> <span class="ot">-&gt;</span> <span class="dt">Gen</span> (<span class="dt">Day</span>, <span class="dt">Day</span>)</span> <span id="cb51-2"><a href="#cb51-2" aria-hidden="true" tabindex="-1"></a>commonDayAndLeaplingBirthDateAroundYearsAgo years <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb51-3"><a href="#cb51-3" aria-hidden="true" tabindex="-1"></a> birthDate&#39; <span class="ot">&lt;-</span> leapDay (Range.linear <span class="dv">1904</span> <span class="dv">2020</span>)</span> <span id="cb51-4"><a href="#cb51-4" aria-hidden="true" tabindex="-1"></a> today <span class="ot">&lt;-</span> addingApproxYears years birthDate&#39;</span> <span id="cb51-5"><a href="#cb51-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (today, birthDate&#39;)</span></code></pre></div> <p>That’s it for the generators. Now, how do we know that we’re covering the edge cases well enough? With coverage checks!</p> <div class="sourceCode" id="cb52"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a></span> <span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a>cover <span class="dv">5</span> ➊</span> <span id="cb52-3"><a href="#cb52-3" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;close to 18, validated on common day&quot;</span></span> <span id="cb52-4"><a href="#cb52-4" aria-hidden="true" tabindex="-1"></a> (closeTo <span class="dv">18</span> <span class="op">&amp;&amp;</span> <span class="fu">not</span> (isLeapDay today))</span> <span id="cb52-5"><a href="#cb52-5" aria-hidden="true" tabindex="-1"></a>cover <span class="dv">1</span></span> <span id="cb52-6"><a href="#cb52-6" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;close to 18, validated on leap day&quot;</span></span> <span id="cb52-7"><a href="#cb52-7" aria-hidden="true" tabindex="-1"></a> (closeTo <span class="dv">18</span> <span class="op">&amp;&amp;</span> isLeapDay today)</span> <span id="cb52-8"><a href="#cb52-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb52-9"><a href="#cb52-9" aria-hidden="true" tabindex="-1"></a>cover <span class="dv">5</span> ➋</span> <span id="cb52-10"><a href="#cb52-10" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;close to 150, validated on common day&quot;</span></span> <span id="cb52-11"><a href="#cb52-11" aria-hidden="true" tabindex="-1"></a> (closeTo <span class="dv">150</span> <span class="op">&amp;&amp;</span> <span class="fu">not</span> (isLeapDay today))</span> <span id="cb52-12"><a href="#cb52-12" aria-hidden="true" tabindex="-1"></a>cover <span class="dv">1</span></span> <span id="cb52-13"><a href="#cb52-13" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;close to 150, validated on leap day&quot;</span></span> <span id="cb52-14"><a href="#cb52-14" aria-hidden="true" tabindex="-1"></a> (closeTo <span class="dv">150</span> <span class="op">&amp;&amp;</span> isLeapDay today)</span> <span id="cb52-15"><a href="#cb52-15" aria-hidden="true" tabindex="-1"></a></span> <span id="cb52-16"><a href="#cb52-16" aria-hidden="true" tabindex="-1"></a>cover <span class="dv">5</span> ➌</span> <span id="cb52-17"><a href="#cb52-17" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;exactly 18 today, born on common day&quot;</span></span> <span id="cb52-18"><a href="#cb52-18" aria-hidden="true" tabindex="-1"></a> (exactly <span class="dv">18</span> <span class="op">&amp;&amp;</span> <span class="fu">not</span> (isLeapDay birthDate&#39;))</span> <span id="cb52-19"><a href="#cb52-19" aria-hidden="true" tabindex="-1"></a>cover ➍</span> <span id="cb52-20"><a href="#cb52-20" aria-hidden="true" tabindex="-1"></a> <span class="dv">1</span></span> <span id="cb52-21"><a href="#cb52-21" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;legally 18 today, born on leap day&quot;</span></span> <span id="cb52-22"><a href="#cb52-22" aria-hidden="true" tabindex="-1"></a> ( isLeapDay birthDate&#39;</span> <span id="cb52-23"><a href="#cb52-23" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;&amp;</span> (addGregorianYearsRollOver <span class="dv">18</span> birthDate&#39; <span class="op">==</span> today)</span> <span id="cb52-24"><a href="#cb52-24" aria-hidden="true" tabindex="-1"></a> )</span></code></pre></div> <p>We add new checks to the property test, checking that we hit both leap day and regular day cases around the 18th birthday (➊) and the 150th birthday (➋). Notice that we had similar checks before, but we were not discriminating between leap days and common days.</p> <p>Finally, we check the coverage of two leap day scenarios that can occur when a person <a href="https://en.wikipedia.org/wiki/February_29#Legal_status">legally turns 18</a>: a person born on a common day turning 18 on a leap day (➌), and a leapling turning 18 on a common day (➍).</p> <p>Running the modified property test, we get the leap day counter-example every time, even with as few as a hundred tests. For example, we might see today’s date being 1904-02-29 and the birth date being 1886-03-01. The validation function deems the person old enough. Again, this is incorrect.</p> <p>Now that we can quickly and reliably reproduce the failing example we are in a great position to find the error. While we could use a fixed seed to reproduce the particular failing case from the 20000 tests run, we are now more confident that the property test would catch future leap day-related bugs, if we were to introduce new ones. Digging into the implementation, we’ll find a boolean expression in a pattern guard being the culprit:</p> <div class="sourceCode" id="cb53"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb53-1"><a href="#cb53-1" aria-hidden="true" tabindex="-1"></a>birthDate&#39; <span class="op">&lt;=</span> addGregorianYearsRollOver (<span class="op">-</span><span class="dv">18</span>) today</span></code></pre></div> <p>The use of <code>addGregorianYearsRollOver</code> together with adding a negative number of years is the problem, rolling over to March 1st instead of clipping to February 28th. Instead, we should use <code>addGregorianYearsClip</code>:</p> <div class="sourceCode" id="cb54"><pre class="sourceCode haskell numbers"><code class="sourceCode haskell"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a>birthDate&#39; <span class="op">&lt;=</span> addGregorianYearsClip (<span class="op">-</span><span class="dv">18</span>) today</span></code></pre></div> <p>Running 100 tests again, we see that they all pass, and that our coverage requirements are met.</p> <pre class="hedgehog"><code>λ&gt; Hedgehog.check prop_validates_age ✓ &lt;interactive&gt; passed 100 tests. too young 17% ███▍················ ✓ 10% not yet born 7% █▍·················· ✓ 1% too old 19% ███▊················ ✓ 1% old enough 83% ████████████████▌··· ✓ 20% close to 18, validated on common day 30% ██████·············· ✓ 5% close to 18, validated on leap day 2% ▍··················· ✓ 1% close to 150, validated on common day 31% ██████▏············· ✓ 5% close to 150, validated on leap day 6% █▏·················· ✓ 1% exactly 18 today, born on common day 17% ███▍················ ✓ 5% legally 18 today, born on leap day 5% █··················· ✓ 1%</code></pre> <h2 id="summary">Summary</h2> <p>In this tutorial, we started with a simple form validation function, checking the name and age of a person signing up for an online service. We defined property tests for positive and negative tests, learned how to test generators with coverage checks, and found bugs in both the test suite and the implementation.</p> <p>When requirements changed, we had to start working with dates. In order to keep the validation function deterministic, we had to pass in today’s date. This enabled us to simulate the validation running on any date, in combination with any reported birth date, and trigger bugs that could otherwise take years to find, if ever. Had we not made it deterministic, we would likely not have found the leap day bug later on.</p> <p>To generate inputs that sufficiently test the validation function’s boundaries, we rewrote our separate positive and negative properties into a single property, and used coverage checks to ensure the quality of our generators. The trade-off between multiple disjoint properties and a single more complicated property is hard.</p> <p>With multiple properties, for example split between positive and negative tests, both generators and assertions can be simpler and more targeted. On the other hand, you run a risk of missing certain inputs. The set of properties might not cover the entire space of inputs. Furthermore, performing coverage checks across multiple properties, using multiple targeted generators, can be problematic.</p> <p>Ensuring coverage of generators in a single property is easier. You might even get away with a naive generator, depending on the system you’re testing. If not, you’ll need to combine more targeted generators, for example with weighted probabilities. The drawback of using a single property is that the assertion not only becomes more complicated, it’s also likely to mirror the implementation of the SUT. As we saw with our single property testing the validation function, the assertion duplicated the validation rules. You might be able to reuse the coverage expressions in assertions, but still, there’s a strong coupling.</p> <p>The choice between single or multiple properties comes down to <em>how</em> you want to cover the boundaries of the SUT. Ultimately, both approaches can achieve the same coverage, in different ways. They both suffer from the classic problem of a test suite mirroring the system it’s testing.</p> <p>Finally, running a larger number of tests, we found a bug related to leap days. Again, without having made the validation function deterministic, this could’ve only been found on a leap day. We further refined our generators to cover leap day cases, and found the bug reliably with as few as 100 tests. The bug was easy to find and fix when we had the inputs pointing directly towards it.</p> <p>That’s it for this tutorial. Thanks for reading, and happy property testing and time travelling!</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2019-06-02-property-based-testing-in-a-screencast-editor-case-study-3.html</id>
    <title>Property-Based Testing in a Screencast Editor, Case Study 3: Integration Testing</title>
    <link href="https://wickstrom.tech/2019-06-02-property-based-testing-in-a-screencast-editor-case-study-3.html"/>
    <published>2019-06-02T00:00:00+02:00</published>
    <updated>2019-06-02T00:00:00+02:00</updated>
    <summary>In the last article we looked at how Komposition automatically
classifies moving and still segments in imported video media, how I went
from ineffective testing by eye to finding curious bugs using
property-based testing (PBT). If you haven’t read it, or its preceding
posts, I encourage you to check them out first:</summary>
    <content type="html"><![CDATA[
<p>In the last article we looked at how Komposition automatically classifies moving and still segments in imported video media, how I went from ineffective testing by eye to finding curious bugs using property-based testing (PBT). If you haven’t read it, or its preceding posts, I encourage you to check them out first:</p> <ol type="1"> <li><a href="/2019-03-02-property-based-testing-in-a-screencast-editor-introduction.html">Introduction</a></li> <li><a href="/2019-03-24-property-based-testing-in-a-screencast-editor-case-study-1.html">Timeline Flattening</a></li> <li><a href="/2019-04-17-property-based-testing-in-a-screencast-editor-case-study-2.html">Video Scene Classification</a></li> </ol> <p>This is the final case study in the “Property-Based Testing in a Screencast Editor” series. It covers property-based integration testing and its value during aggressive refactoring work within Komposition.</p> <h2 id="a-history-of-two-stacks">A History of Two Stacks</h2> <p>In Komposition, a project’s state is represented using an in-memory data structure. It contains the hierarchical timeline, the focus, import and render settings, project storage file paths, and more. To let users navigate backwards and forwards in their history of project edits, for example when they have made a mistake, Komposition supplies <em>undo</em> and <em>redo</em> commands.</p> <p>The undo/redo history was previously implemented as a data structure recording project states, compromised of:</p> <ul> <li>a <em>current state</em> variable</li> <li>a stack of <em>previous states</em></li> <li>a stack of <em>possible future states</em></li> </ul> <p>The undo/redo history data structure held entire project state values. Each undoable and redoable user action created a new state value. Let’s look a bit closer at how this worked.</p> <h3 id="performing-actions">Performing Actions</h3> <p>When a user performed an undoable/redoable action, the undo/redo history would:</p> <ul> <li>push the previous state onto the undo stack</li> <li>perform the action and replace the current state</li> <li>discard all states in the redo stack</li> </ul> <p>This can be visualized as in the following diagram, where the state <em>d</em> is being replaced with a new state <em>h</em>, and <em>d</em> being pushed onto the undo stack. The undo/redo history to the left of the dividing line is the original, and the one to the right is the resulting history.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram12.png" width="600" alt="Performing an action pushes the previous state onto the undo stack and discards the redo stack" /> <figcaption aria-hidden="true">Performing an action pushes the previous state onto the undo stack and discards the redo stack</figcaption> </figure> <p>Again, note that performing new actions discarded all states in the redo stack.</p> <h3 id="undoing-actions">Undoing Actions</h3> <p>When the user chose to undo an action, the undo/redo history would:</p> <ul> <li>pop the undo stack and use that state as the current state</li> <li>push the previous state onto the redo stack</li> </ul> <p>The following diagram shows how undoing the last performed action’s resulting state, <em>d</em>, pushes <em>d</em> onto the redo stack, and pops <em>c</em> from the undo stack to use that as the current state.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram13.png" width="600" alt="Undoing pushes the previous state onto the redo stack and pops the undo stack for a current state" /> <figcaption aria-hidden="true">Undoing pushes the previous state onto the redo stack and pops the undo stack for a current state</figcaption> </figure> <h3 id="redoing-actions">Redoing Actions</h3> <p>When the user chose to redo an action, the undo/redo history would:</p> <ul> <li>pop the redo stack and use that state as the current state</li> <li>push the previous state onto the undo stack</li> </ul> <p>The last diagram shows how redoing, recovering a previously undone state, pops <em>g</em> from the redo stack to use that as the current state, and pushes the previous state <em>d</em> onto the undo stack.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram14.png" width="600" alt="Redoing pushes the previous state onto the undo stack and pops the redo stack for a current state" /> <figcaption aria-hidden="true">Redoing pushes the previous state onto the undo stack and pops the redo stack for a current state</figcaption> </figure> <p>Note that not all user actions in Komposition are undoable/redoable. Actions like navigating the focus or zooming are not recorded in the history.</p> <h3 id="dealing-with-performance-problems">Dealing With Performance Problems</h3> <p>While the “two stacks of states” algorithm was easy to understand and implement, it failed to meet my non-functional requirements. A screencast project compromised of hundreds or thousands of small edits would consume gigabytes of disk space when stored, take tens of seconds to load from disk, and consume many gigabytes of RAM when in memory.</p> <p>Now, you might think that my implementation was incredibly naive, and that the performance problems could be fixed with careful profiling and optimization. And you’d probably be right! I did consider going down that route, optimizing the code, time-windowing edits to compact history on the fly, and capping the history at some fixed size. Those would all be interesting pursuits, but in the end I decided to try something else.</p> <h2 id="refactoring-with-property-based-integration-tests">Refactoring with Property-Based Integration Tests</h2> <p>Instead of optimizing the current stack-based implementation, I decided to implement the undo/redo history in terms of <a href="https://en.wikipedia.org/wiki/Inverse_function">inverse</a> actions. In this model, actions not only modify the project state, they also return another action, its inverse, that <em>reverses</em> the effects of the original action. Instead of recording a new project state data structure for each edit, the history only records descriptions of the actions themselves.</p> <p>I realized early that introducing the new undo/redo history implementation in Komposition was not going to be a small task. It would touch the majority of command implementation code, large parts of the main application logic, and the project binary serialization format. What it wouldn’t affect, though, was the module describing user commands in abstract.</p> <p>To provide a safety net for the refactoring, I decided to cover the undo/redo functionality with tests. As the user commands would stay the same throughout my modifications, I chose to test at that level, which can be characterized as integration-level testing. The tests run Komposition, including its top-level application control flow, but with the user interface and some other effects stubbed out. Making your application testable at this level is hard work, but the payoff can be huge.</p> <p>With Komposition featuring close to twenty types of user commands, combined with a complex hierarchical timeline and navigation model, the combinatory explosion of possible states was daunting. Relying on example-based tests to safeguard my work was not satisfactory. While PBT couldn’t cover the entire state space either, I was confident it would improve my chances of finding actual bugs.</p> <h2 id="undoredo-tests">Undo/Redo Tests</h2> <p>Before I began refactoring, I added tests for the inverse property of undoable/redoable actions. The first test focuses on undoing actions, and is structured as follows:</p> <ol type="1"> <li>Generate an initial project and application state</li> <li>Generate a sequence of undoable/redoable commands (wrapped in <em>events</em>)</li> <li>Run the application with the initial state and the generated events</li> <li>Run an undo command for each original command</li> <li>Assert that final timeline is equal to the initial timeline</li> </ol> <p>Let’s look at the Haskell Hedgehog property test:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>hprop_undo_actions_are_undoable <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate initial timeline and focus</span></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> timelineAndFocus <span class="ot">&lt;-</span> forAllWith showTimelineAndFocus <span class="op">$</span></span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> Gen.timelineWithFocus (Range.linear <span class="dv">0</span> <span class="dv">10</span>) Gen.parallel</span> <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="co">-- ... and initial application state</span></span> <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> initialState <span class="ot">&lt;-</span> forAll (initializeState timelineAndFocus)</span> <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Generate a sequence of undoable/redoable commands</span></span> <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> events <span class="ot">&lt;-</span> forAll <span class="op">$</span></span> <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> Gen.list (Range.exponential <span class="dv">1</span> <span class="dv">100</span>) genUndoableTimelineEvent</span> <span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Run &#39;events&#39; on the original state</span></span> <span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> beforeUndos <span class="ot">&lt;-</span> runTimelineStubbedWithExit events initialState</span> <span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Run as many undo commands as undoable commands</span></span> <span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a> afterUndos <span class="ot">&lt;-</span> runTimelineStubbedWithExit (undoEvent <span class="op">&lt;$</span> events) beforeUndos</span> <span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 5. That should result in a timeline equal to the one we started</span></span> <span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> <span class="co">-- with</span></span> <span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> timelineToTree (initialState <span class="op">^.</span> currentTimeline)</span> <span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> <span class="op">===</span> timelineToTree (afterUndos <span class="op">^.</span> currentTimeline)</span></code></pre></div> <p>The second test, focusing on redoing actions, is structured very similarly to the previous test:</p> <ol type="1"> <li>Generate an initial project and application state</li> <li>Generate a sequence of undoable commands (wrapped in <em>events</em>)</li> <li>Run the application with the initial state and the generated events</li> <li>Run an undo commands for each original command</li> <li>Run an redo commands for each original command</li> <li>Assert that final timeline is equal to the timeline before undoing actions</li> </ol> <p>The test code is also very similar:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>hprop_undo_actions_are_redoable <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate the initial timeline and focus</span></span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> timelineAndFocus <span class="ot">&lt;-</span> forAllWith showTimelineAndFocus <span class="op">$</span></span> <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> Gen.timelineWithFocus (Range.linear <span class="dv">0</span> <span class="dv">10</span>) Gen.parallel</span> <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="co">-- ... and the initial application state</span></span> <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> initialState <span class="ot">&lt;-</span> forAll (initializeState timelineAndFocus)</span> <span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Generate a sequence of undoable/redoable commands</span></span> <span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> events <span class="ot">&lt;-</span> forAll <span class="op">$</span></span> <span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> Gen.list (Range.exponential <span class="dv">1</span> <span class="dv">100</span>) genUndoableTimelineEvent</span> <span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Run &#39;events&#39; on the original state</span></span> <span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> beforeUndos <span class="ot">&lt;-</span> runTimelineStubbedWithExit events initialState</span> <span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Run undo commands corresponding to all original commands</span></span> <span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a> afterRedos <span class="ot">&lt;-</span></span> <span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> runTimelineStubbedWithExit (undoEvent <span class="op">&lt;$</span> events) beforeUndos</span> <span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 5. Run redo commands corresponding to all original commands</span></span> <span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> <span class="op">&gt;&gt;=</span> runTimelineStubbedWithExit (redoEvent <span class="op">&lt;$</span> events)</span> <span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 6. That should result in a timeline equal to the one we had</span></span> <span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a> <span class="co">-- before undoing actions</span></span> <span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a> timelineToTree (beforeUndos <span class="op">^.</span> currentTimeline)</span> <span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a> <span class="op">===</span> timelineToTree (afterRedos <span class="op">^.</span> currentTimeline)</span></code></pre></div> <p>Note that these tests only assert on the equality of timelines, not entire project states, as undoable commands only operate on the timeline.</p> <h3 id="all-tests-passing-everything-works">All Tests Passing, Everything Works</h3> <p>The undo/redo tests were written and run on the original stack-based implementation, kept around during a refactoring that took me two weeks of hacking during late nights and weekends, and finally run and passing with the new implementation based on inverse actions. Except for a few minimal adjustments to data types, these tests stayed untouched during the entire process.</p> <p>The confidence I had when refactoring felt like a super power. Two simple property tests made the undertaking possible. They found numerous bugs, including:</p> <ul> <li>Off-by-one index errors in actions modifying the timeline</li> <li>Inconsistent timeline focus: <ul> <li>focus was incorrectly restored on undoing an action</li> <li>focus was outside of the timeline bounds</li> </ul></li> <li>Non-inverse actions: <ul> <li>actions returning incorrectly constructed inverses</li> <li>the inverse of <em>splitting</em> a sequence is <em>joining</em> sequences, and joining them back up didn’t always work</li> </ul></li> </ul> <p>After all tests passed, I ran the application with its GUI, edited a screencast project, and it all worked flawlessly. It’s almost too good to be true, right?</p> <p>Property testing is not a silver bullet, and there might still be bugs lurking in my undo/redo history implementation. The tests I run are never going to be exhaustive and my generators might be flawed. That being said, they gave me a confidence in refactoring that I’ve never had before. Or maybe I just haven’t hit that disastrous edge case yet?</p> <h2 id="why-test-with-properties">Why Test With Properties?</h2> <p>This was the last case study in the “Property-Based Testing in a Screencast Editor” series. I’ve had a great time writing these articles and giving talks on the subject. Before I wrap up, I’ll summarize my thoughts on PBT in general and my experience with it in Komposition.</p> <p>Property-based testing is not only for pure functions; you can use it to test effectful actions. It is not only for unit testing; you can write integration tests using properties. It’s not only for functional programming languages; there are good frameworks for most popular programming languages.</p> <p>Properties describe the general behavior of the system under test, and verify its correctness using a variety of inputs. Not only is this an effective way of finding errors, it’s also a concise way of documenting the system.</p> <p>The iterative process in property-based testing, in my experience, comes down to the following steps:</p> <ol type="1"> <li>Think about the specification of your system under test</li> <li>Think about how generators and tests should work</li> <li>Write or modify generators, tests, and implementation code, based on steps 1 and 2</li> <li>Get minimal examples of failing tests</li> <li>Repeat</li> </ol> <p>Using PBT within Komposition has made it possible to confidently refactor large parts of the application. It has found errors in my thinking, my generators, my tests, and in my implementation code. Testing video scene classification went from a time consuming, repetitive, and manual verification process to a fast, effective, and automated task.</p> <p>In short, it’s been a joy, and I look forward to continue using PBT in my work and in my own projects. I hope I’ve convinced you of its value, and inspired you to try it out, no matter what kind of project you’re working on and what programming language you are using. Involve your colleagues, practice writing property tests together, and enjoy finding complicated bugs before your users do!</p> <h2 id="buy-the-book">Buy the Book</h2> <p>This series is now available as an <a href="https://leanpub.com/property-based-testing-in-a-screencast-editor">ebook on Leanpub</a>. While the content is mostly the same, there are few changes bringing it up-to-date. Also, if you’ve already enjoyed the articles, you might want support my work by purchasing this book. Finally, you might enjoy a nicely typeset PDF, or an EPUB book, over a web page.</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2019-04-17-property-based-testing-in-a-screencast-editor-case-study-2.html</id>
    <title>Property-Based Testing in a Screencast Editor, Case Study 2: Video Scene Classification</title>
    <link href="https://wickstrom.tech/2019-04-17-property-based-testing-in-a-screencast-editor-case-study-2.html"/>
    <published>2019-04-17T00:00:00+02:00</published>
    <updated>2019-04-17T00:00:00+02:00</updated>
    <summary>In the previous case study on property-based testing (PBT) in
Komposition we looked at timeline flattening. This post covers the video
classifier, how it was tested before, and the bugs I found when I wrote
property tests for it.</summary>
    <content type="html"><![CDATA[
<p>In <a href="/2019-03-24-property-based-testing-in-a-screencast-editor-case-study-1.html">the previous case study</a> on property-based testing (PBT) in Komposition we looked at timeline flattening. This post covers the video classifier, how it was tested before, and the bugs I found when I wrote property tests for it.</p> <p>If you haven’t read <a href="/2019-03-02-property-based-testing-in-a-screencast-editor-introduction.html">the introduction</a> or <a href="/2019-03-24-property-based-testing-in-a-screencast-editor-case-study-1.html">the first case study</a> yet, I recommend checking them out!</p> <h2 id="classifying-scenes-in-imported-video">Classifying Scenes in Imported Video</h2> <p>Komposition can automatically classify <em>scenes</em> when importing video files. This is a central productivity feature in the application, effectively cutting recorded screencast material automatically, letting the user focus on arranging the scenes of their screencast. Scenes are segments that are considered <em>moving</em>, as opposed to <em>still</em> segments:</p> <ul> <li>A still segment is a sequence of at least <span class="math inline"><em>S</em></span> seconds of <em>near-equal</em> frames</li> <li>A moving segment is a sequence of <em>non-equal</em> frames, or a sequence of near-equal frames with a duration less than <span class="math inline"><em>S</em></span></li> </ul> <p><span class="math inline"><em>S</em></span> is a preconfigured minimum still segment duration in Komposition. In the future it might be configurable from the user interface, but for now it’s hard-coded.</p> <p>Equality of two frames <span class="math inline"><em>f</em><sub>1</sub></span> and <span class="math inline"><em>f</em><sub>2</sub></span> is defined as a function <span class="math inline"><em>E</em>(<em>f</em><sub>1</sub>, <em>f</em><sub>2</sub>)</span>, described informally as:</p> <ul> <li>comparing corresponding pixel color values of <span class="math inline"><em>f</em><sub>1</sub></span> and <span class="math inline"><em>f</em><sub>2</sub></span>, with a small epsilon for tolerance of color variation, and</li> <li>deciding two frames equal when at least 99% of corresponding pixel pairs are considered equal.</li> </ul> <p>In addition to the rules stated above, there are two edge cases:</p> <ol type="1"> <li>The first segment is always a considered a moving segment (even if it’s just a single frame)</li> <li>The last segment may be a still segment with a duration less than <span class="math inline"><em>S</em></span></li> </ol> <p>The second edge case is not what I would call a desirable feature, but rather a shortcoming due to the classifier not doing any type of backtracking. This could be changed in the future.</p> <h2 id="manually-testing-the-classifier">Manually Testing the Classifier</h2> <p>The first version of the video classifier had no property tests. Instead, I wrote what I thought was a decent classifier algorithm, mostly messing around with various pixel buffer representations and parallel processing to achieve acceptable performance.</p> <p>The only type of testing I had available, except for general use of the application, was a color-tinting utility. This was a separate program using the same classifier algorithm. It took as input a video file, and produced as output a video file where each frame was tinted green or red, for moving and still frames, respectively.</p> <figure> <img src="/assets/property-based-testing-the-ugly-parts/color-tinting.gif" width="600" height="470" alt="Video classification shown with color tinting" /> <figcaption aria-hidden="true">Video classification shown with color tinting</figcaption> </figure> <p>In the recording above you see the color-tinted output video based on a recent version of the classifier. It classifies moving and still segments rather accurately. Before I wrote property tests and fixed the bugs that I found, it did not look so pretty, flipping back and forth at seemingly random places.</p> <p>At first, debugging the classifier with the color-tinting tool way seemed like a creative and powerful technique. But the feedback loop was horrible, having to record video, process it using the slow color-tinting program, and inspecting it by eye. In hindsight, I can conclude that PBT is far more effective for testing the classifier.</p> <h2 id="video-classification-properties">Video Classification Properties</h2> <p>Figuring out how to write property tests for video classification wasn’t obvious to me. It’s not uncommon in example-based testing that tests end up mirroring the structure, and even the full implementation complexity, of the system under test. The same can happen in property-based testing.</p> <p>With some complex systems it’s very hard to describe the correctness as a relation between any valid input and the system’s observed output. The video classifier is one such case. How do I decide if an output classification is correct for a specific input, without reimplementing the classification itself in my tests?</p> <p>The other way around is easy, though! If I have a classification, I can convert that into video frames. Thus, the solution to the testing problem is to not generate the input, but instead generate the <em>expected output</em>. Hillel Wayne calls this technique “oracle generators” in his recent article.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p> <p>The classifier property tests generate high-level representations of the expected classification output, which are lists of values describing the type and duration of segments.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram10.png" width="600" alt="A generated sequence of expected classified segments" /> <figcaption aria-hidden="true">A generated sequence of expected classified segments</figcaption> </figure> <p>Next, the list of output segments is converted into a sequence of actual frames. Frames are two-dimensional arrays of RGB pixel values. The conversion is simple:</p> <ul> <li>Moving segments are converted to a sequence of alternating frames, flipping between all gray and all white pixels</li> <li>Still frames are converted to a sequence of frames containing all black pixels</li> </ul> <p>The example sequence in the diagram above, when converted to pixel frames with a frame rate of 10 FPS, can be visualized like in the following diagram, where each thin rectangle represents a frame:</p> <figure> <img src="/assets/pbt-screencast-editor/diagram11.png" width="600" alt="Pixel frames derived from a sequence of expected classified output segments" /> <figcaption aria-hidden="true">Pixel frames derived from a sequence of expected classified output segments</figcaption> </figure> <p>By generating high-level output and converting it to pixel frames, I have input to feed the classifier with, and I know what output it should produce. Writing effective property tests then comes down to writing generators that produce valid output, according to the specification of the classifier. In this post I’ll show two such property tests.</p> <h2 id="testing-still-segment-minimum-length">Testing Still Segment Minimum Length</h2> <p>As stated in the beginning of this post, classified still segments must have a duration greater than or equal to <span class="math inline"><em>S</em></span>, where <span class="math inline"><em>S</em></span> is the minimum still segment duration used as a parameter for the classifier. The first property test we’ll look at asserts that this invariant holds for all classification output.</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>hprop_classifies_still_segments_of_min_length <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate a minimum still segment length/duration</span></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> minStillSegmentFrames <span class="ot">&lt;-</span> forAll <span class="op">$</span> Gen.int (Range.linear <span class="dv">2</span> (<span class="dv">2</span> <span class="op">*</span> frameRate))</span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> minStillSegmentTime <span class="ot">=</span> frameCountDuration minStillSegmentFrames</span> <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Generate output segments</span></span> <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> segments <span class="ot">&lt;-</span> forAll <span class="op">$</span></span> <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> genSegments (Range.linear <span class="dv">1</span> <span class="dv">10</span>)</span> <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> (Range.linear <span class="dv">1</span></span> <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> (minStillSegmentFrames <span class="op">*</span> <span class="dv">2</span>))</span> <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> (Range.linear minStillSegmentFrames</span> <span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> (minStillSegmentFrames <span class="op">*</span> <span class="dv">2</span>))</span> <span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> resolution</span> <span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Convert test segments to actual pixel frames</span></span> <span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> pixelFrames <span class="ot">=</span> testSegmentsToPixelFrames segments</span> <span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Run the classifier on the pixel frames</span></span> <span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> counted <span class="ot">=</span> classifyMovement minStillSegmentTime (Pipes.each pixelFrames)</span> <span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> Pipes.toList</span> <span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> countSegments</span> <span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 5. Sanity check</span></span> <span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a> countTestSegmentFrames segments <span class="op">===</span> totalClassifiedFrames counted</span> <span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 6. Ignore last segment and verify all other segments</span></span> <span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> initMay counted <span class="kw">of</span></span> <span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> rest <span class="ot">-&gt;</span></span> <span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a> traverse_ (assertStillLengthAtLeast minStillSegmentTime) rest</span> <span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> success</span> <span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> <span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a> resolution <span class="ot">=</span> <span class="dv">10</span> <span class="op">:.</span> <span class="dv">10</span></span></code></pre></div> <p>This chunk of test code is pretty busy, and it’s using a few helper functions that I’m not going to bore you with. At a high level, this test:</p> <ol type="1"> <li>Generates a minimum still segment duration, based on a minimum frame count (let’s call it <span class="math inline"><em>n</em></span>) in the range <span class="math inline">[2, 20]</span>. The classifier currently requires that <span class="math inline"><em>n</em> ≥ 2</span>, hence the lower bound. The upper bound of 20 frames is an arbitrary number that I’ve chosen.</li> <li>Generates valid output segments using the custom generator <code>genSegments</code>, where <ul> <li>moving segments have a frame count in <span class="math inline">[1, 2<em>n</em>]</span>, and</li> <li>still segments have a frame count in <span class="math inline">[<em>n</em>, 2<em>n</em>]</span>.</li> </ul></li> <li>Converts the generated output segments to actual pixel frames. This is done using a helper function that returns a list of alternating gray and white frames, or all black frames, as described earlier.</li> <li>Count the number of consecutive frames within each segment, producing a list like <code>[Moving 18, Still 5, Moving 12, Still 30]</code>.</li> <li>Performs a sanity check that the number of frames in the generated expected output is equal to the number of frames in the classified output. The classifier must not lose or duplicate frames.</li> <li>Drops the last classified segment, which according to the specification can have a frame count less than <span class="math inline"><em>n</em></span>, and asserts that all other still segments have a frame count greater than or equal to <span class="math inline"><em>n</em></span>.</li> </ol> <p>Let’s run some tests.</p> <pre class="text"><code>&gt; :{ | hprop_classifies_still_segments_of_min_length | &amp; Hedgehog.withTests 10000 | &amp; Hedgehog.check | :} ✓ &lt;interactive&gt; passed 10000 tests.</code></pre> <p>Cool, it looks like it’s working.</p> <h2 id="sidetrack-why-generate-the-output">Sidetrack: Why generate the output?</h2> <p>Now, you might wonder why I generate output segments first, and then convert to pixel frames. Why not generate random pixel frames to begin with? The property test above only checks that the still segments are long enough!</p> <p>The benefit of generating valid output becomes clearer in the next property test, where I use it as the expected output of the classifier. Converting the output to a sequence of pixel frames is easy, and I don’t have to state any complex relation between the input and output in my property. When using oracle generators, the assertions can often be plain equality checks on generated and actual output.</p> <p>But there’s benefit in using the same oracle generator for the “minimum still segment length” property, even if it’s more subtle. By generating valid output and converting to pixel frames, I can generate inputs that cover the edge cases of the system under test. Using property test statistics and coverage checks, I could inspect coverage, and even fail test runs where the generators don’t hit enough of the cases I’m interested in.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p> <p>Had I generated random sequences of pixel frames, then perhaps the majority of the generated examples would only produce moving segments. I could tweak the generator to get closer to either moving or still frames, within some distribution, but wouldn’t that just be a variation of generating valid scenes? It would be worse, in fact. I wouldn’t then be reusing existing generators, and I wouldn’t have a high-level representation that I could easily convert from and compare with in assertions.</p> <h2 id="testing-moving-segment-time-spans">Testing Moving Segment Time Spans</h2> <p>The second property states that the classified moving segments must start and end at the same timestamps as the moving segments in the generated output. Compared to the previous property, the relation between generated output and actual classified output is stronger.</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>hprop_classifies_same_scenes_as_input <span class="ot">=</span> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate a minimum still still segment duration</span></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> minStillSegmentFrames <span class="ot">&lt;-</span> forAll <span class="op">$</span> Gen.int (Range.linear <span class="dv">2</span> (<span class="dv">2</span> <span class="op">*</span> frameRate))</span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> minStillSegmentTime <span class="ot">=</span> frameCountDuration minStillSegmentFrames</span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Generate test segments</span></span> <span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> segments <span class="ot">&lt;-</span> forAll <span class="op">$</span> genSegments (Range.linear <span class="dv">1</span> <span class="dv">10</span>)</span> <span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> (Range.linear <span class="dv">1</span></span> <span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> (minStillSegmentFrames <span class="op">*</span> <span class="dv">2</span>))</span> <span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> (Range.linear minStillSegmentFrames</span> <span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a> (minStillSegmentFrames <span class="op">*</span> <span class="dv">2</span>))</span> <span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a> resolution</span> <span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Convert test segments to actual pixel frames</span></span> <span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> pixelFrames <span class="ot">=</span> testSegmentsToPixelFrames segments</span> <span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Convert expected output segments to a list of expected time spans</span></span> <span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a> <span class="co">-- and the full duration</span></span> <span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> durations <span class="ot">=</span> <span class="fu">map</span> segmentWithDuration segments</span> <span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a> expectedSegments <span class="ot">=</span> movingSceneTimeSpans durations</span> <span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a> fullDuration <span class="ot">=</span> <span class="fu">foldMap</span> unwrapSegment durations</span> <span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 5. Classify movement of frames</span></span> <span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> classifiedFrames <span class="ot">=</span></span> <span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a> Pipes.each pixelFrames</span> <span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> classifyMovement minStillSegmentTime</span> <span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> Pipes.toList</span> <span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 6. Classify moving scene time spans</span></span> <span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> classified <span class="ot">=</span></span> <span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a> (Pipes.each classifiedFrames</span> <span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> classifyMovingScenes fullDuration)</span> <span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a> <span class="op">&gt;-&gt;</span> Pipes.drain</span> <span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> Pipes.runEffect</span> <span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> runIdentity</span> <span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 7. Check classified time span equivalence</span></span> <span id="cb3-38"><a href="#cb3-38" aria-hidden="true" tabindex="-1"></a> expectedSegments <span class="op">===</span> classified</span> <span id="cb3-39"><a href="#cb3-39" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-40"><a href="#cb3-40" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> <span id="cb3-41"><a href="#cb3-41" aria-hidden="true" tabindex="-1"></a> resolution <span class="ot">=</span> <span class="dv">10</span> <span class="op">:.</span> <span class="dv">10</span></span></code></pre></div> <p>Steps 1–3 are the same as in the previous property test. From there, this test:</p> <ol start="4" type="1"> <li>Converts the generated output segments into a list of time spans. Each time span marks the start and end of an expected moving segment. Furthermore, it needs the full duration of the input in step 6, so that’s computed here.</li> <li>Classify the movement of each frame, i.e. if it’s part of a moving or still segment.</li> <li>Run the second classifier function called <code>classifyMovingScenes</code>, based on the full duration and the frames with classified movement data, resulting in a list of time spans.</li> <li>Compare the expected and actual classified list of time spans.</li> </ol> <p>While this test looks somewhat complicated with its setup and various conversions, the core idea is simple. But is it effective?</p> <h3 id="bugs-bugs-everywhere">Bugs! Bugs everywhere!</h3> <p>Preparing for a talk on property-based testing, I added the “moving segment time spans” property a week or so before the event. At this time, I had used Komposition to edit multiple screencasts. Surely, all significant bugs were caught already. Adding property tests should only confirm the level of quality the application already had. Right?</p> <p>Nope. First, I discovered that my existing tests were fundamentally incorrect to begin with. They were not reflecting the specification I had in mind, the one I described in the beginning of this post.</p> <p>Furthermore, I found that the generators had errors. At first, I used Hedgehog to generate the pixels used for the classifier input. Moving frames were based on a majority of randomly colored pixels and a small percentage of equally colored pixels. Still frames were based on a random single color.</p> <p>The problem I had not anticipated was that the colors used in moving frames were not guaranteed to be distinct from the color used in still frames. In small-sized examples I got black frames at the beginning and end of moving segments, and black frames for still segments, resulting in different classified output than expected. Hedgehog shrinking the failing examples’ colors towards 0, which is black, highlighted this problem even more.</p> <p>I made my generators much simpler, using the alternating white/gray frames approach described earlier, and went on to running my new shiny tests. Here’s what I got:</p> <p><img src="/assets/property-based-testing-the-ugly-parts/video-classification-failure.png" /></p> <p>What? Where does 0s–0.6s come from? The classified time span should’ve been 0s–1s, as the generated output has a single moving scene of 10 frames (1 second at 10 FPS). I started digging, using the <code>annotate</code> function in Hedgehog to inspect the generated and intermediate values in failing examples.</p> <p>I couldn’t find anything incorrect in the generated data, so I shifted focus to the implementation code. The end timestamp 0.6s was consistently showing up in failing examples. Looking at the code, I found a curious hard-coded value 0.5 being bound and used locally in <code>classifyMovement</code>.</p> <p>The function is essentially a <em>fold</em> over a stream of frames, where the accumulator holds vectors of previously seen and not-yet-classified frames. Stripping down and simplifying the old code to highlight one of the bugs, it looked something like this:</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>classifyMovement minStillSegmentTime <span class="ot">=</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> <span class="op">...</span> <span class="kw">of</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">InStillState</span>{<span class="op">..</span>} <span class="ot">-&gt;</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">if</span> someDiff <span class="op">&gt;</span> minEqualTimeForStill</span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">then</span> <span class="op">...</span></span> <span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">else</span> <span class="op">...</span></span> <span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">InMovingState</span>{<span class="op">..</span>} <span class="ot">-&gt;</span></span> <span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">if</span> someOtherDiff <span class="op">&gt;=</span> minStillSegmentTime</span> <span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">then</span> <span class="op">...</span></span> <span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">else</span> <span class="op">...</span></span> <span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> <span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> minEqualTimeForStill <span class="ot">=</span> <span class="fl">0.5</span></span></code></pre></div> <p>Let’s look at what’s going on here. In the <code>InStillState</code> branch it uses the value <code>minEqualTimeForStill</code>, instead of always using the <code>minStillSegmentTime</code> argument. This is likely a residue from some refactoring where I meant to make the value a parameter instead of having it hard-coded in the definition.</p> <p>Sparing you the gory implementation details, I’ll outline two more problems that I found. In addition to using the hard-coded value, it incorrectly classified frames based on that value. Frames that should’ve been classified as “moving” ended up “still”. That’s why I didn’t get 0s–1s in the output.</p> <p>Why didn’t I see 0s–0.5s, given the hard-coded value 0.5? Well, there was also an off-by-one bug, in which one frame was classified incorrectly together with the accumulated moving frames.</p> <p>The <code>classifyMovement</code> function is 30 lines of Haskell code juggling some state, and I managed to mess it up in three separate ways at the same time. With these tests in place I quickly found the bugs and fixed them. I ran thousands of tests, all passing.</p> <p>Finally, I ran the application, imported a previously recorded video, and edited a short screencast. The classified moving segments where <em>notably</em> better than before.</p> <h2 id="summary">Summary</h2> <p>A simple streaming fold can hide bugs that are hard to detect with manual testing. The consistent result of 0.6, together with the hard-coded value 0.5 and a frame rate of 10 FPS, pointed clearly towards an off-by-one bug. I consider this is a great showcase of how powerful shrinking in PBT is, consistently presenting minimal examples that point towards specific problems. It’s not just a party trick on ideal mathematical functions.</p> <p>Could these errors have been caught without PBT? I think so, but what effort would it require? Manual testing and introspection did not work for me. Code review might have revealed the incorrect definition of <code>minEqualTimeForStill</code>, but perhaps not the off-by-one and incorrect state handling bugs. There are of course many other QA techniques, I won’t evaluate all. But given the low effort that PBT requires in this setting, the amount of problems it finds, and the accuracy it provides when troubleshooting, I think it’s a clear win.</p> <p>I also want to highlight the iterative process that I find naturally emerges when applying PBT:</p> <ol type="1"> <li>Think about how your system is supposed to work. Write down your <em>specification</em>.</li> <li>Think about how to generate input data and how to test your system, based on your specification. Tune your generators to provide better test data. Try out alternative styles of properties. Perhaps model-based or metamorphic testing fits your system better.</li> <li>Run tests and analyze the minimal failing examples. Fix your implementation until all tests pass.</li> </ol> <p>This can be done when modifying existing code, or when writing new code. You can apply this without having any implementation code yet, perhaps just a minimal stub, and the workflow is essentially the same as TDD.</p> <h2 id="coming-up">Coming Up</h2> <p>The final post in this series will cover testing at a higher level of the system, with effects and multiple subsystems being integrated to form a full application. We will look at property tests that found many bugs and that made a substantial refactoring possible.</p> <ol type="1"> <li><a href="/2019-03-02-property-based-testing-in-a-screencast-editor-introduction.html">Introduction</a></li> <li><a href="/2019-03-24-property-based-testing-in-a-screencast-editor-case-study-1.html">Timeline Flattening</a></li> <li><strong>Video Scene Classification</strong></li> <li><a href="/2019-06-02-property-based-testing-in-a-screencast-editor-case-study-3.html">Integration Testing</a></li> </ol> <p>Until then, thanks for reading!</p> <h2 id="credits">Credits</h2> <p>Thank you Ulrik Sandberg, Pontus Nagy, and Fredrik Björeman for reviewing drafts of this post.</p> <h2 id="buy-the-book">Buy the Book</h2> <p>This series is now available as an <a href="https://leanpub.com/property-based-testing-in-a-screencast-editor">ebook on Leanpub</a>. While the content is mostly the same, there are few changes bringing it up-to-date. Also, if you’ve already enjoyed the articles, you might want support my work by purchasing this book. Finally, you might enjoy a nicely typeset PDF, or an EPUB book, over a web page.</p> <h2 id="footnotes">Footnotes</h2> <section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"> <hr /> <ol> <li id="fn1"><p>See the “Oracle Generators” section in <a href="https://www.hillelwayne.com/post/contract-examples/">Finding Property Tests</a>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li> <li id="fn2"><p>John Hughes’ talk <a href="https://www.youtube.com/watch?v=NcJOiQlzlXQ">Building on developers’ intuitions</a> goes into depth on this. There’s also <a href="https://github.com/hedgehogqa/haskell-hedgehog/pull/253">work being done</a> to provide similar functionality for Hedgehog.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li> </ol> </section>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2019-03-24-property-based-testing-in-a-screencast-editor-case-study-1.html</id>
    <title>Property-Based Testing in a Screencast Editor, Case Study 1: Timeline Flattening</title>
    <link href="https://wickstrom.tech/2019-03-24-property-based-testing-in-a-screencast-editor-case-study-1.html"/>
    <published>2019-03-24T00:00:00+01:00</published>
    <updated>2019-03-24T00:00:00+01:00</updated>
    <summary>In the first post of this series I introduced the Komposition screencast
editor, and briefly explained the fundamentals of property-based testing
(PBT). Furthermore, I covered how to write testable code, regardless of
how you check your code with automated tests. Lastly, I highlighted some
difficulties in using properties to perform component and integration
testing.</summary>
    <content type="html"><![CDATA[
<p>In <a href="/2019-03-02-property-based-testing-in-a-screencast-editor-introduction.html">the first post of this series</a> I introduced the Komposition screencast editor, and briefly explained the fundamentals of property-based testing (PBT). Furthermore, I covered how to write testable code, regardless of <em>how</em> you check your code with automated tests. Lastly, I highlighted some difficulties in using properties to perform component and integration testing.</p> <p>If you haven’t read the introductory post, I suggest doing so before continuing with this one. You’ll need an understanding of what PBT is for this case study to make sense.</p> <p>This post is the first case study in the series, covering the <em>timeline flattening</em> process in Komposition and how it’s tested using PBT. The property tests aren’t integration-level tests, but rather unit tests. This case study serves as a warm-up to the coming, more advanced, ones.</p> <p>Before we look at the tests, we need to learn more about Komposition’s hierarchical timeline and how the flattening process works.</p> <h2 id="the-hierarchical-timeline">The Hierarchical Timeline</h2> <p>Komposition’s timeline is hierarchical. While many non-linear editing systems have support for some form of nesting<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> they are primarily focused on flat timeline workflows. The timeline structure and the keyboard-driven editing in Komposition is optimized for the screencast editing workflow I use.</p> <p>It’s worth emphasizing that Komposition is not a general video editor. In addition to its specific editing workflow, you may need to adjust your recording workflow to use it effectively<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>.</p> <h3 id="video-and-audio-in-parallels">Video and Audio in Parallels</h3> <p>At the lowest level of the timeline are <em>clips</em> and <em>gaps</em>. Those are put within the video and audio <em>tracks</em> of <em>parallels</em>. The following diagram shows a parallel consisting of two video clips and one audio clip.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram1.png" width="350" alt="Clips and gaps are placed in video and audio tracks" /> <figcaption aria-hidden="true">Clips and gaps are placed in video and audio tracks</figcaption> </figure> <p>The tracks of a parallel are played simultaneously (in parallel), as indicated by the arrows in the above diagram. The tracks start playing at the same time. This makes parallels useful to synchronize the playback of specific parts of a screencast, and to group closely related clips.</p> <h3 id="gaps">Gaps</h3> <p>When editing screencasts made up of separate video and audio recordings you often end up with differing clip duration. The voice-over audio clip might be longer than the corresponding video clip, or vice versa. A useful default behaviour is to extend the short clips. For audio, this is easy. Just pad with silence. For video, it’s not so clear what to do. In Komposition, shorter video tracks are padded with repeated still frame sections called <em>gaps</em>.</p> <p>The following diagram shows a parallel with a short video clip and a longer audio clip. The dashed area represents the implicit gap.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram2.png" width="350" alt="Still frames are automatically inserted at implicit gaps to match track duration" /> <figcaption aria-hidden="true">Still frames are automatically inserted at implicit gaps to match track duration</figcaption> </figure> <p>You can also add gaps manually, specifying a duration of the gap and inserting it into a video or audio track. The following diagram shows a parallel with manually added gaps in both video and audio tracks.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram3.png" width="350" alt="Adding explicit gaps manually" /> <figcaption aria-hidden="true">Adding explicit gaps manually</figcaption> </figure> <p>Manually added gaps (called <em>explicit</em> gaps) are padded with still frames or silence, just as implicit gaps that are added automatically to match track duration.</p> <h3 id="sequences">Sequences</h3> <p>Parallels are put in <em>sequences</em>. The parallels within a sequence are played sequentially; the first one is played in its entirety, then the next one, and so on. This behaviour is different from how parallels play their tracks. Parallels and sequences, with their different playback behaviors, make up the fundamental building blocks of the compositional editing in Komposition.</p> <p>The following diagram shows a sequence of two parallels, playing sequentially:</p> <figure> <img src="/assets/pbt-screencast-editor/diagram4.png" width="600" alt="A sequence containing two parallels" /> <figcaption aria-hidden="true">A sequence containing two parallels</figcaption> </figure> <h3 id="the-timeline">The Timeline</h3> <p>Finally, at the top level, we have the <em>timeline</em>. Effectively, the timeline is a sequence of sequences; it plays every child sequence in sequence. The reason for this level to exist is for the ability to group larger chunks of a screencast within separate sequences.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram5.png" width="750" alt="A timeline containing two sequences, with two parallels each" /> <figcaption aria-hidden="true">A timeline containing two sequences, with two parallels each</figcaption> </figure> <p>I use separate sequences within the timeline to delimit distinct parts of a screencast, such as the introduction, the different chapters, and the summary.</p> <h2 id="timeline-flattening">Timeline Flattening</h2> <p>Komposition currently uses <a href="https://ffmpeg.org/">FFmpeg</a> to render the final media. This is done by constructing an <code>ffmpeg</code> command invocation with a <a href="https://ffmpeg.org/ffmpeg-filters.html">filter graph</a> describing how to fit together all clips, still frames, and silent audio parts.</p> <p>FFmpeg doesn’t know about hierarchical timelines; it only cares about video and audio streams. To convert the hierarchical timeline into a suitable representation to build the FFmpeg filter graph from, Komposition performs <em>timeline flattening</em>.</p> <p>The flat representation of a timeline contains only two tracks; audio and video. All gaps are <em>explicitly</em> represented in those tracks. The following graph shows how a hierarchical timeline is flattened into two tracks.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram6.png" width="750" alt="Timeline flattening transforming a hierarchical timeline" /> <figcaption aria-hidden="true">Timeline flattening transforming a hierarchical timeline</figcaption> </figure> <p>Notice in the graphic above how the implicit gaps at the ends of video and audio tracks get represented with explicit gaps in the flat timeline. This is because FFmpeg does not know how to render implicit gaps. All gaps are represented explicitly, and are converted to clips of still frames or silent audio when rendered with FFmpeg.</p> <h2 id="property-tests">Property Tests</h2> <p>To test the timeline flattening, there’s a number of properties that are checked. I’ll go through each one and their property test code.</p> <p>These properties were primarily written after I already had an implementation. They capture some general properties of flattening that I’ve come up with. In other cases, I’ve written properties before beginning on an implementation, or to uncover an existing bug that I’ve observed.</p> <p>Thinking about your system’s general behaviour and expressing that as executable property tests is hard. I believe, like with any other skill, that it requires a lot of practice. Finding general patterns for properties, like the ones Scott Wlaschin describe in <a href="https://fsharpforfunandprofit.com/posts/property-based-testing-2/">Choosing properties for property-based testing</a>, is a great place to start. When you struggle with finding properties of your system under test, try applying these patterns and see which work for you.</p> <h3 id="property-duration-equality">Property: Duration Equality</h3> <p>Given a timeline <span class="math inline"><em>t</em></span>, where all parallels have at least one video clip, the total duration of the flattened <span class="math inline"><em>t</em></span> must be equal to the total duration of <span class="math inline"><em>t</em></span>. Or, in a more dense notation,</p> <p><span class="math display">∀<em>t</em> ∈ <em>T</em> → <em>d</em><em>u</em><em>r</em><em>a</em><em>t</em><em>i</em><em>o</em><em>n</em>(<em>f</em><em>l</em><em>a</em><em>t</em><em>t</em><em>e</em><em>n</em>(<em>t</em>)) = <em>d</em><em>u</em><em>r</em><em>a</em><em>t</em><em>i</em><em>o</em><em>n</em>(<em>t</em>)</span></p> <p>where <span class="math inline"><em>T</em></span> is the set of timelines with at least one video clip in each parallel.</p> <p>The reason that all parallels must have at least one video clip is because currently the flattening algorithm can only locate still frames for video gaps from within the same parallel. If it encounters a parallel with no video clips, the timeline flattening fails. This limitation is discussed in greater detail at the end of this article.</p> <p>The test for the duration equality property is written using Hedgehog, and looks like this:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>hprop_flat_timeline_has_same_duration_as_hierarchical <span class="ot">=</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate a timeline with video clips in each parallel</span></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> timeline&#39; <span class="ot">&lt;-</span> forAll <span class="op">$</span></span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> Gen.timeline (Range.exponential <span class="dv">0</span> <span class="dv">5</span>) Gen.parallelWithClips</span> <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Flatten the timeline and extract the result</span></span> <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> <span class="dt">Just</span> flat <span class="ot">=</span> Render.flattenTimeline timeline&#39;</span> <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Check that hierarchical and flat timeline duration are equal</span></span> <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> durationOf <span class="dt">AdjustedDuration</span> timeline&#39;</span> <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> <span class="op">===</span> durationOf <span class="dt">AdjustedDuration</span> flat</span></code></pre></div> <p>It generates a timeline using <code>forAll</code> and custom generators (1). Instead of generating timelines of <em>any</em> shape and filtering out only the ones with video clips in each parallel, which would be very inefficient, this test uses a custom generator to only obtain inputs that satisfy the invariants of the system under test.</p> <p>The range passed as the first argument to <code>Gen.timeline</code> is used as the bounds of the generator, such that each level in the generated hierarchical timeline will have at most 5 children.</p> <p><code>Gen.timeline</code> takes as its second argument <em>another generator</em>, the one used to generate parallels, which in this case is <code>Gen.parallelWithClips</code>. With Hedgehog generators being regular values, it’s practical to compose them like this. A “higher-order generator” can be a regular function taking other generators as arguments.</p> <p>As you might have noticed in the assertion (3), <code>durationOf</code> takes as its first argument a value <code>AdjustedDuration</code>. What’s that about? Komposition supports adjusting the playback speed of video media for individual clips. To calculate the final duration of a clip, the playback speed needs to taken into account. By passing <code>AdjustedDuration</code> we take playback speed into account for all video clips.</p> <h4 id="sidetrack-finding-a-bug">Sidetrack: Finding a Bug</h4> <p>Let’s say I had introduced a bug in timeline flattening, in which all video gaps weren’t added correctly to the flat video tracks. The flattening is implemented as a fold, and it would not be unthinkable that the accumulator was incorrectly constructed in a case. The test would catch this quickly and present us with a minimal counter-example:</p> <figure> <img src="/assets/property-based-testing-the-ugly-parts/timeline-duration-failure.png" style="width:100.0%" alt="Hedgehog presenting a minimal counter-example" /> <figcaption aria-hidden="true">Hedgehog presenting a minimal counter-example</figcaption> </figure> <p>Hedgehog prints the source code for the failing property. Below the <code>forAll</code> line the generated value is printed. The difference between the expected and actual value is printed below the failing assertion. In this case it’s a simple expression of type <code>Duration</code>. In case you’re comparing large tree-like structures, this diff will highlight only the differing expressions. Finally, it prints the following:</p> <pre><code>This failure can be reproduced by running: &gt; recheck (Size 23) (Seed 16495576598183007788 5619008431246301857) &lt;property&gt;</code></pre> <p>When working on finding and fixing the fold bug, we can use the printed <em>size</em> and <em>seed</em> values to deterministically rerun the test with the exact same inputs.</p> <h2 id="property-clip-occurrence">Property: Clip Occurrence</h2> <p>Slightly more complicated than the duration equality property, the clip occurrence property checks that all clips from the hierarchical timeline, and no other clips, occur within the flat timeline. As discussed in the introduction on timeline flattening, implicit gaps get converted to explicit gaps and thereby add more gaps, but no video or audio clips should be added or removed.</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>hprop_flat_timeline_has_same_clips_as_hierarchical <span class="ot">=</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate a timeline with video clips in each parallel</span></span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> timeline&#39; <span class="ot">&lt;-</span> forAll <span class="op">$</span></span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> Gen.timeline (Range.exponential <span class="dv">0</span> <span class="dv">5</span>) Gen.parallelWithClips</span> <span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Flatten the timeline</span></span> <span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> flat <span class="ot">=</span> Render.flattenTimeline timeline&#39;</span> <span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Check that all video clips occur in the flat timeline</span></span> <span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a> flat <span class="op">^..</span> _Just <span class="op">.</span> Render.videoParts <span class="op">.</span> each <span class="op">.</span> Render._VideoClipPart</span> <span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a> <span class="op">===</span> timelineVideoClips timeline&#39;</span> <span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a></span> <span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Check that all audio clips occur in the flat timeline</span></span> <span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a> flat <span class="op">^..</span> _Just <span class="op">.</span> Render.audioParts <span class="op">.</span> each <span class="op">.</span> Render._AudioClipPart</span> <span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a> <span class="op">===</span> timelineAudioClips timeline&#39;</span></code></pre></div> <p>The hierarchical timeline is generated and flattened like before (1, 2). The two assertions check that the respective video clips (3) and audio clips (4) are equal. It’s using lenses to extract clips from the flat timeline, and the helper functions <code>timelineVideoClips</code> and <code>timelineAudioClips</code> to extract clips from the original hierarchical timeline.</p> <h2 id="still-frames-used">Still Frames Used</h2> <p>In the process of flattening, the still frame source for each gap is selected. It doesn’t assign the actual pixel data to the gap, but a value describing which asset the still frame should be extracted from, and whether to pick the first or the last frame (known as <em>still frame mode</em>.) This representation lets the flattening algorithm remain a pure function, and thus easier to test. Another processing step runs the effectful action that extracts still frames from video files on disk.</p> <p>The decision of still frame mode and source is made by the flattening algorithm based on the parallel in which each gap occur, and what video clips are present before or after. It favors using clips occurring after the gap. It only uses frames from clips before the gap in case there are no clips following it. To test this behaviour, I’ve defined three properties.</p> <h3 id="property-single-initial-video-clip">Property: Single Initial Video Clip</h3> <p>The following property checks that an initial single video clip, followed by one or more gaps, is used as the still frame source for those gaps.</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>hprop_flat_timeline_uses_still_frame_from_single_clip <span class="ot">=</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate a video track generator where the first</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="co">-- video part is always a clip</span></span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> genVideoTrack <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> v1 <span class="ot">&lt;-</span> Gen.videoClip</span> <span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> vs <span class="ot">&lt;-</span> Gen.list (Range.linear <span class="dv">1</span> <span class="dv">5</span>) Gen.videoGap</span> <span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (<span class="dt">VideoTrack</span> () (v1 <span class="op">:</span> vs))</span> <span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Generate a timeline with the custom video track</span></span> <span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> <span class="co">-- generator</span></span> <span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> timeline&#39; <span class="ot">&lt;-</span> forAll <span class="op">$</span> Gen.timeline</span> <span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a> (Range.exponential <span class="dv">0</span> <span class="dv">5</span>)</span> <span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a> (<span class="dt">Parallel</span> () <span class="op">&lt;$&gt;</span> genVideoTrack <span class="op">&lt;*&gt;</span> Gen.audioTrack)</span> <span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Flatten the timeline</span></span> <span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> flat <span class="ot">=</span> Render.flattenTimeline timeline&#39;</span> <span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Check that any video gaps will use the last frame</span></span> <span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a> <span class="co">-- of a preceding video clip</span></span> <span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a> flat</span> <span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a> <span class="op">^..</span> ( _Just</span> <span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> Render.videoParts</span> <span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> each</span> <span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> Render._StillFramePart</span> <span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> Render.stillFrameMode</span> <span id="cb4-27"><a href="#cb4-27" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb4-28"><a href="#cb4-28" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> traverse_ (<span class="dt">Render.LastFrame</span> <span class="op">===</span>)</span></code></pre></div> <p>The custom video track generator (1) always produces tracks with an initial video clip followed by one or more video gaps. The generated timeline (2) can contain parallels with any audio track shape, which may result in a <em>longer</em> audio track and thus an implicit gap at the end of the video track. In either case, all video gaps should padded with the last frame of the initial video clip, which is checked in the assertion (4).</p> <figure> <img src="/assets/pbt-screencast-editor/diagram7.png" width="375" alt="Still frames being sourced from the single initial video clip" /> <figcaption aria-hidden="true">Still frames being sourced from the single initial video clip</figcaption> </figure> <h3 id="property-ending-with-a-video-clip">Property: Ending with a Video Clip</h3> <p>In case the video track ends with a video clip, and is longer than the audio track, all video gaps within the track should use the first frame of a following clip.</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>hprop_flat_timeline_uses_still_frames_from_subsequent_clips <span class="ot">=</span></span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate a parallel where the video track ends with</span></span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="co">-- a video clip, and where the audio track is shorter</span></span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span></span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> genParallel <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> vt <span class="ot">&lt;-</span></span> <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">VideoTrack</span> ()</span> <span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;$&gt;</span> ( snoc</span> <span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;$&gt;</span> Gen.list (Range.linear <span class="dv">1</span> <span class="dv">10</span>) Gen.videoPart</span> <span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;*&gt;</span> Gen.videoClip</span> <span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a> at <span class="ot">&lt;-</span> <span class="dt">AudioTrack</span> () <span class="op">.</span> <span class="fu">pure</span> <span class="op">.</span> <span class="dt">AudioGap</span> () <span class="op">&lt;$&gt;</span> Gen.duration&#39;</span> <span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a> (Range.linearFrac</span> <span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span></span> <span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a> (durationToSeconds (durationOf <span class="dt">AdjustedDuration</span> vt) <span class="op">-</span> <span class="fl">0.1</span>)</span> <span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (<span class="dt">Parallel</span> () vt at)</span> <span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Generate a timeline with the custom parallel generator</span></span> <span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a> timeline&#39; <span class="ot">&lt;-</span> forAll <span class="op">$</span> Gen.timeline (Range.exponential <span class="dv">0</span> <span class="dv">5</span>) genParallel</span> <span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Flatten the timeline</span></span> <span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> flat <span class="ot">=</span> Render.flattenTimeline timeline&#39;</span> <span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Check that all gaps use the first frame of subsequent clips</span></span> <span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a> flat</span> <span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a> <span class="op">^..</span> ( _Just</span> <span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> Render.videoParts</span> <span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> each</span> <span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> Render._StillFramePart</span> <span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> Render.stillFrameMode</span> <span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> traverse_ (<span class="dt">Render.FirstFrame</span> <span class="op">===</span>)</span></code></pre></div> <p>The custom generator (1) produces parallels where the video track is guaranteed to end with a clip, and where the audio track is 100 ms shorter than the video track. This ensures that there’s no implicit video gap at the end of the video track. Generating (2) and flattening (3) is otherwise the same as before. The assertion (4) checks that all video gaps uses the first frame of a following clip.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram8.png" width="600" alt="Still frames being sourced from following video clips when possible" /> <figcaption aria-hidden="true">Still frames being sourced from following video clips when possible</figcaption> </figure> <h3 id="property-ending-with-an-implicit-video-gap">Property: Ending with an Implicit Video Gap</h3> <p>The last property on still frame usage covers the case where the video track is shorter than the audio track. This leaves an implicit gap which, just like explicit gaps inserted by the user, are padded with still frames.</p> <div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>hprop_flat_timeline_uses_last_frame_for_automatic_video_padding <span class="ot">=</span></span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate a parallel where the video track only contains a video</span></span> <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="co">-- clip, and where the audio track is longer</span></span> <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span></span> <span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> genParallel <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> vt <span class="ot">&lt;-</span> <span class="dt">VideoTrack</span> () <span class="op">.</span> <span class="fu">pure</span> <span class="op">&lt;$&gt;</span> Gen.videoClip</span> <span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> at <span class="ot">&lt;-</span> <span class="dt">AudioTrack</span> () <span class="op">.</span> <span class="fu">pure</span> <span class="op">.</span> <span class="dt">AudioGap</span> () <span class="op">&lt;$&gt;</span> Gen.duration&#39;</span> <span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> (Range.linearFrac</span> <span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> (durationToSeconds (durationOf <span class="dt">AdjustedDuration</span> vt) <span class="op">+</span> <span class="fl">0.1</span>)</span> <span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> <span class="dv">10</span></span> <span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (<span class="dt">Parallel</span> () vt at)</span> <span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a></span> <span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Generate a timeline with the custom parallel generator</span></span> <span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a> timeline&#39; <span class="ot">&lt;-</span> forAll <span class="op">$</span> Gen.timeline (Range.exponential <span class="dv">0</span> <span class="dv">5</span>) genParallel</span> <span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a></span> <span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Flatten the timeline</span></span> <span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> flat <span class="ot">=</span> Render.flattenTimeline timeline&#39;</span> <span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a></span> <span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Check that video gaps (which should be a single gap at the</span></span> <span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a> <span class="co">-- end of the video track) use the last frame of preceding clips</span></span> <span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a> flat</span> <span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a> <span class="op">^..</span> ( _Just</span> <span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> Render.videoParts</span> <span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> each</span> <span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> Render._StillFramePart</span> <span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> Render.stillFrameMode</span> <span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> traverse_ (<span class="dt">Render.LastFrame</span> <span class="op">===</span>)</span></code></pre></div> <p>The custom generator (1) generates a video track consisting of video clips only, and an audio track that is 100ms longer. Generating the timeline (2) and flattening (3) are again similar to the previous property tests. The assertion (4) checks that all video gaps use the last frame of preceding clips, even if we know that there should only be one at the end.</p> <figure> <img src="/assets/pbt-screencast-editor/diagram9.png" width="350" alt="Still frames being sourced from preceding video clip for last implicit gap" /> <figcaption aria-hidden="true">Still frames being sourced from preceding video clip for last implicit gap</figcaption> </figure> <h2 id="properties-flattening-equivalences">Properties: Flattening Equivalences</h2> <p>The last property I want to show in this case study checks flattening at the sequence and parallel levels. While rendering a full project always flattens at the timeline, the <em>preview</em> feature in Komposition can be used to render and preview a single sequence or parallel.</p> <p>There should be no difference between flattening an entire timeline and flattening all of its sequences or parallels and folding those results into a single flat timeline. This is what the <em>flattening equivalences</em> properties are about.</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>hprop_flat_timeline_is_same_as_all_its_flat_sequences <span class="ot">=</span></span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate a timeline</span></span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> timeline&#39; <span class="ot">&lt;-</span> forAll <span class="op">$</span></span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> Gen.timeline (Range.exponential <span class="dv">0</span> <span class="dv">5</span>) Gen.parallelWithClips</span> <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Flatten all sequences and fold the resulting flat</span></span> <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> <span class="co">-- timelines together</span></span> <span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> flat <span class="ot">=</span> timeline&#39; <span class="op">^..</span> sequences <span class="op">.</span> each</span> <span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> <span class="fu">foldMap</span> Render.flattenSequence</span> <span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a></span> <span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Make sure we successfully flattened the timeline</span></span> <span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> flat <span class="op">/==</span> <span class="dt">Nothing</span></span> <span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a></span> <span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Flatten the entire timeline and compare to the</span></span> <span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> <span class="co">-- flattened sequences</span></span> <span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> Render.flattenTimeline timeline&#39; <span class="op">===</span> flat</span></code></pre></div> <p>The first property generates a timeline (1) where all parallels have at least one video clip. It flattens all sequences within the timeline and folds the results together (2). Folding flat timelines together means concatenating their video and audio tracks, resulting in a single flat timeline.</p> <p>Before the final assertion, it checks that we got a result (3) and not <code>Nothing</code>. As it’s using the <code>Gen.parallelWithClips</code> generator there should always be video clips in each parallel, and we should always successfully flatten and get a result. The final assertion (4) checks that rendering the original timeline gives the same result as the folded-together results of rendering each sequence.</p> <p>The other property is very similar, but operates on parallels rather than sequences:</p> <div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>hprop_flat_timeline_is_same_as_all_its_flat_parallels <span class="ot">=</span></span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> property <span class="op">$</span> <span class="kw">do</span></span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 1. Generate a timeline</span></span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> timeline&#39; <span class="ot">&lt;-</span> forAll <span class="op">$</span></span> <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> Gen.timeline (Range.exponential <span class="dv">0</span> <span class="dv">5</span>) Gen.parallelWithClips</span> <span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 2. Flatten all parallels and fold the resulting flat</span></span> <span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="co">-- timelines together</span></span> <span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> flat <span class="ot">=</span> timeline&#39; <span class="op">^..</span> sequences <span class="op">.</span> each <span class="op">.</span> parallels <span class="op">.</span> each</span> <span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span> <span class="fu">foldMap</span> Render.flattenParallel</span> <span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a></span> <span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 3. Make sure we successfully flattened the timeline</span></span> <span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a> flat <span class="op">/==</span> <span class="dt">Nothing</span></span> <span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a></span> <span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 4. Flatten the entire timeline and compare to the</span></span> <span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a> <span class="co">-- flattened parallels</span></span> <span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a> Render.flattenTimeline timeline&#39; <span class="op">===</span> flat</span></code></pre></div> <p>The only difference is in the traversal (2), where we apply <code>Render.flattenParallel</code> to each parallel instead of applying <code>Render.flattenSequence</code> to each sequence.</p> <h2 id="missing-properties">Missing Properties</h2> <p>Whew! That was quite a lot of properties and code, especially for a warm-up. But timeline flattening could be tested more thoroughly! I haven’t yet written the following properties, but I’m hoping to find some time to add them:</p> <ul> <li><p><strong>Clip playback timestamps are the same.</strong> The “clip occurrence” property only checks that the hierarchical timeline’s clips occur in the flat timeline. It doesn’t check <em>when</em> in the flat timeline they occur. One way to test this would be to first annotate each clip in original timeline with its playback timestamp, and transfer this information through to the flat timeline. Then the timestamps could be included in the assertion.</p></li> <li><p><strong>Source assets used as still frame sources.</strong> The “still frames used” properties only check the still frame <em>mode</em> of gaps, not the still frame <em>sources</em>. The algorithm could have a bug where it always uses the first video clip’s asset as a frame source, and the current property tests would not catch it.</p></li> <li><p><strong>Same flat result is produced regardless of sequence grouping.</strong> Sequences can be split or joined in any way without affecting the final rendered media. They are merely ways of organizing parallels in logical groups. A property could check that however you split or join sequences within a timeline, the flattened result is the same.</p></li> </ul> <h2 id="a-missing-feature">A Missing Feature</h2> <p>As pointed out earlier, parallels must have at least one video clip. The flattening algorithm can only locate still frame sources for video gaps from within the same parallel. This is an annoying limitation when working with Komposition, and the algorithm should be improved.</p> <p>As the existing set of properties describe timeline flattening fairly well, changing the algorithm could be done with a TDD-like workflow:</p> <ol type="1"> <li>Modify the property tests to capture the intended behaviour</li> <li>Tests will fail, with the errors showing how the existing implementation fails to find still frame sources as expected</li> <li>Change the implementation to make the tests pass</li> </ol> <p>PBT is not only an after-the-fact testing technique. It can be used much like conventional example-based testing to drive development.</p> <h2 id="obligatory-cliff-hanger">Obligatory Cliff-Hanger</h2> <p>In this post we’ve looked at timeline flattening, the simplest case study in the “Property-Based Testing in a Screencast Editor” series. The system under test was a module of pure functions, with complex enough behaviour to showcase PBT as a valuable tool. The tests are more closely related to the design choices and concrete representations of the implementation.</p> <p>Coming case studies will dive deeper into the more complex subsystems of Komposition, and finally we’ll see how PBT can be used for integration testing. At that level, the property tests are less tied to the implementation, and focus on describing the higher-level outcomes of the interaction between subsystems.</p> <p>Next up is property tests for the video classifier. It’s also implemented a pure function, but with slightly more complicated logic that is trickier to test. We’re going to look at an interesting technique where we generate the <em>expected output</em> instead of the input.</p> <ol type="1"> <li><a href="/2019-03-02-property-based-testing-in-a-screencast-editor-introduction.html">Introduction</a></li> <li><strong>Timeline Flattening</strong></li> <li><a href="/2019-04-17-property-based-testing-in-a-screencast-editor-case-study-2.html">Video Scene Classification</a></li> <li><a href="/2019-06-02-property-based-testing-in-a-screencast-editor-case-study-3.html">Integration Testing</a></li> </ol> <p>Thanks for reading! See you next time.</p> <h2 id="credits">Credits</h2> <p>Thank you Chris Ford and Ulrik Sandberg for proof-reading and giving valuable feedback on drafts of this post.</p> <h2 id="buy-the-book">Buy the Book</h2> <p>This series is now available as an <a href="https://leanpub.com/property-based-testing-in-a-screencast-editor">ebook on Leanpub</a>. While the content is mostly the same, there are few changes bringing it up-to-date. Also, if you’ve already enjoyed the articles, you might want support my work by purchasing this book. Finally, you might enjoy a nicely typeset PDF, or an EPUB book, over a web page.</p> <h2 id="footnotes">Footnotes</h2> <section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"> <hr /> <ol> <li id="fn1"><p>Final Cut Pro has <a href="https://support.apple.com/kb/PH12631?locale=en_US">compound clips</a>, and Adobe Premiere Pro has <a href="https://www.premiumbeat.com/blog/nesting-in-adobe-premiere-pro/">nested sequences</a>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li> <li id="fn2"><p>The <a href="https://owickstrom.github.io/komposition/user-guide/workflow/">section on workflow</a> in Komposition’s documentation describes how to plan, record, and edit your screencast in way compatible with Komposition.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li> </ol> </section>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2019-03-02-property-based-testing-in-a-screencast-editor-introduction.html</id>
    <title>Property-Based Testing in a Screencast Editor: Introduction</title>
    <link href="https://wickstrom.tech/2019-03-02-property-based-testing-in-a-screencast-editor-introduction.html"/>
    <published>2019-03-02T00:00:00+01:00</published>
    <updated>2019-03-02T00:00:00+01:00</updated>
    <summary>This is the first in a series of posts about using property-based
testing (PBT) within Komposition, a screencast editor that I’ve been
working on during the last year. It introduces PBT and highlights some
challenges in testing properties of an application like Komposition.</summary>
    <content type="html"><![CDATA[
<p>This is the first in a series of posts about using property-based testing (PBT) within <em>Komposition</em>, a screencast editor that I’ve been working on during the last year. It introduces PBT and highlights some challenges in testing properties of an application like Komposition.</p> <p>Future posts will focus on individual case studies, covering increasingly complex components and how they are tested. I’ll reflect on what I’ve learned in each case, what bugs the tests have found, and what still remains to be improved.</p> <p>For example, I’ll explain how using PBT helped me find and fix bugs in the specification and implementation of Komposition’s video classifier. Those were bugs that would be very hard to find using example-based tests or using a static type system!</p> <p>This series is not a tutorial on PBT, but rather a collection of motivating examples. That said, you should be able to follow along without prior knowledge of PBT.</p> <h2 id="komposition">Komposition</h2> <p>In early 2018 I started producing <a href="https://haskell-at-work.com">Haskell screencasts</a>. A majority of the work involved cutting and splicing video by hand in a <a href="https://en.wikipedia.org/wiki/Non-linear_editing_system">non-linear editing system (NLE)</a> like <a href="https://en.wikipedia.org/wiki/Adobe_Premiere_Pro">Premiere Pro</a> or <a href="https://kdenlive.org/en/">Kdenlive</a>. I decided to write a screencast editor specialized for my needs, reducing the amount of manual labor needed to edit the recorded material. Komposition was born.</p> <p>Komposition is a modal GUI application built for editing screencasts. Unlike most NLE systems, it features a hierarchical timeline, built out of <em>sequences</em>, <em>parallels</em>, <em>tracks</em>, <em>clips</em>, and <em>gaps</em>. To make the editing experience more efficient, it automatically classifies scenes in screen capture video, and sentences in recorded voice-over audio.</p> <p>If you are curious about Komposition and want to learn more right away, check out its <a href="https://owickstrom.github.io/komposition/">documentation</a>.</p> <figure> <img src="/assets/property-based-testing-the-ugly-parts/komposition-light.png" style="width:100.0%" alt="Komposition’s timeline mode." /> <figcaption aria-hidden="true">Komposition’s timeline mode.</figcaption> </figure> <p>Some of the most complex parts of Komposition include focus and timeline transformations, video classification, video rendering, and the main application logic. Those are the areas in which I’ve spent most effort writing tests, using a combination of example-based and property-based testing.</p> <p>I’ve selected the four most interesting areas where I’ve applied PBT in Komposition, and I’ll cover one in each coming blog post:</p> <ol type="1"> <li>Timeline flattening</li> <li>Video scene classification</li> <li>Focus and timeline consistency</li> <li>Symmetry of undo/redo</li> </ol> <p>I hope these case studies will be motivating, and that they will show the value of properties all the way from unit testing to integration testing.</p> <h2 id="property-based-testing">Property-Based Testing</h2> <p>To get the most out of this series, you need a basic understanding of what PBT is, so let’s start there. For my take on a minimal definition, PBT is about:</p> <ol type="1"> <li>Specifying your system under test in terms of properties, where properties describe invariants of the system based on its input and output.</li> <li>Testing that those properties hold against a large variety of inputs.</li> </ol> <p>It’s worth noting that PBT is not equal to QuickCheck, or any other specific tool, for that matter. The set of inputs doesn’t have to be randomly generated. You don’t have to use “shrinking”. You don’t have to use a static type system or a functional programming language. PBT is a general idea that can be applied in many ways.</p> <p>The following resources are useful if you want to learn more about PBT:</p> <ul> <li>The <a href="https://hypothesis.works/articles/intro/">introductory articles on Hypothesis</a>, although specific to Python.</li> <li><a href="https://hypothesis.works/articles/what-is-property-based-testing/">“What is Property Based Testing?”</a> by David R. MacIver is a definition of what PBT is, and particularly what it <em>isn’t</em>.</li> </ul> <p>The code examples will be written in Haskell and using the <a href="https://hackage.haskell.org/package/hedgehog">Hedgehog</a> testing system. You don’t have to know Haskell to follow this series, as I’ll explain the techniques primarily without code. But if you are interested in the Haskell specifics and in Hedgehog, check out <a href="https://teh.id.au/#/posts/2017/04/23/property-testing-with-hedgehog/">“Property testing with Hedgehog”</a> by Tim Humphries.</p> <h2 id="properties-of-the-ugly-parts">Properties of the Ugly Parts</h2> <p>When I started with PBT, I struggled with applying it to anything beyond simple functions. Examples online are often focused on the fundamentals. They cover concepts like reversing lists, algebraic laws, and symmetric encoders and decoders. Those are important properties to test, and they are good examples for teaching the foundations of PBT.</p> <p>I wanted to take PBT beyond pure and simple functions, and leverage it on larger parts of my system. The “ugly” parts, if you will. In my experience, the complexity of a system often becomes much higher than the sum of its parts. The way subsystems are connected and form a larger graph of dependencies drives the need for integration testing at an application level.</p> <p>Finding resources on integration testing using PBT is hard, and it might drive you to think that PBT is not suited for anything beyond the introductory examples. With the case studies in this blog series I hope to contribute to debunking such misconceptions.</p> <h3 id="designing-for-testability">Designing for Testability</h3> <p>In my case, it’s a desktop multimedia application. What if we’re working on a backend that connects to external systems and databases? Or if we’re writing a frontend application with a GUI driven by user input? In addition to these kinds of systems being hard to test at a high level due to their many connected subsystems, they usually have stateful components, side effects, and non-determinism. How do we make such systems testable with properties?</p> <p>Well, the same way we would design our systems to be testable with examples. Going back to <a href="https://testing.googleblog.com/2008/08/by-miko-hevery-so-you-decided-to.html">“Writing Testable Code”</a> by Miško Hevery from 2008, and Kent Beck’s <a href="https://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530">“Test-Driven Development by Example”</a> from 2003, setting aside the OOP specifics, many of their guidelines apply equally well to code tested with properties:</p> <ul> <li><strong>Determinism:</strong> Make it possible to run the “system under test” deterministically, such that your tests can be reliable. This does <em>not</em> mean the code has to be pure, but you might need to stub or otherwise control side effects during your tests.</li> <li><strong>No global state:</strong> In order for tests to be repeatable and independent of execution order, you might have to rollback database transactions, use temporary directories for generated files, stub out effects, etc.</li> <li><strong>High cohesion:</strong> Strive for modules of high cohesion, with smaller units each having a single responsibility. Spreading closely related responsibilities thin across multiple modules makes the implementation harder to maintain and test.</li> <li><strong>Low coupling:</strong> Decrease coupling between interface and implementation. This makes it easier to write tests that don’t depend on implementation details. You may then modify the implementation without modifying the corresponding tests.</li> </ul> <p>I find these guidelines universal for writing testable code in any programming language I’ve used professionally, regardless of paradigm or type system. They apply to both example-based and property-based testing.</p> <h3 id="patterns-for-properties">Patterns for Properties</h3> <p>Great, so we know how to write testable code. But how do we write properties for more complex units, and even for integration testing? There’s not a lot of educational resources on this subject that I know of, but I can recommend the following starting points:</p> <ul> <li><a href="https://fsharpforfunandprofit.com/posts/property-based-testing-2/">“Choosing properties for property-based testing”</a> by Scott Wlaschin, giving examples of properties within a set of common categories.</li> <li>The talk <a href="https://www.youtube.com/watch?v=shngiiBfD80">“Property-Based Testing for Better Code”</a> by Jessica Kerr, with examples of generating valid inputs and dealing with timeouts.</li> </ul> <p>Taking a step back, we might ask “Why it’s so hard to come up with these properties?” I’d argue that it’s because doing so forces us to understand our system in a way we’re not used to. It’s challenging understanding and expressing the general behavior of a system, rather than particular <em>anecdotes</em> that we’ve observed or come up with.</p> <p>If you want to get better at writing properties, the only advice I can give you (in addition to studying whatever you can find in other projects) is to <em>practice</em>. Try it out on whatever you’re working on. Talk to your colleagues about using properties in addition to example-based tests at work. Begin at a smaller scale, testing simple functions, and progress towards testing larger parts of your system once you’re comfortable. It’s a long journey, filled with reward, surprise, and joy!</p> <h2 id="testing-case-studies">Testing Case Studies</h2> <p>With a basic understanding of PBT, how we can write testable code, and how to write properties for our system under test, we’re getting ready to dive into the case studies:</p> <ol type="1"> <li><strong>Introduction</strong></li> <li><a href="/2019-03-24-property-based-testing-in-a-screencast-editor-case-study-1.html">Timeline Flattening</a></li> <li><a href="/2019-04-17-property-based-testing-in-a-screencast-editor-case-study-2.html">Video Scene Classification</a></li> <li><a href="/2019-06-02-property-based-testing-in-a-screencast-editor-case-study-3.html">Integration Testing</a></li> </ol> <h2 id="credits">Credits</h2> <p>Thank you Chris Ford, Alejandro Serrano Mena, Tobias Pflug, Hillel Wayne, and Ulrik Sandberg for kindly providing your feedback on my drafts!</p> <h2 id="buy-the-book">Buy the Book</h2> <p>This series is now available as an <a href="https://leanpub.com/property-based-testing-in-a-screencast-editor">ebook on Leanpub</a>. While the content is mostly the same, there are few changes bringing it up-to-date. Also, if you’ve already enjoyed the articles, you might want support my work by purchasing this book. Finally, you might enjoy a nicely typeset PDF, or an EPUB book, over a web page.</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2018-12-29-why-im-no-longer-taking-donations.html</id>
    <title>Why I'm No Longer Taking Donations</title>
    <link href="https://wickstrom.tech/2018-12-29-why-im-no-longer-taking-donations.html"/>
    <published>2018-12-29T00:00:00+01:00</published>
    <updated>2018-12-29T00:00:00+01:00</updated>
    <summary>Haskell at Work, the screencast focused on Haskell in practice, is
approaching its one year birthday. Today, I decided to stop taking
donations through Patreon due to the negative stress I’ve been
experiencing.</summary>
    <content type="html"><![CDATA[
<p><a href="https://haskell-at-work.com/">Haskell at Work</a>, the screencast focused on Haskell in practice, is approaching its one year birthday. Today, I decided to stop taking donations through Patreon due to the negative stress I’ve been experiencing.</p> <h2 id="the-beginning">The Beginning</h2> <p>This journey started in January 2018. Having a wave of inspiration after watching some of <a href="https://www.destroyallsoftware.com/screencasts">Gary Bernhardt’s new videos</a>, I decided to try making my own videos about practical Haskell programming. Not only producing high-quality content, but with high video and audio quality, was the goal. <em>Haskell at Work</em> was born, and the first video was surprisingly well-received by followers on Twitter.</p> <p>With the subsequent episodes being published in rapid succession, a follower base on YouTube grew quickly. A thousand or so followers might not be exceptional for a programming screencast channel on YouTube, but to me this was exciting and unexpected. To be honest, Haskell is not exactly a mainstream programming language.</p> <p>Early on, encouraged by some followers, and being eager to develop the concept, I decided to set up Patreon as a way of people to donate to Haskell at Work. Much like the follower count, the number of patrons and their monthly donations grew rapidly, beyond any hopes I had.</p> <h2 id="fatigue-kicks-in">Fatigue Kicks In</h2> <p>The majority of screencasts were published between January and May. Then came the summer and my month-long vacation, in which I attended ZuriHac and spent three weeks in Bali with my wife and friends. Also, I had started getting side-tracked by my project to build a <a href="https://wickstrom.tech/programming/2018/10/26/writing-a-screencast-video-editor-in-haskell.html">screencast video editor in Haskell</a>. Working on Komposition also spawned the Haskell package <a href="https://owickstrom.github.io/gi-gtk-declarative/">gi-gtk-declarative</a>, and my focus got swept away from screencasts. In all fairness, I’m not great at consistently doing <em>one</em> thing for an extended period. My creativity and energy comes in bursts, and it may not strike where and when I hope. Maybe this can be managed or controlled somehow, but I don’t know how.</p> <p>With the lower publishing pace over the summer, a vicious circle of anxiety and low productivity grew. I had thoughts about shutting down the Patreon back then, but decided to instead pause it for a few months.</p> <h2 id="regaining-energy">Regaining Energy</h2> <p>By October, I had recovered some energy. I got very good feedback and lots of encouragement from people at <a href="https://skillsmatter.com/conferences/10237-haskell-exchange-2018">Haskell eXchange</a>, and decided to throw myself back into the game. I published one screencast in November, but something was still there nagging me. I felt pressure and guilt. That I had not delivered on the promise given.</p> <p>By this time, the Patreon donations had covered my recording equipment expenses, hosting costs over the year, and a few programming books I bought. The donations were still coming in, however, at around $160 per month, with me producing no obvious value for the patrons. The guilt was still there, even stronger than before.</p> <p>I’m certain that this is all in my head. I do not blame any supporter for these feelings. You have all been great! With all the words of caution you hear about <a href="https://twitter.com/avoidcomments">not reading the comments</a>, having a YouTube channel filled with positive feedback, and almost exclusively thumbs-up ratings, I’m beyond thankful for the support I have received.</p> <h2 id="trying-something-else">Trying Something Else</h2> <p>After Christmas this year, I had planned to record and publish a new screencast. Various personal events got in the way, though, and I had very little time to spend on working with it, resulting in the same kind of stress. I took a step back and thought about it carefully, and I’ve realized that money is not a good driver for the free material and open-source code work that I do, and that it’s time for a change.</p> <p>I want to make screencasts because I love doing it, and I will do so when I have time and energy.</p> <p>From the remaining funds in my PayPal account, I have allocated enough to keep the domain name and hosting costs covered for another year, and I have donated the remaining amount (USD 450) to <a href="https://wiki.haskell.org/Donate_to_Haskell.org">Haskell.org</a>.</p> <p>Please keep giving me feedback and suggestions for future episodes. Your ideas are great! I’m looking forward to making more Haskell at Work videos in the future, and I’m toying around with ideas on how to bring in guests, and possibly trying out new formats. Stay tuned, and thank you all for your support!</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2018-10-26-writing-a-screencast-video-editor-in-haskell.html</id>
    <title>Writing a Screencast Video Editor in Haskell</title>
    <link href="https://wickstrom.tech/2018-10-26-writing-a-screencast-video-editor-in-haskell.html"/>
    <published>2018-10-26T00:00:00+02:00</published>
    <updated>2018-10-26T00:00:00+02:00</updated>
    <summary>For the last six months I’ve been working on a screencast video editor
called Komposition, and it’s now released and open source. This is an
experience report, based on a talk from Lambda World Cádiz 2018, that’ll
give an overview of Komposition’s design, implementation, testing, and
planned future work.</summary>
    <content type="html"><![CDATA[
<p>For the last six months I’ve been working on a screencast video editor called <em>Komposition</em>, and it’s now released and open source. This is an experience report, based on a talk from Lambda World Cádiz 2018, that’ll give an overview of Komposition’s design, implementation, testing, and planned future work.</p> <h2 id="background">Background</h2> <p>It all began with <a href="https://haskell-at-work.com">Haskell at Work</a>, the series of screencasts focused on practical Haskell that I’ve been producing the last year. The screencasts are fast-paced tutorials centered around the terminal and text editor, complemented by a voice-over audio track.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/evolution.png" alt="“The evolution of a functional programmer,” based on Human evolution scheme by M. Garde - Self work (Original by: José-Manuel Benitos), CC BY-SA 3.0" /> <figcaption aria-hidden="true">“The evolution of a functional programmer,” based on <a href="https://commons.wikimedia.org/w/index.php?curid=2165296">Human evolution scheme by M. Garde - Self work (Original by: José-Manuel Benitos), CC BY-SA 3.0</a></figcaption> </figure> <p>My workflow for producing screencasts looks like this:</p> <ol type="1"> <li>Write a very detailed script. The script usually gets uploaded to the website as-is, being used as the show notes for the screencast.</li> <li>Record video separately, including all mistakes and retries, in a single video file. Each little part of editing or running commands in the terminal is separated by a rest, where I don’t type anything at all for a few seconds.</li> <li>Record audio separately, where the voice-over audio is based on the script. A mistake or mispronunciation is corrected by taking a small break and then trying the same sentence or paragraph again.</li> <li>Cut and arrange all parts of the screencast using a video editor. This is the most painful and time-consuming part of the process.</li> </ol> <p>I’ve found this workflow beneficial for me, partly because of being a non-native English speaker and not being keen on coding and talking simultaneously, but also because I think it helps with organizing the content into a cohesive narrative. I can write the script almost as a text-based tutorial, read it through to make sure it flows well, and then go into recording. Having to redo the recording phase is <em>very</em> time-consuming, so I’m putting a lot of effort into the script phase to catch mistakes early.</p> <h3 id="video-editors">Video Editors</h3> <p>I’ve used a bunch of video editors. First, I tried free software alternatives, including Kdenlive, OpenShot, and a few more. Unfortunately, the audio effects available were a bit disappointing. I use, at a minimum, normalization and a noise gate. Having a good compressor is a plus.</p> <p>More importantly, these applications are built for general video editing, like film shot with a camera, and they are optimized for that purpose. I’m using a very small subset of their feature set, which is not suited to my editing workflow. It works, but the tasks are repetitive and time-consuming.</p> <p>On the commercial side, there are applications like Premiere Pro and Final Cut Pro. These are proprietary and expensive systems, but they offer very good effects and editing capabilities. I used Premiere Pro for a while and enjoyed the stability and quality of the tools, but I still suffered from the repetitive workload of cutting and organizing the video and audio clips to form my screencasts.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/yak.jpg" alt="Yak by travelwayoflife - Flickr, CC BY-SA 2.0" /> <figcaption aria-hidden="true"><a href="https://commons.wikimedia.org/w/index.php?curid=22106967">Yak by travelwayoflife - Flickr, CC BY-SA 2.0</a></figcaption> </figure> <p>What does a programmer do when faced with a repetitive task? <em>Spend way more time on automating it!</em> Thus, I started what came to be the greatest yak shave of my life.</p> <h3 id="building-a-screencast-video-editor">Building a Screencast Video Editor</h3> <p>I decided to build a screencast video editor tailored to my workflow; an editor with a minimal feature set, doing only screencast editing, and doing that <em>really well</em>.</p> <p>You might think “why not extend the free software editors to cover your needs?” That is a fair question. I wanted to rethink the editing experience, starting with a blank slate, and question the design choices made in the traditional systems. Also, to be honest, I’m not so keen on using my spare time to write C++.</p> <p>I decided to write it in GHC Haskell, using GTK+ for the graphical user interface. Another option would be Electron and PureScript, but the various horror stories about Electron memory usage, in combination with it running on NodeJS, made me decide against it. As I expected my application to perform some performance-critical tasks around video and audio processing, Haskell seemed like the best choice of the two. There are many other languages and frameworks that could’ve been used, but this combination fit me well.</p> <h2 id="komposition">Komposition</h2> <p>About five months later, after many hours of hacking, <em>Komposition</em> was <a href="https://github.com/owickstrom/komposition">released open-source under the Mozilla Public License 2.0</a>. The project working name was “FastCut,” but when making it open source I renamed it to the Swedish word for “composition.” It has nothing to do with KDE.</p> <p>Komposition is a modal, cross-platform, GUI application. The modality means that you’re always in exactly one mode, and that only certain actions can be taken depending on the mode. It currently runs on Linux, Windows, and macOS, if you compile and install it from source.</p> <p>At the heart of the editing model lies the hierarchical timeline, which we’ll dive into shortly. Other central features of Komposition include the automatic video and audio classification tools. They automate the tedious task of working through your recorded video and audio files to cut out the interesting parts. After importing, you’ll have a collection of classified video scenes, and a collection of classified audio parts. The audio parts are usually sentences, but it depends on how you take rests when recording your voice-over audio.</p> <h3 id="keyboard-driven-editing">Keyboard-Driven Editing</h3> <p>Komposition is built for keyboard-driven editing, currently with Vim-like bindings, and commands transforming the hierarchical timeline, inspired by <a href="https://www.emacswiki.org/emacs/ParEdit">Paredit for Emacs</a>.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/keybindings.png" width="345" alt="The help dialog shows the current mode’s key bindings" /> <figcaption aria-hidden="true">The help dialog shows the current mode’s key bindings</figcaption> </figure> <p>There are corresponding menu items for most commands, and there’s limited support for using the mouse in the timeline. If you need help with keybindings, press the question mark key, and you will be presented with a help dialog showing the bindings available in the current mode.</p> <h3 id="the-hierarchical-timeline">The Hierarchical Timeline</h3> <p>The hierarchical timeline is a tree structure with a fixed depth. At the leaves of the tree there are <em>clips</em>. Clips are placed in the video and audio tracks of a <em>parallel</em>. It’s named that way because the video and audio tracks play in parallel. Clips within a track play in sequence.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/timeline1.svg" alt="Video and audio tracks play in parallel" /> <figcaption aria-hidden="true">Video and audio tracks play in parallel</figcaption> </figure> <p>If the audio track is longer than the video track, the remaining part of the video track is padded with still frames from an adjacent clip.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/timeline2.svg" alt="Shorter video tracks are automatically padded with still frames" /> <figcaption aria-hidden="true">Shorter video tracks are automatically padded with still frames</figcaption> </figure> <p>Explicit <em>gaps</em> can be added to the video and audio tracks. Video gaps are padded with still frames, and audio gaps are silent. When adding the gap, you specify its duration.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/timeline3.svg" alt="Gaps can be added in video and audio tracks" /> <figcaption aria-hidden="true">Gaps can be added in video and audio tracks</figcaption> </figure> <p>Parallels are put in <em>sequences</em>, and they are played in sequence. The first parallel is played until its end, then the next is played, and so on. Parallels and sequences are used to group cohesive parts of your screencast, and to synchronize the start of related video and audio clips.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/timeline4.svg" alt="Parallels are played in sequence" /> <figcaption aria-hidden="true">Parallels are played in sequence</figcaption> </figure> <p>When editing within a sequence or parallel, for example when deleting or adding clips, you will not affect the synchronization of other sequences or parallels. If there were only audio and video tracks in Komposition, deleting an audio clip would possibly shift many other audio clips, causing them to get out of sync with their related video clips. This is why the timeline structure is built up using sequences and parallels.</p> <p>Finally, the <em>timeline</em> is the top-level structure that contains sequences. This is merely for organizing larger parts of a screencast. You can comfortably build up your screencast with a single sequence containing parallels.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/timeline5.svg" alt="The timeline contains sequences that are played in sequence" /> <figcaption aria-hidden="true">The timeline contains sequences that are played in sequence</figcaption> </figure> <p>Note that the timeline always contains at least one sequence, and that all sequences contain at least one parallel. The tracks within a parallel can be empty, though.</p> <h3 id="documentation">Documentation</h3> <p><a href="https://owickstrom.github.io/komposition/">The project website</a> includes a user guide, covering the concepts of the application, along with recommendations on how to plan and record your screencasts to achieve the best results and experience using Komposition.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/documentation.png" alt="Komposition’s user guide" /> <figcaption aria-hidden="true">Komposition’s user guide</figcaption> </figure> <p>The landing page features a tutorial screencast, explaining how to import, edit, and render a screencast. It’s already a bit outdated, but I might get around to making an updated version when doing the next release. Be sure to check it out, it’ll give you a feel for how editing with Komposition works.</p> <p>I can assure you, editing a screencast, that is about editing screencasts using your screencast editor, in your screencast editor, is <em>quite the mind-bender</em>. And I thought I had recursion down.</p> <h2 id="implementation">Implementation</h2> <p>I’ve striven to keep the core domain code in Komposition pure. That is, only pure function and data structures. Currently, the timeline and focus, command and event handling, key bindings, and the video classification algorithm are all pure. There are still impure parts, like audio and video import, audio classification, preview frame rendering, and the main application control flow.</p> <p>Some parts are inherently effectful, so it doesn’t make sense to try writing them as pure functions, but as soon as the complexity increases, it’s worth considering what can be separated out as pure functions. The approach of <a href="https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell/">“Functional core, imperative shell”</a> describes this style very well. If you can do this for the complex parts of your program, you have a great starting point for automated testing, something I’ll cover later in this post.</p> <h3 id="gtk">GTK+</h3> <p>Komposition’s GUI is built with GTK+ 3 and the Haskell bindings from <code>gi-gtk</code>. I noticed early in the project that the conventional programming style and the APIs of GTK+ were imperative, callback-oriented, and all operating within <code>IO</code>, making it painful to use from Haskell, especially while trying to keep complex parts of the application free of effects.</p> <p>To mitigate this issue, I started building a library (more yak shaving!) called <code>gi-gtk-declarative</code>, which is a declarative layer on top of <code>gi-gtk</code>. The <a href="/programming/2018/09/04/declarative-gtk-programming-with-haskell.html">previous post in this blog</a> describes the project in detail. Using the declarative layer, rendering becomes a pure function <code>(state -&gt; Widget event)</code>, where <code>state</code> and <code>event</code> varies with the mode and view that’s being rendered. Event handling is based on values and pure functions.</p> <p>There are cases where custom widgets are needed, calling the imperative APIs of <code>gi-gtk</code>, but they are well-isolated and few in numbers.</p> <h3 id="type-indexed-state-machines">Type-Indexed State Machines</h3> <p>I had a curiosity itching when starting this project that I decided to scratch. Last year I worked on porting <a href="http://docs.idris-lang.org/en/latest/st/">the Idris ST library</a>, providing a way to encode type-indexed state machines in GHC Haskell. The library is called <a href="http://hackage.haskell.org/package/motor">Motor</a>. I wanted to try it in the context of a GUI application.</p> <p>Just to give some short examples, the following type signatures, used in the main application control flow, operate on the application state machine that’s parameterized by its mode.</p> <p>The <code>start</code> function takes a name and key maps, and creates a new application state machine associated with the name, and in the state of <code>WelcomeScreenMode</code>:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>start</span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Name</span> n</span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">KeyMaps</span></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Actions</span> m &#39;[ n <span class="op">!+</span> <span class="dt">State</span> m <span class="dt">WelcomeScreenMode</span>] r ()</span></code></pre></div> <p>The <code>returnToTimeline</code> function takes a name of an existing state machine and a <code>TimelineModel</code>, and transitions the application from the current mode to <code>TimelineMode</code>, given that the current mode instantiates the <code>ReturnsToTimeline</code> class:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>returnToTimeline</span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">ReturnsToTimeline</span> mode</span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">Name</span> n</span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">TimelineModel</span></span> <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Actions</span> m &#39;[ n <span class="op">:=</span> <span class="dt">State</span> m mode <span class="op">!--&gt;</span> <span class="dt">State</span> m <span class="dt">TimelineMode</span>] r ()</span></code></pre></div> <p>The usage of Motor in Komposition is likely the most complicated aspect of the codebase, and I have been unsure if it is worth the cost. On the positive side, combining this with GADTs and the singleton pattern for mode-specific commands and events, GHC can really help out with pattern-matching and exhaustivity-checking. No nasty <code>(error "this shouldn't happen")</code> as the fall-through case when pattern matching!</p> <p>I’m currently in the process of rewriting much of the state machine encoding in Komposition, using it more effectively for managing windows and modals in GTK+, and I think this warrants the use of Motor more clearly. Otherwise, it might be worth falling back to a less advanced encoding, like the one I described in <a href="/2017-11-19-finite-state-machines-part-2.html">Finite-State Machines, Part 2: Explicit Typed State Transitions</a>.</p> <h3 id="singleton-pattern">Singleton Pattern</h3> <p>The <em>singleton pattern</em> is used in a few places in Komposition, as mentioned above. To show a concrete example, the encoding of mode-specific commands and events is based on the <code>Mode</code> data type.</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Mode</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">WelcomeScreenMode</span></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">TimelineMode</span></span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">LibraryMode</span></span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">ImportMode</span></span></code></pre></div> <p>This type is lifted to the kind level using the <code>DataKinds</code> language extension, and is used in the corresponding definition of the singleton <code>SMode</code>.</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">SMode</span> m <span class="kw">where</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">SWelcomeScreenMode</span><span class="ot"> ::</span> <span class="dt">SMode</span> <span class="dt">WelcomeScreenMode</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">STimelineMode</span><span class="ot"> ::</span> <span class="dt">SMode</span> <span class="dt">TimelineMode</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">SLibraryMode</span><span class="ot"> ::</span> <span class="dt">SMode</span> <span class="dt">LibraryMode</span></span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">SImportMode</span><span class="ot"> ::</span> <span class="dt">SMode</span> <span class="dt">ImportMode</span></span></code></pre></div> <p>The <code>Command</code> data type is parameterized by the mode in which the command is valid. Some commands are valid in all modes, like <code>Cancel</code> and <code>Help</code>, while others are specific to a certain mode. <code>FocusCommand</code> and <code>JumpFocus</code> are only valid in the timeline mode, as seen in their type signatures below.</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Command</span> (<span class="ot">mode ::</span> <span class="dt">Mode</span>) <span class="kw">where</span></span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">Cancel</span><span class="ot"> ::</span> <span class="dt">Command</span> mode</span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">Help</span><span class="ot"> ::</span> <span class="dt">Command</span> mode</span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">FocusCommand</span><span class="ot"> ::</span> <span class="dt">FocusCommand</span> <span class="ot">-&gt;</span> <span class="dt">Command</span> <span class="dt">TimelineMode</span></span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">JumpFocus</span><span class="ot"> ::</span> <span class="dt">Focus</span> <span class="dt">SequenceFocusType</span> <span class="ot">-&gt;</span> <span class="dt">Command</span> <span class="dt">TimelineMode</span></span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> <span class="co">-- ...</span></span></code></pre></div> <p>Finally, by passing a singleton for a mode to the <code>keymaps</code> function, we get back a keymap specific to that mode. This is used to do event handling and key bindings generically for the entire application.</p> <div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ot">keymaps ::</span> <span class="dt">SMode</span> m <span class="ot">-&gt;</span> <span class="dt">KeyMap</span> (<span class="dt">Command</span> m)</span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>keymaps <span class="ot">=</span></span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> \<span class="kw">case</span></span> <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">SWelcomeScreenMode</span> <span class="ot">-&gt;</span></span> <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> [ ([<span class="dt">KeyChar</span> <span class="ch">&#39;q&#39;</span>], <span class="dt">Mapping</span> <span class="dt">Cancel</span>)</span> <span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> , ([<span class="dt">KeyEscape</span>], <span class="dt">Mapping</span> <span class="dt">Cancel</span>)</span> <span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> , ([<span class="dt">KeyChar</span> <span class="ch">&#39;?&#39;</span>], <span class="dt">Mapping</span> <span class="dt">Help</span>)</span> <span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> ]</span> <span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="co">-- ...</span></span></code></pre></div> <p>In the spirit of calling out usage of advanced GHC features, I think singletons and GADTs are one more such instance. However, I find them very useful in this context, and worth the added cognitive load. You don’t have to go full “Dependent Haskell” or bring in the <a href="http://hackage.haskell.org/package/singletons">singletons</a> library to leverage some of these techniques.</p> <h3 id="automatic-scene-classification">Automatic Scene Classification</h3> <p>The automatic classification of scenes in video is implemented using the <a href="http://hackage.haskell.org/package/pipes">Pipes</a> and <a href="http://hackage.haskell.org/package/ffmpeg-light">ffmpeg-light</a> libraries, mainly. It begins with the <code>readVideoFile</code>, that given a video file path will give us a <code>Pipes.Producer</code> of timed frames, which are basically pixel arrays tagged with their time in the original video. The producer will stream the video file, and yield timed frames as they are consumed.</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ot">readVideoFile ::</span> <span class="dt">MonadIO</span> m <span class="ot">=&gt;</span> <span class="dt">FilePath</span> <span class="ot">-&gt;</span> <span class="dt">Producer</span> (<span class="dt">Timed</span> <span class="dt">Frame</span>) m ()</span></code></pre></div> <p>The frames are converted from <a href="http://hackage.haskell.org/package/JuicyPixels">JuicyPixels</a> frames to <a href="http://hackage.haskell.org/package/massiv">massiv</a> frames. Then, the producer is passed to the <code>classifyMovement</code> function together with a minimum segment duration (such that segments cannot be shorter than <em>N</em> seconds), which returns a producer of <code>Classified</code> frames, tagging each frame as being either moving or still.</p> <div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>classifyMovement</span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Monad</span> m</span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">Time</span> <span class="co">-- ^ Minimum segment duration</span></span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Producer</span> (<span class="dt">Timed</span> <span class="dt">RGB8Frame</span>) m ()</span> <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Producer</span> (<span class="dt">Classified</span> (<span class="dt">Timed</span> <span class="dt">RGB8Frame</span>)) m ()</span> <span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Classified</span> f</span> <span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">Moving</span> f</span> <span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Still</span> f</span> <span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Functor</span>, <span class="dt">Show</span>)</span></code></pre></div> <p>Finally, the <code>classifyMovingScenes</code> function, given a full duration of the original video and a producer of classified frames, returns a producer that yields <code>ProgressUpdate</code> values and returns a list of time spans.</p> <div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ot">classifyMovingScenes ::</span></span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">Monad</span> m</span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">Duration</span> <span class="co">-- ^ Full length of video</span></span> <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Producer</span> (<span class="dt">Classified</span> (<span class="dt">Timed</span> <span class="dt">RGB8Frame</span>)) m ()</span> <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Producer</span> <span class="dt">ProgressUpdate</span> m [<span class="dt">TimeSpan</span>]</span></code></pre></div> <p>The time spans describe which parts of the original video are considered moving scenes, and the progress update values are used to render a progress bar in the GUI as the classification makes progress.</p> <h3 id="automatic-sentence-classification">Automatic Sentence Classification</h3> <p>Similar to the video classification, Komposition also classifies audio files to find sentences or paragraphs in voice-over audio. The implementation relies on the <code>sox</code> tool, a separate executable that’s used to:</p> <ol type="1"> <li>normalize the audio,</li> <li>apply a noise gate, and</li> <li>auto-split by silence.</li> </ol> <p>One problem with <code>sox</code> is that it, as far as I can tell, can only write the split audio files to disk. I haven’t found a way to retrieve the time spans in the original audio file, so that information is unfortunately lost. This will become more apparent when Komposition supports editing the start and end position of clips, as it can’t be supported for audio clips produced by <code>sox</code>.</p> <p>I hope to find some way around this, by extending or parsing output from <code>sox</code> somehow, by using <code>libsox</code> through FFI bindings, or by implementing the audio classification in Haskell. I’m trying to avoid the last alternative.</p> <h3 id="rendering">Rendering</h3> <p>The rendering pipeline begins with a pure function that converts the hierarchical timeline to a flat timeline representation. This representation consists of a video track and an audio track, where all gaps are made explicit, and where the tracks are of equal duration.</p> <p>From the flat representation, an FFmpeg command is built up. This is based on a data type representation of the FFmpeg command-line syntax, and most importantly the filter graph DSL that’s used to build up the complex FFmpeg rendering command.</p> <p>Having an intermediate data type representation when building up complex command invocations, instead of going directly to <code>Text</code> or <code>String</code>, is something I highly recommend.</p> <h3 id="preview">Preview</h3> <p>The preview pipeline is very similar to the rendering pipeline. In fact, it’s using the same machinery, except for the output being a streaming HTTP server instead of a file, and that it’s passed the proxy media instead of the full-resolution original video. On the other side there’s a GStreamer widget embedded in the GTK+ user interface that plays back the HTTP video stream.</p> <p>Using HTTP might seem like a strange choice for IPC between FFmpeg and GStreamer. Surprisingly, it’s the option that have worked most reliably across operating systems for me, but I’d like to find another IPC mechanism, eventually.</p> <p>The HTTP solution is also somewhat unreliable, as I couldn’t find a way to ensure that the server is ready to stream, so there’s a race condition between the server and the GStreamer playback, silently “solved” with an ugly <code>threadDelay</code>.</p> <h2 id="testing">Testing</h2> <p>Let’s talk a bit about testing in Komposition. I’ve used multiple techniques, but the one that stands out as unconventional, and specific to the domain of this application, is the color-tinting video classifier.</p> <figure> <img src="/assets/writing-a-screencast-video-editor-in-haskell/color-tinting.gif" alt="Output of the color-tinting video classifier" /> <figcaption aria-hidden="true">Output of the color-tinting video classifier</figcaption> </figure> <p>It uses the same classification functions as described before, but instead of returning time spans, it creates a new video file where moving frames are tinted green and still frames are tinted red. This tool made it much easier to tweak the classifier and test it on real recordings.</p> <h3 id="property-based-testing">Property-Based Testing</h3> <p>I’ve used Hedgehog to test some of the most complex parts of the codebase. This has been incredibly valuable, and has found numerous errors and bad assumptions in the implementation. The functionality tested with Hedgehog and properties includes:</p> <ul> <li><strong>Timeline commands and movement:</strong> It generates a sequence of commands, together with a consistent timeline and focus. It folds over the commands, applying each one to the current timeline and focus, and asserts that the resulting timeline and focus are still consistent. The tested property ensures that there’s no possibility of out-of-bounds movement, and that deleting or otherwise transforming the timeline doesn’t cause an inconsistent timeline and focus pair.</li> <li><strong>Video scene classification:</strong> It generates known test scenes of random duration, that are either scenes of only still frames, or scenes with moving frames. It translates the test scenes, which are just descriptions, to real frames, and runs the classifier on the frames. Finally, it checks that the classified scenes are the same as the generated test scenes.</li> <li><strong>Flattening of hierarchical timeline:</strong> The flattening process converts the hierarchical timeline to a flat representation. The tested property ensures that hierarchical and flat timelines are always of the same total duration. There are other properties that could be added, e.g. that all clips in the original timeline are present in the flat timeline.</li> <li><strong>Round-trip properties of FFmpeg format printers and parsers:</strong> This is a conventional use of property-based tests. It ensures that parsing an FFmpeg-format timestamp string, produced by the FFmpeg-format timestamp printer, gives you back the same timestamp as you started with.</li> </ul> <p>There are also cases of example-based testing, but I won’t cover them in this report.</p> <h2 id="used-packages">Used Packages</h2> <p>Komposition depends on a fairly large collection of Haskell and non-Haskell tools and libraries to work with video, audio, and GUI rendering. I want to highlight some of them.</p> <h3 id="haskell-gi">haskell-gi</h3> <p>The <a href="https://github.com/haskell-gi/haskell-gi">haskell-gi</a> family of packages are used extensively, including:</p> <ul> <li>gi-gobject</li> <li>gi-glib</li> <li>gi-gst</li> <li>gi-gtk</li> <li>gi-gdk</li> <li>gi-gdkpixbuf</li> <li>gi-pango</li> </ul> <p>They supply bindings to GTK+, GStreamer, and more, primarily generated from the GObject Introspection metadata. While GTK+ has been problematic to work with in Haskell, these bindings have been crucial to the development of Komposition.</p> <h2 id="massiv-massiv-io">massiv &amp; massiv-io</h2> <p>The <a href="http://hackage.haskell.org/package/massiv">massiv</a> package is an array library that uses function composition to accomplish a sort of fusion. It’s used to do parallel pixel comparison in the video classifier. Thank you, <a href="https://github.com/lehins">Alexey Kuleshevich</a> (author and maintainer of the massiv packages) for helping me implement the first version!</p> <h2 id="pipes">Pipes</h2> <p>The <a href="http://hackage.haskell.org/package/pipes">Pipes</a> library is used extensively in Komposition:</p> <ul> <li>The streaming video reader from <a href="http://hackage.haskell.org/package/ffmpeg-light">ffmpeg-light</a> is wrapped in a <code>Pipes.Producer</code> to provide composable streaming.</li> <li>In general, effectful operations with progress notifications are producers that yield <code>ProgressUpdate</code> values as they perform their work.</li> <li><a href="http://hackage.haskell.org/package/pipes-safe">pipes-safe</a> is used for handling resources and processes.</li> <li><a href="http://hackage.haskell.org/package/pipes-parse">pipes-parse</a> is used in stateful transformations in the video classifier.</li> </ul> <p>A big thanks to <a href="https://twitter.com/GabrielG439">Gabriel Gonzales</a>, the author of Pipes and the related packages!</p> <h2 id="others">Others</h2> <p>To name a few more:</p> <ul> <li>I’ve used <a href="http://hackage.haskell.org/package/protolude">protolude</a> as the basis for a custom prelude.</li> <li>The <a href="http://hackage.haskell.org/package/lens">lens</a> library is used for working with nested data structures, positional updates in lists, and monadic transformations.</li> <li><a href="http://hackage.haskell.org/package/typed-process">typed-process</a> is used together with pipes-safe, in a situation where I couldn’t use the regular <a href="http://hackage.haskell.org/package/process">process</a> package because of version constraint issues. The typed-process API turned out to be really nice, so I think it will be used more in the future.</li> </ul> <h2 id="summary">Summary</h2> <p>Looking back at this project, the best part has been to first write it for my own use, and later find out that quite a lot of people are interested in how it’s built, and even in using it themselves. I’ve already received pull requests, bug reports, usability feedback, and many kind words and encouragements. It’s been great!</p> <p>Also, it’s been fun to work on an application that can be considered outside of Haskell’s comfort zone, namely a multimedia and GUI application. Komposition is not the first application to explore this space — see <a href="https://lettier.github.io/movie-monad/">Movie Monad</a> and <a href="https://lettier.github.io/gifcurry/">Gifcurry</a> for other examples — but it is exciting, nonetheless.</p> <p>Speaking of using Haskell, the effort to keep complex domain logic free of effects, and the use of property-based testing with Hedgehog to lure out nasty bugs, has been incredibly satisfactory and a great learning experience.</p> <h3 id="the-problematic-parts">The Problematic Parts</h3> <p>It’s not been all fun and games, though. I’ve spent many hours struggling with FFmpeg, video and audio codecs, containers, and streaming. Executing external programs and parsing their output has been time-consuming and very hard to test. GTK+ has been very valuable, but also difficult to work with in Haskell. Finally, management of non-Haskell dependencies, in combination with trying to be cross-platform, is painful. Nix has helped with my own setup, but everyone will not install Komposition using Nix.</p> <h3 id="next-steps">Next Steps</h3> <p>There are many features that I’d like to add in the near future.</p> <ul> <li>More commands to work with the timeline, e.g. yank, paste, and join.</li> <li>More Vim-like movement commands.</li> <li>Previewing of any timeline part. Currently you can only preview the entire timeline, a sequence, or a parallel.</li> <li>Adjustable clips, meaning that you can change the time span of a clip. This is useful if the automatic classification generated slightly incorrect clip time spans.</li> <li>Content-addressed project files, to enable reuse of generated files, and to avoid collision. This includes most files involved in importing, and generated preview frames.</li> </ul> <p>It would be great to set up packaging for popular operating systems, so that people can install Komposition without compiling from source. There’s already a Nix expression that can be used, but I’d like to supply Debian packages, macOS bundles, Windows installers, and possibly more.</p> <p>There are some things that I’d like to explore and assess, but that won’t necessarily happen. The first is to use GStreamer in the rendering pipeline, instead of FFmpeg. I think this is possible, but I haven’t done the research yet. The second thing, an idea that evolved when talking to people at Lambda World Cádiz, would be to use voice recognition on audio clips to show text in the preview area, instead of showing a waveform.</p> <p>Finally, there are some long-awaited refactorings and cleanups waiting, and optimization of the FFmpeg filter graph and the diffing in gi-gtk-declarative. Some of these I’ve already started on.</p> <h2 id="wrap-up">Wrap-Up</h2> <p>I hope you enjoyed reading this report, and that you now have got a clearer picture of <a href="https://owickstrom.github.io/komposition/">Komposition</a>, its implementation, and where it’s going. If you’re interested in using it, let me know how it works out, either by posting in the <a href="https://gitter.im/owickstrom/komposition">Gitter channel</a> or by reaching out <a href="https://twitter.com/owickstrom">on Twitter</a>. If you want to contribute by reporting bugs or sending pull requests, there’s <a href="https://github.com/owickstrom/komposition/issues">the issue tracker on GitHub</a>.</p> <p>Thanks for reading!</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2018-09-04-declarative-gtk-programming-with-haskell.html</id>
    <title>Declarative GTK+ Programming with Haskell</title>
    <link href="https://wickstrom.tech/2018-09-04-declarative-gtk-programming-with-haskell.html"/>
    <published>2018-09-04T00:00:00+02:00</published>
    <updated>2018-09-04T00:00:00+02:00</updated>
    <summary>This post introduces a declarative GTK+ architecture for Haskell which
I’ve been working on during the journey with FastCut, a video editor
specialized for my own screencast editing workflow. It outlines the
motivation, introduces the packages and their uses, and highlights parts
of the implementation.</summary>
    <content type="html"><![CDATA[
<p>This post introduces a declarative GTK+ architecture for Haskell which I’ve been working on during the journey with FastCut, a video editor specialized for my own screencast editing workflow. It outlines the motivation, introduces the packages and their uses, and highlights parts of the implementation.</p> <h2 id="imperative-gui-programming">Imperative GUI Programming</h2> <p>When starting to work on <em>FastCut</em>, I wanted to use a GUI framework that would allow FastCut to be portable across Linux, macOS, and Windows. Furthermore, I was looking for a native GUI framework, as opposed to something based on web technology. GTK+ stood out as an established framework, with good bindings for Haskell in the <a href="https://github.com/haskell-gi/haskell-gi">haskell-gi</a> package.</p> <p>It didn’t take me very long before the imperative APIs of GTK+ became problematic. Widgets are created, properties are set, callbacks are attached, and methods are called, all in <code>IO</code>. With the imperative style, I see there being two distinct phases of a widget’s life cycle, which need to be handled separately in your rendering code:</p> <ol type="1"> <li><em>construction</em>, where you instantiate the widget with its initial state, and</li> <li><em>subsequent updates</em>, where some relevant state changes and the widget’s state is modified to reflect that. Often, the application state and the widget state are the same.</li> </ol> <p>This programming style makes testing and locally reasoning about your code really hard, as it forces you to scatter your application logic and state over <code>IORef</code>s, <code>MVar</code>s, and various callbacks, mutating shared state in <code>IO</code>. These symptoms are not specific to GTK+ or its Haskell bindings, but are common in object-oriented and imperative GUI frameworks. If you’re familiar with JavaScript and jQuery UI programming, you have most likely experienced similar problems.</p> <p>While GTK+ has the <a href="https://glade.gnome.org/">Glade</a> “WYSIWYG” editor, and the <code>Builder</code> class, they only make the first phase declarative. You still need to find all widgets in the instantiated GUI, attach event handlers, and start mutating state, to handle the second phase.</p> <p>After having experienced these pains and getting repeatedly stuck when building FastCut with the imperative GTK+ APIs, I started exploring what a declarative GTK+ programming model could look like.</p> <h2 id="going-declarative">Going Declarative</h2> <p>The package I’ve been working on is called <em>gi-gtk-declarative</em>, and it aims to be a minimal declarative layer on top of the GTK+ bindings in <a href="https://github.com/haskell-gi/haskell-gi">haskell-gi</a>.</p> <p>Rendering becomes a pure function from state to a <em>declarative widget</em>, which is a data structure <em>representation</em> of the user interface. The library uses a patching mechanism to calculate the updates needed to be performed on the actual GTK+ widgets, based on differences in the declarative widgets, similar to a <a href="https://reactjs.org/docs/faq-internals.html">virtual DOM</a> implementation.</p> <p>Event handling is declarative, i.e. you declare which events are emitted on particular signals, rather than mutating state in callbacks or using concurrency primitives to communicate changes. Events of widgets can be mapped to other data types using the <code>Functor</code> instance, making widgets reusable and composable.</p> <p>Finally, gi-gtk-declarative tries to be agnostic of the application architecture it’s used in. It should be possible to reuse it for different styles. As we shall see later there is a state reducer-based architecture available in <em>gi-gtk-declarative-app-simple</em>, and FastCut is using a custom architecture based on indexed monads and the <a href="http://hackage.haskell.org/package/motor">Motor</a> library.</p> <h2 id="declarative-widgets">Declarative Widgets</h2> <p>By reusing type-level information provided by the <code>haskell-gi</code> framework, and by using the <code>OverloadedLabels</code> language extension in GHC, gi-gtk-declarative can support many widgets automatically. Even though some widgets need special support, specifically containers, it is a massive benefit not having to redefine all GTK+ widgets.</p> <h3 id="single-widgets">Single Widgets</h3> <p>A single widget, i.e. one that cannot contain children, is constructed using the <code>widget</code> smart constructor, taking a GTK+ widget constructor and an attribute list:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>myButton <span class="ot">=</span> widget <span class="dt">Button</span> []</span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>myCheckButton <span class="ot">=</span> widget <span class="dt">CheckButton</span> []</span></code></pre></div> <p>Note that <a href="http://hackage.haskell.org/package/gi-gtk-3.0.24/docs/GI-Gtk-Objects-Button.html#t:Button">Button</a> and <a href="http://hackage.haskell.org/package/gi-gtk-3.0.24/docs/GI-Gtk-Objects-CheckButton.html#t:CheckButton">CheckButton</a> are constructors from gi-gtk. They are not defined by gi-gtk-declarative.</p> <h3 id="bins">Bins</h3> <p><em>Bins</em>, in GTK+ lingo, are widgets that contain a single child. They are created using the <code>bin</code> smart constructor:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>myScrollArea <span class="ot">=</span></span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> bin <span class="dt">ScrolledWindow</span> [] <span class="op">$</span></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> widget <span class="dt">Button</span> []</span></code></pre></div> <p>Other examples of bins are <a href="http://hackage.haskell.org/package/gi-gtk-3.0.24/docs/GI-Gtk-Objects-Expander.html#t:Expander">Expander</a>, <a href="http://hackage.haskell.org/package/gi-gtk-3.0.24/docs/GI-Gtk-Objects-Viewport.html#t:Viewport">Viewport</a>, and <a href="http://hackage.haskell.org/package/gi-gtk-3.0.24/docs/GI-Gtk-Objects-SearchBar.html#t:SearchBar">SearchBar</a>.</p> <h3 id="containers">Containers</h3> <p>Containers are widgets that can contain zero or more child widgets, and they are created using the <code>container</code> smart constructor:</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>myListBox <span class="ot">=</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> container <span class="dt">ListBox</span> [] <span class="op">$</span> <span class="kw">do</span></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> bin <span class="dt">ListBoxRow</span> [] <span class="op">$</span> widget <span class="dt">Button</span> []</span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> bin <span class="dt">ListBoxRow</span> [] <span class="op">$</span> widget <span class="dt">CheckButton</span> []</span></code></pre></div> <p>In regular GTK+, containers often accept any type of widget to be added to the container, and if the container requires its children to be of a specific type, it will automatically insert the in-between widget implicitly. An example of such a container is <code>ListBox</code>, which automatically wraps added children in <code>ListBoxRow</code> bins, if needed.</p> <p>In gi-gtk-declarative, on the other hand, containers restrict the type of their children to make these relationships explicit. Thus, as seen above, to embed child widgets in a <code>ListBox</code> they have to be wrapped in <code>ListBoxRow</code> bins.</p> <p>Another example, although slightly different, is <code>Box</code>. While <code>Box</code> doesn’t have a specific child widget type, you can in regular GTK+ add children using the <code>boxPackStart</code> and <code>boxPackEnd</code> methods. The arguments to those methods are <em>expand</em>, <em>fill</em>, and <em>padding</em>, which control how the child is rendered (<em>packed</em>) in the box. As gi-gtk-declarative doesn’t support method calls, there is a helper function and corresponding declarative widget <code>boxChild</code> to control <code>Box</code> child rendering:</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>myBox <span class="ot">=</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> container <span class="dt">Box</span> [] <span class="op">$</span> <span class="kw">do</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> boxChild <span class="dt">False</span> <span class="dt">False</span> <span class="dv">0</span> <span class="op">$</span> widget <span class="dt">Button</span> []</span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> boxChild <span class="dt">True</span> <span class="dt">True</span> <span class="dv">0</span> <span class="op">$</span> widget <span class="dt">CheckButton</span> []</span></code></pre></div> <p>Note that we are using <code>do</code> notation to construct adjacent <code>boxChild</code> markup values. There is a monadic <code>MarkupOf</code> builder in the library that the <code>container</code> smart constructor takes as its last argument. Although we need the <code>Monad</code> instance to be able to use do notation, the return value of such expressions are rarely useful, and is thus constrained to <code>()</code> by the library.</p> <h2 id="attributes">Attributes</h2> <p>All declarative widgets can have attributes, and so far we’ve only seen empty attribute lists. Attributes on declarative widgets are not the same as GTK+ <em>properties</em>. They do include GTK+ properties, but also include CSS classes declarations and event handling.</p> <h3 id="properties">Properties</h3> <p>One type of attribute is a <em>property</em> declaration. To declare a property, use the <code>(:=)</code> operator, which takes a property name label, and a property value, much like <code>(:=)</code> in <a href="https://github.com/haskell-gi/haskell-gi">haskell-gi</a>:</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>myButtonWithLabel <span class="ot">=</span></span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> widget <span class="dt">Button</span> [<span class="op">#</span>label <span class="op">:=</span> <span class="st">&quot;Click Here&quot;</span>]</span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>myHorizontallyScrolledWindow <span class="ot">=</span></span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> bin <span class="dt">ScrolledWindow</span> [ <span class="op">#</span>hscrollbarPolicy <span class="op">:=</span> <span class="dt">PolicyTypeAutomatic</span> ] <span class="op">$</span></span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> someSuperWideWidget</span> <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>myContainerWithMultipleSelection <span class="ot">=</span></span> <span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> container <span class="dt">ListBox</span> [ <span class="op">#</span>selectionMode <span class="op">:=</span> <span class="dt">SelectionModeMultiple</span> ] <span class="op">$</span></span> <span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a> children</span></code></pre></div> <p>To find out what properties are available, see the <a href="https://hackage.haskell.org/package/gi-gtk-3.0.24/docs/GI-Gtk-Objects.html">GI.Gtk.Objects</a> module, find the widget module you’re interested in, and see the “Properties” section. As an example, you’d find the properties available for <code>Button</code> <a href="https://hackage.haskell.org/package/gi-gtk-3.0.24/docs/GI-Gtk-Objects-Button.html#g:32">here</a>.</p> <h3 id="events">Events</h3> <p>Using the <code>on</code> attribute, you can emit events on GTK+ signal emissions.</p> <div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>counterButton clickCount <span class="ot">=</span></span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> msg <span class="ot">=</span> <span class="st">&quot;I&#39;ve been clicked &quot;</span></span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;&gt;</span> Text.pack (<span class="fu">show</span> clickCount)</span> <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;&gt;</span> <span class="st">&quot; times.&quot;</span></span> <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span> widget</span> <span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">Button</span></span> <span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> [ <span class="op">#</span>label <span class="op">:=</span> msg</span> <span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> , on <span class="op">#</span>clicked <span class="dt">ButtonClicked</span></span> <span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div> <p>Some events need to be constructed with <code>IO</code> actions, to be able to query underlying GTK+ widgets for attributes. The <code>onM</code> attribute receives the widget as its first argument, and returns an <code>IO event</code> action. In the following example <code>getColorButtonRgba</code> has type <code>ColorButton -&gt; IO (Maybe RGBA)</code>, and so we compose it with an <code>fmap</code> of the <code>ColorChanged</code> constructor to get an <code>IO Event</code>.</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Event</span> <span class="ot">=</span> <span class="dt">ColorChanged</span> (<span class="dt">Maybe</span> <span class="dt">RGBA</span>)</span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>colorButton color <span class="ot">=</span></span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> widget</span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">ColorButton</span></span> <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> [ <span class="op">#</span>title <span class="op">:=</span> <span class="st">&quot;Selected color&quot;</span></span> <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> , <span class="op">#</span>rgba <span class="op">:=</span> color</span> <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> , onM <span class="op">#</span>colorSet (<span class="fu">fmap</span> <span class="dt">ColorChanged</span> <span class="op">.</span> getColorButtonRgba)</span> <span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div> <p>You can think of <code>onM</code> having the following signature, even if it’s really a simplified version:</p> <div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>onM</span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Gtk.SignalProxy</span> widget</span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> (widget <span class="ot">-&gt;</span> <span class="dt">IO</span> event)</span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Attribute</span> widget event</span></code></pre></div> <p>Finally, CSS classes can be declared for widgets in the attributes list, using the <code>classes</code> attribute:</p> <div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>myAnnoyingButton <span class="ot">=</span></span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> widget</span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">Button</span></span> <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> [ classes [<span class="st">&quot;big-button&quot;</span>]</span> <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> , <span class="op">#</span>label <span class="op">:=</span> <span class="st">&quot;CLICK ME&quot;</span></span> <span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div> <h2 id="gi.gtk.declarative.app.simple">GI.Gtk.Declarative.App.Simple</h2> <p>In addition to the declarative widget library gi-gtk-declarative, there’s an application architecture for you to use, based on the state reducer design of <a href="http://purescript-pux.org/">Pux</a>.</p> <p>At the heart of this architecture is the <code>App</code>:</p> <div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">App</span> state event <span class="ot">=</span></span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">App</span></span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> {<span class="ot"> update ::</span> state <span class="ot">-&gt;</span> event <span class="ot">-&gt;</span> <span class="dt">Transition</span> state event</span> <span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> view ::</span> state <span class="ot">-&gt;</span> <span class="dt">Widget</span> event</span> <span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> inputs ::</span> [<span class="dt">Producer</span> event <span class="dt">IO</span> ()]</span> <span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> initialState ::</span> state</span> <span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> }</span></code></pre></div> <p>The type parameters <code>state</code> and <code>event</code> will be instantiated with our specific types used in our application. For example, if we were writing a “Snake” clone, our state datatype would describe the current playing field, the snake length and where it’s been, the edible objects’ positions, etc. The event datatype would likely include key press events, such as “arrow down” and “arrow right”.</p> <p>The <code>App</code> datatype consists of:</p> <ul> <li>an <code>update</code> function, that reduces the current state and a new event to a <code>Transition</code>, which decides the next state to transition to,</li> <li>a <code>view</code> function, that renders a state value as a <code>Widget</code>, parameterized by the <code>App</code>s event type,</li> <li>inputs, which are <a href="http://hackage.haskell.org/package/pipes-4.3.9/docs/Pipes-Core.html#t:Producer">Producer</a>s that feed events into the application, and</li> <li>the initial state value of the state reduction loop.</li> </ul> <h3 id="running-applications">Running Applications</h3> <p>To run an <code>App</code>, you can use the convenient <code>run</code> function, that initializes GTK+ and sets up a window for you:</p> <div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>run</span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Typeable</span> event</span> <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">Text</span> <span class="dt">Window</span> title</span> <span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> (<span class="dt">Int32</span>, <span class="dt">Int32</span>) <span class="co">-- ^ Optional window size</span></span> <span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">App</span> state event <span class="co">-- ^ Application to run</span></span> <span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span></code></pre></div> <p>There’s also <code>runInWindow</code> if you like to initialize GTK+ yourself, and set up your own window; something you need to do if you want to use CSS, for instance.</p> <h2 id="declarative-hello-world">Declarative “Hello, world!”</h2> <p>The <a href="https://github.com/haskell-gi/haskell-gi">haskell-gi</a> README includes an “Hello, world!” example, written in an imperative style:</p> <div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span> <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a> Gtk.init <span class="dt">Nothing</span></span> <span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a> win <span class="ot">&lt;-</span> new <span class="dt">Gtk.Window</span> [ <span class="op">#</span>title <span class="op">:=</span> <span class="st">&quot;Hi there&quot;</span> ]</span> <span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a> on win <span class="op">#</span>destroy Gtk.mainQuit</span> <span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a> button <span class="ot">&lt;-</span> new <span class="dt">Gtk.Button</span> [ <span class="op">#</span>label <span class="op">:=</span> <span class="st">&quot;Click me&quot;</span> ]</span> <span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a></span> <span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a> on button <span class="op">#</span>clicked <span class="op">$</span></span> <span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a> set</span> <span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a> button</span> <span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a> [ <span class="op">#</span>sensitive <span class="op">:=</span> <span class="dt">False</span></span> <span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a> , <span class="op">#</span>label <span class="op">:=</span> <span class="st">&quot;Thanks for clicking me&quot;</span></span> <span id="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a> ]</span> <span id="cb12-17"><a href="#cb12-17" aria-hidden="true" tabindex="-1"></a></span> <span id="cb12-18"><a href="#cb12-18" aria-hidden="true" tabindex="-1"></a> <span class="op">#</span>add win button</span> <span id="cb12-19"><a href="#cb12-19" aria-hidden="true" tabindex="-1"></a></span> <span id="cb12-20"><a href="#cb12-20" aria-hidden="true" tabindex="-1"></a> <span class="op">#</span>showAll win</span> <span id="cb12-21"><a href="#cb12-21" aria-hidden="true" tabindex="-1"></a></span> <span id="cb12-22"><a href="#cb12-22" aria-hidden="true" tabindex="-1"></a> Gtk.main</span></code></pre></div> <p>It has two states; either the button has not been clicked yet, in which it shows a “sensitive” button, or the button has been clicked, in which it shows an “insensitive” button and a label thanking the user for clicking.</p> <table style="width: 100%; text-align: center;"> <tr> <td> <figure> <img src="/assets/gtk/hello1.png" width="248" alt="Initial state of “Hello, world!”" /> <figcaption aria-hidden="true">Initial state of “Hello, world!”</figcaption> </figure> </td> <td> <figure> <img src="/assets/gtk/hello2.png" width="248" alt="Terminal state of “Hello, world!”" /> <figcaption aria-hidden="true">Terminal state of “Hello, world!”</figcaption> </figure> </td> </tr> </table> <p>Let’s rewrite this application in a declarative style, using gi-gtk-declarative and gi-gtk-declarative-app-simple, and see how that works out! Our state and event datatypes describe what states the application can be in, and what events can be emitted, respectively:</p> <div class="sourceCode" id="cb13"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">State</span> <span class="ot">=</span> <span class="dt">NotClicked</span> <span class="op">|</span> <span class="dt">Clicked</span></span> <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Event</span> <span class="ot">=</span> <span class="dt">ButtonClicked</span></span></code></pre></div> <p>Our view function, here defined as <code>view'</code>, renders a label according to what state the application is in:</p> <div class="sourceCode" id="cb14"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="ot">view&#39; ::</span> <span class="dt">State</span> <span class="ot">-&gt;</span> <span class="dt">Widget</span> <span class="dt">Event</span></span> <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>view&#39; <span class="ot">=</span> \<span class="kw">case</span></span> <span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">NotClicked</span> <span class="ot">-&gt;</span></span> <span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a> widget</span> <span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">Button</span></span> <span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a> [ <span class="op">#</span>label <span class="op">:=</span> <span class="st">&quot;Click me&quot;</span></span> <span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> , on <span class="op">#</span>clicked <span class="dt">ButtonClicked</span></span> <span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> ]</span> <span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">Clicked</span> <span class="ot">-&gt;</span></span> <span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a> widget</span> <span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">Button</span></span> <span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a> [ <span class="op">#</span>sensitive <span class="op">:=</span> <span class="dt">False</span></span> <span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a> , <span class="op">#</span>label <span class="op">:=</span> <span class="st">&quot;Thanks for clicking me&quot;</span></span> <span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div> <p>The <code>update</code> function reduces the current state and an event to a <code>Transition event state</code>, which can either be <code>Transition</code> or <code>Exit</code>. Here we always transition to the <code>Clicked</code> state if the button has been clicked.</p> <div class="sourceCode" id="cb15"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="ot">update&#39; ::</span> <span class="dt">State</span> <span class="ot">-&gt;</span> <span class="dt">Event</span> <span class="ot">-&gt;</span> <span class="dt">Transition</span> <span class="dt">State</span> <span class="dt">Event</span></span> <span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>update&#39; _ <span class="dt">ButtonClicked</span> <span class="ot">=</span> <span class="dt">Transition</span> <span class="dt">Clicked</span> (<span class="fu">return</span> <span class="dt">Nothing</span>)</span></code></pre></div> <p>Note that the <code>Transition</code> constructor not only takes the next state, but also an <code>IO (Maybe Event)</code> action. This makes it possible to generate a new event in the update function.</p> <p>Finally, we run the “Hello, world!” application using <code>run</code>.</p> <div class="sourceCode" id="cb16"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span> <span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span></span> <span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> run</span> <span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Hi there&quot;</span></span> <span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">Nothing</span></span> <span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">App</span></span> <span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a> { view <span class="ot">=</span> view&#39;</span> <span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a> , update <span class="ot">=</span> update&#39;</span> <span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a> , inputs <span class="ot">=</span> []</span> <span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a> , initialState <span class="ot">=</span> <span class="dt">NotClicked</span></span> <span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a> }</span></code></pre></div> <p>Comparing with the imperative version, I like this style a lot better. The rendering code is a pure function, and core application logic can also be pure functions on data structures, instead of mutation of shared state. Moreover, the small state machine that was hiding in the original code is now explicit with the <code>State</code> sum type and the <code>update'</code> function.</p> <p>There are more examples in <a href="https://github.com/owickstrom/gi-gtk-declarative">gi-gtk-declarative</a> if you want to check them out.</p> <h2 id="implementation">Implementation</h2> <p>Writing gi-gtk-declarative has been a journey full of insights for me, and I’d like to share some implementation notes that might be interesting and helpful if you want to understand how the library works.</p> <h3 id="patching">Patching</h3> <p>At the core of the library lies the <code>Patchable</code> type class. The <code>create</code> method creates a new GTK+ widget given a declarative <code>widget</code>. The <code>patch</code> method calculates a minimal patch given two declarative widgets; the old and the new version:</p> <div class="sourceCode" id="cb17"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">Patchable</span> widget <span class="kw">where</span></span> <span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> create ::</span> widget e <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">Gtk.Widget</span></span> <span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a><span class="ot"> patch ::</span> widget e1 <span class="ot">-&gt;</span> widget e2 <span class="ot">-&gt;</span> <span class="dt">Patch</span></span></code></pre></div> <p>A patch describes a modification of a GTK+ widget, specifies a replacement of a GTK+ widget, or says that the GTK+ widget should be kept as-is.</p> <div class="sourceCode" id="cb18"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Patch</span></span> <span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">Modify</span> (<span class="dt">Gtk.Widget</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ())</span> <span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Replace</span> (<span class="dt">IO</span> <span class="dt">Gtk.Widget</span>)</span> <span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Keep</span></span></code></pre></div> <p>Replacing a widget is necessary if the declarative widget changes from one type of widget to another, say from <code>Button</code> to <code>ListBox</code>. We can’t modify a <code>Button</code> to become a <code>ListBox</code> in GTK+, so we have to create a new GTK+ widget and replace the existing one.</p> <h3 id="heterogeneous-widgets">Heterogeneous Widgets</h3> <p>Declarative widgets are often wrapped in the <code>Widget</code> datatype, to support widgets of any type to be used as a child in a heterogeneous container, and to be able to return <em>any</em> declarative widget, as we did in the <code>App</code> view function previously. The <code>Widget</code> datatype is a <a href="https://en.wikibooks.org/wiki/Haskell/GADT">GADT</a>:</p> <div class="sourceCode" id="cb19"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Widget</span> event <span class="kw">where</span></span> <span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">Widget</span></span> <span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> ( <span class="dt">Typeable</span> widget</span> <span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Patchable</span> widget</span> <span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Functor</span> widget</span> <span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a> , <span class="dt">EventSource</span> widget</span> <span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> widget event</span> <span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Widget</span> event</span></code></pre></div> <p>If you look at the <code>Widget</code> constructor’s type signature, you can see that it hides the inner <code>widget</code> type, and that it carries all the constraints needed to write instances for patching and event handling.</p> <p>We can define a <code>Patchable</code> instance for <code>Widget</code> as the inner widget is constrained to have an instance of <code>Patchable</code>. As the <code>widget</code> is also constrained with <code>Typeable</code>, we can use <code>eqT</code> to compare the types of two <code>Widget</code>s. If their inner declarative widget types are equal, we can calculate a patch from the declarative widgets. If not, we replace the old GTK+ widget with a new one created from the new declarative widget.</p> <div class="sourceCode" id="cb20"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Patchable</span> <span class="dt">Widget</span> <span class="kw">where</span></span> <span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a> create (<span class="dt">Widget</span> w) <span class="ot">=</span> create w</span> <span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> patch (<span class="dt">Widget</span> (<span class="ot">w1 ::</span> t1 e1)) (<span class="dt">Widget</span> (<span class="ot">w2 ::</span> t2 e2)) <span class="ot">=</span></span> <span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> eqT <span class="op">@</span>t1 <span class="op">@</span>t2 <span class="kw">of</span></span> <span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> <span class="dt">Refl</span> <span class="ot">-&gt;</span> patch w1 w2</span> <span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a> _ <span class="ot">-&gt;</span> <span class="dt">Replace</span> (create w2)</span></code></pre></div> <p>Similar to the case with <code>Patchable</code>, as we’ve constrained the inner widget type in the GADT, we can define instances for <code>Functor</code> and <code>EventSource</code>.</p> <p>At first, it might seem unintuitive to use dynamic typing in Haskell, but I think this case is very motivating, and it’s central to the implementation of gi-gtk-declarative.</p> <h3 id="smart-constructors-and-return-type-polymorphism">Smart Constructors and Return Type Polymorphism</h3> <p>All smart constructors — <code>widget</code>, <code>bin</code>, and <code>container</code> — can return either a <code>Widget</code> value, such that you can use it in a context where the inner widget type needs to be hidden, or a <code>MarkupOf</code> with a type specifically needed in the contexts in which the widget is used, for example, a bin or container with a requirement on what child widget it can contain.</p> <p>Here are some possible specializations of smart constructor return types:</p> <div class="sourceCode" id="cb21"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>widget <span class="dt">Button</span> []<span class="ot"> ::</span> <span class="dt">Widget</span> <span class="dt">MyEvent</span></span> <span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>widget <span class="dt">Button</span> []<span class="ot"> ::</span> <span class="dt">MarkupOf</span> (<span class="dt">SingleWidget</span> <span class="dt">Button</span>) <span class="dt">MyEvent</span> ()</span> <span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a>bin <span class="dt">ScrolledWindow</span> []<span class="ot"> _ ::</span> <span class="dt">Widget</span> <span class="dt">MyEvent</span></span> <span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a>bin <span class="dt">ScrolledWindow</span> []<span class="ot"> _ ::</span> <span class="dt">MarkupOf</span> (<span class="dt">Bin</span> <span class="dt">ScrolledWindow</span> <span class="dt">Widget</span>) <span class="dt">MyEvent</span> ()</span> <span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a>container <span class="dt">Box</span> []<span class="ot"> _ ::</span> <span class="dt">Widget</span> <span class="dt">MyEvent</span></span> <span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>container <span class="dt">Box</span> []<span class="ot"> _ ::</span> <span class="dt">MarkupOf</span> (<span class="dt">Container</span> <span class="dt">Box</span> (<span class="dt">Children</span> <span class="dt">BoxChild</span>)) <span class="dt">MyEvent</span> ()</span></code></pre></div> <p>As a small example, consider the helper <code>textRow</code> that constructs a <code>ListBoxRow</code> to be contained in a <code>ListBox</code>:</p> <div class="sourceCode" id="cb22"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="ot">myList ::</span> <span class="dt">Widget</span> <span class="dt">Event</span></span> <span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a>myList <span class="ot">=</span></span> <span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a> container <span class="dt">ListBox</span> [] <span class="op">$</span></span> <span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">mapM</span> textRow [<span class="st">&quot;Foo&quot;</span>, <span class="st">&quot;Bar&quot;</span>, <span class="st">&quot;Baz&quot;</span>]</span> <span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a><span class="ot">textRow ::</span> <span class="dt">Text</span> <span class="ot">-&gt;</span> <span class="dt">MarkupOf</span> (<span class="dt">Bin</span> <span class="dt">ListBoxRow</span> <span class="dt">Widget</span>) <span class="dt">Event</span> ()</span> <span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a>textRow t <span class="ot">=</span></span> <span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a> bin <span class="dt">ListBoxRow</span> [] <span class="op">$</span></span> <span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a> widget <span class="dt">Label</span> [ <span class="op">#</span>label <span class="op">:=</span> t ]</span></code></pre></div> <p>As the type signature above shows, the <code>textRow</code> function returns a <code>MarkupOf</code> value parameterized by a specific child type: <code>Bin ListBoxRow Widget</code>. You can read the whole type as “markup containing bins of list box rows, where the list box rows can contain any type of widget, and where they all emit events of type <code>Event</code>.” I know, it’s a mouthful, but as you probably won’t split your markup-building function up so heavily, and as GHC will be able to infer these types, it’s not an issue.</p> <p>The return type polymorphism of the smart constructors should not affect type inference badly. If you find a case where it does, please submit an issue on GitHub.</p> <h2 id="summary">Summary</h2> <p>Callback-centric GUI programming is hard. I prefer using data structures and pure functions for core application code, and keep it decoupled from the GUI code by making rendering as simple as a function <code>State -&gt; Widget</code>. This is the ideal I’m striving for, and what motivated the creation of these packages.</p> <p>I have just released <a href="https://hackage.haskell.org/package/gi-gtk-declarative">gi-gtk-declarative</a> and <a href="https://hackage.haskell.org/package/gi-gtk-declarative-app-simple">gi-gtk-declarative-app-simple</a> on Hackage. They are both to be regarded as experimental packages, but I hope for them to be useful and stable some day. Please try them out, and post issues on the <a href="https://github.com/owickstrom/gi-gtk-declarative/issues">GitHub tracker</a> if you find anything weird, and give me shout if you have any questions.</p> <p>The gi-gtk-declarative library is used heavily in FastCut, with great success. Unfortunately that project is not open source yet, so I can’t point you to any code examples right now. Hopefully, I’ll have it open-sourced soon, and I’m planning on blogging more about its implementation.</p> <p>Until then, happy native and declarative GUI hacking!</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2017-11-19-finite-state-machines-part-2.html</id>
    <title>Finite-State Machines, Part 2: Explicit Typed State Transitions</title>
    <link href="https://wickstrom.tech/2017-11-19-finite-state-machines-part-2.html"/>
    <published>2017-11-19T00:00:00+01:00</published>
    <updated>2017-11-19T00:00:00+01:00</updated>
    <summary>In the first part of this series, we left off having made states
explicit using Haskell data types. We concluded that state transitions
were implicit, and that a mistake in implementation, making an erroneous
state transition, would not be caught by the type system. We also noted
that side effects performed at state transitions complicated testing of
the state machine, as we were tied to IO.</summary>
    <content type="html"><![CDATA[
<p>In <a href="/2017-11-10-finite-state-machines-part-1-modeling-with-haskell.html">the first part of this series</a>, we left off having made states explicit using Haskell data types. We concluded that state transitions were implicit, and that a mistake in implementation, making an erroneous state transition, would not be caught by the type system. We also noted that side effects performed at state transitions complicated testing of the state machine, as we were tied to <code>IO</code>.</p> <p>Before addressing those problems, let’s remind ourselves of the example state machine diagram. If you haven’t read the previous post, I recommend you go do that first.</p> <p><img src="/assets/fsm-checkout.svg" /></p> <p>We begin with the language extensions we’ll need, along with the module declaration.</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE OverloadedStrings #-}</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE GADTs #-}</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE GeneralizedNewtypeDeriving #-}</span></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE TypeFamilies #-}</span></span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">EnforcingLegalStateTransitions</span> <span class="kw">where</span></span></code></pre></div> <p>Let me quickly explain the GHC language extensions used:</p> <ul> <li><code>OverloadedStrings</code> converts string literals in Haskell source code to <code>Text</code> values, in our case.</li> <li><code>GADTs</code> enables the use of <em>generalized algebraic data types</em>, an extension to GHC that lets us specify different type signatures for each constructor in a data type. This is useful to parameterize constructors with different types, something we will use for state data types.</li> <li>We use <code>GeneralizedNewtypeDeriving</code> to have our implementation newtype derive instances for <code>Functor</code>, <code>Applicative</code>, and <code>MonadIO</code>. I’ll explain this later in this post.</li> <li><code>TypeFamilies</code> extends GHC Haskell with what you can consider type-level functions. We’ll use this extension to associate a concrete state data type with our instance of the state machine.</li> </ul> <p>In addition to <code>Control.Monad.IO.Class</code>, we import the same modules as in the previous post.</p> <div class="sourceCode" id="cb2"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Monad.IO.Class</span></span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.List.NonEmpty</span></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Semigroup</span></span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text.IO</span> <span class="kw">as</span> <span class="dt">T</span></span></code></pre></div> <p>From the modules specific to the blog post series we import some functions and data types. As before, their exact implementations are not important. The <code>ConsoleInput</code> module provides helpers for retrieving text input and confirmation from the terminal.</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">PaymentProvider</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Checkout</span> ( <span class="dt">Card</span>(..)</span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> , <span class="dt">CartItem</span></span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> , <span class="dt">OrderId</span>(<span class="op">..</span>)</span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> , mkItem</span> <span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> , calculatePrice</span> <span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> , newOrderId</span> <span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> )</span> <span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">ConsoleInput</span></span></code></pre></div> <p>Enough imports, let’s go build our state machine!</p> <h2 id="the-state-machine-protocol">The State Machine Protocol</h2> <p>In contrast to the transition function in <a href="/2017-11-10-finite-state-machines-part-1-modeling-with-haskell.html#finite-state-machines">the previous post</a>, where a single function had the responsibility of deciding which state transitions were legal, performing associated side effects on transitions, and transitioning to the next state, we will now separate the <em>protocol</em> from the <em>program</em>. In other words, the set of states, and the associated state transitions for certain events, will be encoded separately from the implementation of the automaton. Conceptually, it still follows the definition we borrowed from Erlang:</p> <blockquote> <p> <p><em>State(S) × Event(E) → Actions (A), State(S’)</em></p> </p> </blockquote> <p>With the risk of stretching a metaphor too thin, I like to think of the split representation as taking the single-function approach and turning it inside-out. Our program is separate from our state machine protocol, and we can implement it however we like, as long as we follow the protocol.</p> <h2 id="empty-data-types-for-states">Empty Data Types for States</h2> <p>Our states are no longer represented by a single data type with constructors for each state. Instead, we create an <em>empty data type</em> for each state. Such a type has no constructors, and is therefore not inhabited by any value. We will use them solely as <em>markers</em> in GADT constructors, a technique generally known as <a href="https://stackoverflow.com/a/28250226">phantom types</a>.</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">NoItems</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">HasItems</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">NoCard</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">CardSelected</span></span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">CardConfirmed</span></span> <span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">OrderPlaced</span></span></code></pre></div> <p>I know that phantom types and GADTs sound scary at first, but please hang in there, as I’ll explain their practical use throughout this post, and hopefully give you a sense of why we are using them.</p> <h2 id="state-machine-protocol-as-a-type-class">State Machine Protocol as a Type Class</h2> <p>We encode our state machine protocol, separate from the program, using a <em>type class</em>.</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">Checkout</span> m <span class="kw">where</span></span></code></pre></div> <p>In our protocol, we do not want to be specific about what data type is used to represent the current state; we only care about the state type it is parameterized by. Therefore, we use an associated type alias, also known as an open type family, with kind <code>* -&gt; *</code> to represent states.</p> <div class="sourceCode" id="cb6"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a> <span class="kw">type</span> <span class="dt">State</span><span class="ot"> m ::</span> <span class="op">*</span> <span class="ot">-&gt;</span> <span class="op">*</span></span></code></pre></div> <p>The signature <code>* -&gt; *</code> can be thought of as a type-level function, or a type constructor, from type to type (the star is the kind of types). The parameter <code>m</code> to state means we are associating the state type with the instance of <code>m</code>, so that different instances can specify their own concrete state types.</p> <p>There are two benefits of using an associated type for state:</p> <ol type="1"> <li>Different instances of <code>Checkout</code> can provide different concrete state data types, hiding whatever nasty implementation details they need to operate, such as database connections, web sessions, or file handles.</li> <li>The concrete state type is not known when using the state machine protocol, and it is therefore impossible to create a state “manually”; the program would not typecheck.</li> </ol> <p>In our case, the parameter will always be one of our empty data types declared for states. As an example, <code>(State m NoItems)</code> has kind <code>*</code>, and is used to represent the “NoItems” state abstractly.</p> <p>Note that <code>m</code> <em>also</em> has kind <code>* -&gt; *</code>, but not for the same reason; the <code>m</code> is going to be the monadic type we use for our implementation, and is therefore higher-kinded.</p> <h2 id="events-as-type-class-methods">Events as Type Class Methods</h2> <p><code>Checkout</code> specifies the state machine events as type class <em>methods</em>, where method type signatures describe state transitions. The <code>initial</code> method creates a new checkout, returning a “NoItems” state. It can be thought of as a <em>constructor</em> in object-oriented programming terms.</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a> initial</span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> m (<span class="dt">State</span> m <span class="dt">NoItems</span>)</span></code></pre></div> <p>The value returned, of type <code>(State m NoItems)</code>, is the first state. We use this value as a parameter to the subsequent event, transitioning to another state. Events that transition state from one to another take the current state as an argument, and return the resulting state.</p> <p>The <code>select</code> event is a bit tricky, as it is accepted from both “NoItems” and “HasItems”. We use the union data type <code>SelectState</code>, analogous to <code>Either</code>, that represents the possibility of either “NoItems” or “HasItems”. The definition of <code>SelectState</code> is included further down this post.</p> <div class="sourceCode" id="cb8"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a> select</span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">SelectState</span> m</span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">CartItem</span></span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">HasItems</span>)</span></code></pre></div> <p>Worth noting is that the resulting state is returned inside <code>m</code>. We do that to enable the instance of <code>Checkout</code> to perform computations available in <code>m</code> at the state transition.</p> <p><em>Does this ring a bell?</em></p> <p>Just as in the previous post, we want the possibility to interleave side effects on state transitions, and using a monadic return value gives the instance that flexibility.</p> <p>The <code>checkout</code> event is simpler than <code>select</code>, as it transitions from exactly one state to another.</p> <div class="sourceCode" id="cb9"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a> checkout</span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">State</span> m <span class="dt">HasItems</span></span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">NoCard</span>)</span></code></pre></div> <p>Some events, like <code>selectCard</code>, carry data in the form of arguments, corresponding to how some event data constructors had arguments. Most of the events in <code>Checkout</code> follow the patterns described so far.</p> <div class="sourceCode" id="cb10"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a> selectCard</span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">State</span> m <span class="dt">NoCard</span></span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Card</span></span> <span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">CardSelected</span>)</span> <span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> confirm</span> <span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">State</span> m <span class="dt">CardSelected</span></span> <span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">CardConfirmed</span>)</span> <span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a> placeOrder</span> <span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">State</span> m <span class="dt">CardConfirmed</span></span> <span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">OrderPlaced</span>)</span></code></pre></div> <p>Note that we are <em>not doing any error handling</em>. All operations return state values. In a real-world system you might need to handle error cases, like <code>selectCard</code> not accepting the entered card number. I have deliberately excluded error handling from this already lengthy post, but I will probably write a post about different ways of handling errors in this style of state machine encoding.</p> <p>Similar to <code>select</code>, the <code>cancel</code> event is accepted from more than one state. In fact, it is accepted from <em>three</em> states: “NoCard”, “CardSelected”, and “CardConfirmed”. Like with <code>select</code>, we use a union data type representing the ternary alternative.</p> <div class="sourceCode" id="cb11"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a> cancel</span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">CancelState</span> m</span> <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">HasItems</span>)</span></code></pre></div> <p>Finally, we have the <code>end</code> method as a way of ending the state machine in its terminal state, similar to a <em>destructor</em> in object-oriented programming terms. Instances of <code>Checkout</code> can have <code>end</code> clean up resources associated with the machine.</p> <div class="sourceCode" id="cb12"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a> end</span> <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">State</span> m <span class="dt">OrderPlaced</span></span> <span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m <span class="dt">OrderId</span></span></code></pre></div> <p>As promised, I will show you the definitions of <code>SelectState</code> and <code>CancelState</code>, the data types that represent alternative source states for the <code>select</code> and <code>cancel</code> events, respectively.</p> <div class="sourceCode" id="cb13"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">SelectState</span> m</span> <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">NoItemsSelect</span> (<span class="dt">State</span> m <span class="dt">NoItems</span>)</span> <span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">HasItemsSelect</span> (<span class="dt">State</span> m <span class="dt">HasItems</span>)</span> <span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">CancelState</span> m</span> <span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">NoCardCancel</span> (<span class="dt">State</span> m <span class="dt">NoCard</span>)</span> <span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">CardSelectedCancel</span> (<span class="dt">State</span> m <span class="dt">CardSelected</span>)</span> <span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">CardConfirmedCancel</span> (<span class="dt">State</span> m <span class="dt">CardConfirmed</span>)</span></code></pre></div> <p>Each constructor takes a specific state as argument, thus creating a union type wrapping the alternatives.</p> <h2 id="a-program-using-the-state-machine-protocol">A Program using the State Machine Protocol</h2> <p>Now that we have a state machine protocol, the <code>Checkout</code> type class, we can write a program with it. This is the automaton part of our implementation, i.e. the part that <em>drives</em> the state machine forward.</p> <p>As long as the program follows the protocol, it can be structured however we like; we can drive it using user input from a console, by listening to a queue of commands, or by incoming HTTP requests from a web server. In the interest of this post, however, we will keep to reading user input from the console.</p> <p>The type signature of <code>fillCart</code> constrains <code>m</code> to be an instance of both <code>Checkout</code> and <code>MonadIO</code>. Moreover, it is a function from a “NoItems” state to a “HasItems” state. The type is similar to the event methods’ type signatures in the <code>Checkout</code> protocol, and similarly describes a state transition with a type.</p> <div class="sourceCode" id="cb14"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>fillCart</span> <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> (<span class="dt">Checkout</span> m, <span class="dt">MonadIO</span> m)</span> <span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">State</span> m <span class="dt">NoItems</span></span> <span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">HasItems</span>)</span></code></pre></div> <p>This is where we are starting to use the <a href="https://ocharles.org.uk/blog/posts/2016-01-26-transformers-free-monads-mtl-laws.html">MTL style</a> of abstracting effects, and combining different effects by constraining the monadic type with multiple type classes.</p> <p>The critical reader might object to using <code>MonadIO</code>, and claim that we have not separated all side effects, and failed in making the program testable. They wouldn’t be wrong. I have deliberately left the direct use of <code>MonadIO</code> in to keep the example somewhat concrete. We could refactor it to depend on, say, a <code>UserInput</code> type class for collecting more abstract user commands. By using <code>MonadIO</code>, though, the example highlights particularly how the state machine protocol has been abstracted, and how the effects of state transitions are guarded by the type system, rather than making everything abstract. I encourage you to try out both approaches in your code!</p> <p>The definition of <code>fillCart</code> takes a “NoItems” state value as an argument, prompts the user for the first cart item, selects it, and hands off to <code>selectMoreItems</code>.</p> <div class="sourceCode" id="cb15"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>fillCart noItems <span class="ot">=</span></span> <span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> mkItem <span class="op">&lt;$&gt;</span> ConsoleInput.prompt <span class="st">&quot;First item:&quot;</span></span> <span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a> <span class="op">&gt;&gt;=</span> select (<span class="dt">NoItemsSelect</span> noItems)</span> <span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> <span class="op">&gt;&gt;=</span> selectMoreItems</span></code></pre></div> <p>The event methods of the <code>Checkout</code> protocol, and functions like <code>fillCart</code> and <code>selectMoreItems</code>, are functions from one state to a monadic return value of another state, and thus compose using <code>(&gt;&gt;=)</code>.</p> <p>The <code>selectMoreItems</code> function remains in a “HasItems” state. It asks the user if they want to add more items. If so, it asks for the next item, selects that and recurses to possibly add even more items; if not, it returns the current “HasItems” state. Note how we need to wrap the “HasItems” state in <code>HasItemsSelect</code> to create a <code>SelectState</code> value.</p> <div class="sourceCode" id="cb16"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>selectMoreItems</span> <span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> (<span class="dt">Checkout</span> m, <span class="dt">MonadIO</span> m)</span> <span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">State</span> m <span class="dt">HasItems</span></span> <span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">HasItems</span>)</span> <span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>selectMoreItems s <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a> more <span class="ot">&lt;-</span> ConsoleInput.confirm <span class="st">&quot;More items?&quot;</span></span> <span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">if</span> more</span> <span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">then</span></span> <span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a> mkItem <span class="op">&lt;$&gt;</span> ConsoleInput.prompt <span class="st">&quot;Next item:&quot;</span></span> <span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a> <span class="op">&gt;&gt;=</span> select (<span class="dt">HasItemsSelect</span> s)</span> <span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a> <span class="op">&gt;&gt;=</span> selectMoreItems</span> <span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">else</span> <span class="fu">return</span> s</span></code></pre></div> <p>When all items have been added, we are ready to start the checkout part. The type signature of <code>startCheckout</code> tells us that it transitions from a “HasItems” state to an “OrderPlaced” state.</p> <div class="sourceCode" id="cb17"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>startCheckout</span> <span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> (<span class="dt">Checkout</span> m, <span class="dt">MonadIO</span> m)</span> <span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">State</span> m <span class="dt">HasItems</span></span> <span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">OrderPlaced</span>)</span></code></pre></div> <p>The function starts the checkout, prompts for a card, and selects it. It asks the user to confirm the use of the selected card, and ends by placing the order. If the user did not confirm, the checkout is canceled, and we go back to selecting more items, followed by attempting a new checkout.</p> <div class="sourceCode" id="cb18"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>startCheckout hasItems <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> noCard <span class="ot">&lt;-</span> checkout hasItems</span> <span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> card <span class="ot">&lt;-</span> ConsoleInput.prompt <span class="st">&quot;Card:&quot;</span></span> <span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> cardSelected <span class="ot">&lt;-</span> selectCard noCard (<span class="dt">Card</span> card)</span> <span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a> useCard <span class="ot">&lt;-</span> ConsoleInput.confirm (<span class="st">&quot;Confirm use of &#39;&quot;</span> <span class="op">&lt;&gt;</span> card <span class="op">&lt;&gt;</span> <span class="st">&quot;&#39;?&quot;</span>)</span> <span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">if</span> useCard</span> <span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">then</span> confirm cardSelected <span class="op">&gt;&gt;=</span> placeOrder</span> <span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">else</span> cancel (<span class="dt">CardSelectedCancel</span> cardSelected) <span class="op">&gt;&gt;=</span></span> <span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a> selectMoreItems <span class="op">&gt;&gt;=</span></span> <span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a> startCheckout</span></code></pre></div> <p>The protocol allows for cancellation in all three checkout states, but that the program only gives the user a possibility to cancel in the end of the process. Again, the program must follow the rules of the protocol, but it is not required to trigger all events the protocol allows for.</p> <p>The definition of <code>checkoutProgram</code> is a composition of what we have so far. It creates the state machine in its initial state, fills the shopping cart, starts the checkout, and eventually ends the checkout.</p> <div class="sourceCode" id="cb19"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>checkoutProgram</span> <span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> (<span class="dt">Checkout</span> m, <span class="dt">MonadIO</span> m)</span> <span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> m <span class="dt">OrderId</span></span> <span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>checkoutProgram <span class="ot">=</span></span> <span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a> initial <span class="op">&gt;&gt;=</span> fillCart <span class="op">&gt;&gt;=</span> startCheckout <span class="op">&gt;&gt;=</span> end</span></code></pre></div> <p>We now have a complete program using the <code>Checkout</code> state machine protocol. To run it, however, we need an instance of <code>Checkout</code>.</p> <h2 id="defining-an-instance-for-checkout">Defining an Instance for Checkout</h2> <p>To define an instance for <code>Checkout</code>, we need a type to define it for. A common way of defining such types, especially in MTL style, is using <code>newtype</code> around a monadic value. The type name often ends with <code>T</code> to denote that it’s a transformer. Also by convention, the constructor takes a single-field record, where the field accessor follows the naming scheme <code>run&lt;TypeName&gt;</code>; in our case <code>runCheckoutT</code>.</p> <div class="sourceCode" id="cb20"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">CheckoutT</span> m a <span class="ot">=</span> <span class="dt">CheckoutT</span></span> <span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a> {<span class="ot"> runCheckoutT ::</span> m a</span> <span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> } <span class="kw">deriving</span> ( <span class="dt">Functor</span></span> <span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Monad</span></span> <span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Applicative</span></span> <span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a> , <span class="dt">MonadIO</span></span> <span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a> )</span></code></pre></div> <p>We derive the <code>MonadIO</code> instance automatically, along with the standard <code>Functor</code>, <code>Applicative</code>, and <code>Monad</code> hierarchy.</p> <h2 id="monad-transformer-stacks-and-instance-coupling">Monad Transformer Stacks and Instance Coupling</h2> <p>Had we not derived <code>MonadIO</code>, the program from before, with constraints on both <code>Checkout</code> and <code>MonadIO</code>, would not have compiled. Therein lies a subtle dependency that is hard to see at first, but that might cause you a lot of headache. Data types used to instantiate MTL style type classes, when stacked, need to implement all type classes in use. This is caused by the <em>stacking</em> aspect of monad transformers, and is a common critique of MTL style.</p> <p>Other techniques for separating side effects, such as free monads or extensible effects, have other tradeoffs. I have chosen to focus on MTL style as it is widely used, and in my opinion, a decent starting point. If anyone rewrites these examples using another technique, please drop a comment!</p> <h2 id="a-concrete-state-data-type">A Concrete State Data Type</h2> <p>Remember how we have, so far, only been talking about state values abstractly, in terms of the associated type alias <code>State</code> in the <code>Checkout</code> class? It is time to provide the concrete data type for state that we will use in our instance.</p> <p>As discussed earlier, the type we associate for <code>State</code> need to have kind <code>(* -&gt; *)</code>. The argument is the state marker type, i.e. one of the empty data types for states. We define the data type <code>CheckoutState</code> using a GADT, where <code>s</code> is the state type.</p> <div class="sourceCode" id="cb21"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">CheckoutState</span> s <span class="kw">where</span></span></code></pre></div> <p>With <code>GADTs</code>, data constructors specify their own type signatures, allowing the use of phantom types, and differently typed values resulting from the constructors. Each constructor parameterize the <code>CheckoutState</code> with a different state type.</p> <p>The <code>NoItems</code> constructor is nullary, and constructs a value of type <code>CheckoutState NoItems</code>.</p> <div class="sourceCode" id="cb22"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a> <span class="dt">NoItems</span></span> <span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">CheckoutState</span> <span class="dt">NoItems</span></span></code></pre></div> <div class="note"> <p>The <em>data constructor</em> <code>NoItems</code> is defined here, whereas the <em>type</em> <code>NoItems</code> is defined in the beginning of the program, and they are <em>not directly related</em>.</p> </div> <p>There is, however, a relation between them in terms of the <code>CheckoutState</code> data type. If we have a value of type <code>CheckoutState NoItems</code>, and we pattern match on it, GHC knows that there is only one constructor for such a value. This will become very handy when defining our instance.</p> <p>The other constructors are defined similarly, but some have arguments, in the same way the <code>State</code> data type in the previous post had. They accumulate the extended state needed by the state machine, up until the order is placed.</p> <div class="sourceCode" id="cb23"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a> <span class="dt">HasItems</span></span> <span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">NonEmpty</span> <span class="dt">CartItem</span></span> <span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">CheckoutState</span> <span class="dt">HasItems</span></span> <span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">NoCard</span></span> <span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">NonEmpty</span> <span class="dt">CartItem</span></span> <span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">CheckoutState</span> <span class="dt">NoCard</span></span> <span id="cb23-8"><a href="#cb23-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb23-9"><a href="#cb23-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">CardSelected</span></span> <span id="cb23-10"><a href="#cb23-10" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">NonEmpty</span> <span class="dt">CartItem</span></span> <span id="cb23-11"><a href="#cb23-11" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Card</span></span> <span id="cb23-12"><a href="#cb23-12" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">CheckoutState</span> <span class="dt">CardSelected</span></span> <span id="cb23-13"><a href="#cb23-13" aria-hidden="true" tabindex="-1"></a></span> <span id="cb23-14"><a href="#cb23-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">CardConfirmed</span></span> <span id="cb23-15"><a href="#cb23-15" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">NonEmpty</span> <span class="dt">CartItem</span></span> <span id="cb23-16"><a href="#cb23-16" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Card</span></span> <span id="cb23-17"><a href="#cb23-17" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">CheckoutState</span> <span class="dt">CardConfirmed</span></span> <span id="cb23-18"><a href="#cb23-18" aria-hidden="true" tabindex="-1"></a></span> <span id="cb23-19"><a href="#cb23-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">OrderPlaced</span></span> <span id="cb23-20"><a href="#cb23-20" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">OrderId</span></span> <span id="cb23-21"><a href="#cb23-21" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">CheckoutState</span> <span class="dt">OrderPlaced</span></span></code></pre></div> <p>We have a concrete state data type, defined as a GADT, and we can go ahead defining the instance of <code>Checkout</code> for our <code>CheckoutT</code> newtype. We need <code>MonadIO</code> to perform <code>IO</code> on state transitions, such as charging the customer card.</p> <div class="sourceCode" id="cb24"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> (<span class="dt">MonadIO</span> m) <span class="ot">=&gt;</span> <span class="dt">Checkout</span> (<span class="dt">CheckoutT</span> m) <span class="kw">where</span></span></code></pre></div> <p>Next, we can finally tie the knot, associating the state type for <code>CheckoutT</code> with <code>CheckoutState</code>.</p> <div class="sourceCode" id="cb25"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a> <span class="kw">type</span> <span class="dt">State</span> (<span class="dt">CheckoutT</span> m) <span class="ot">=</span> <span class="dt">CheckoutState</span></span></code></pre></div> <p>We continue by defining the methods. The <code>initial</code> method creates the state machine by returning the initial state, the <code>NoItems</code> constructor.</p> <div class="sourceCode" id="cb26"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a> initial <span class="ot">=</span> <span class="fu">return</span> <span class="dt">NoItems</span></span></code></pre></div> <p>In <code>select</code>, we receive the current state, which can be either one of the constructors of <code>SelectState</code>. Unwrapping those gives us the <code>CheckoutState</code> value. We return the <code>HasItems</code> state with the selected item prepended to a non-empty list.</p> <div class="sourceCode" id="cb27"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a> select state item <span class="ot">=</span></span> <span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> state <span class="kw">of</span></span> <span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">NoItemsSelect</span> <span class="dt">NoItems</span> <span class="ot">-&gt;</span></span> <span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">HasItems</span> (item <span class="op">:|</span> []))</span> <span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">HasItemsSelect</span> (<span class="dt">HasItems</span> items) <span class="ot">-&gt;</span></span> <span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">HasItems</span> (item <span class="op">&lt;|</span> items))</span></code></pre></div> <p>As emphasized before, GHC knows which constructors of <code>CheckoutState</code> can occur in the <code>SelectState</code> wrappers, and we can pattern match exhaustively on only the possible state constructors.</p> <p>The <code>checkout</code>, <code>selectCard</code>, and <code>confirm</code> methods accumulate the extended state, and returns the appropriate state constructor.</p> <div class="sourceCode" id="cb28"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a> checkout (<span class="dt">HasItems</span> items) <span class="ot">=</span></span> <span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">NoCard</span> items)</span> <span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a> selectCard (<span class="dt">NoCard</span> items) card <span class="ot">=</span></span> <span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">CardSelected</span> items card)</span> <span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a> confirm (<span class="dt">CardSelected</span> items card) <span class="ot">=</span></span> <span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">CardConfirmed</span> items card)</span></code></pre></div> <p>Now for <code>placeOrder</code>, where we want to perform a side effect. We have constrained <code>m</code> to be an instance of <code>MonadIO</code>, and we can thus use the effectful <code>newOrderId</code> and <code>PaymentProvider.chargeCard</code> in our definition.</p> <div class="sourceCode" id="cb29"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a> placeOrder (<span class="dt">CardConfirmed</span> items card) <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a> orderId <span class="ot">&lt;-</span> newOrderId</span> <span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> price <span class="ot">=</span> calculatePrice items</span> <span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a> PaymentProvider.chargeCard card price</span> <span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">OrderPlaced</span> orderId)</span></code></pre></div> <p>Similar to <code>select</code>, <code>cancel</code> switches on the alternatives of the <code>CancelState</code> data type. In all cases it returns the <code>HasItems</code> state with the current list of items.</p> <div class="sourceCode" id="cb30"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a> cancel cancelState <span class="ot">=</span></span> <span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> cancelState <span class="kw">of</span></span> <span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">NoCardCancel</span> (<span class="dt">NoCard</span> items) <span class="ot">-&gt;</span></span> <span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">HasItems</span> items)</span> <span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">CardSelectedCancel</span> (<span class="dt">CardSelected</span> items _) <span class="ot">-&gt;</span></span> <span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">HasItems</span> items)</span> <span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">CardConfirmedCancel</span> (<span class="dt">CardConfirmed</span> items _) <span class="ot">-&gt;</span></span> <span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">HasItems</span> items)</span></code></pre></div> <p>Finally, the definition of <code>end</code> returns the generated order identifier.</p> <div class="sourceCode" id="cb31"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a> end (<span class="dt">OrderPlaced</span> orderId) <span class="ot">=</span> <span class="fu">return</span> orderId</span></code></pre></div> <p>The <code>CheckoutT</code> instance of <code>Checkout</code> is complete, and we are ready to stitch everything together into a running program.</p> <h2 id="putting-the-pieces-together">Putting the Pieces Together</h2> <p>To run <code>checkoutProgram</code>, we need an instance of <code>Checkout</code>, and an instance of <code>MonadIO</code>. There is already an instance <code>(MonadIO IO)</code> available. To select our <code>CheckoutT</code> instance for <code>Checkout</code>, we use <code>runCheckoutT</code>.</p> <div class="sourceCode" id="cb32"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="ot">example ::</span> <span class="dt">IO</span> ()</span> <span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a>example <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">OrderId</span> orderId <span class="ot">&lt;-</span> runCheckoutT checkoutProgram</span> <span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a> T.putStrLn (<span class="st">&quot;Completed with order ID: &quot;</span> <span class="op">&lt;&gt;</span> orderId)</span></code></pre></div> <p>The complete checkout program is run, using the <code>CheckoutT</code> instance, and an <code>OrderId</code> is returned, which we print at the end. A sample execution of this program looks like this:</p> <pre> λ> <strong>example</strong> First item: <strong>Banana</strong> More items? (y/N) <strong>y</strong> Next item: <strong>Horse</strong> More items? (y/N) <strong>y</strong> Next item: <strong>House</strong> More items? (y/N) <strong>n</strong> Card: <strong>0000-0000-0000-0000</strong> Confirm use of '0000-0000-0000-0000'? (y/N) <strong>y</strong> Charging card 0000-0000-0000-0000 $200 Completed with order ID: foo </pre> <p>Cool, we have a console implementation running!</p> <h2 id="instances-without-side-effects">Instances Without Side Effects</h2> <p>A benefit of using MTL style, in addition to have effects be explicit, is that we can write alternative instances. We might write an instance that only logs the effects, using a <code>Writer</code> monad , collecting them as pure values in a list, and use that instance when testing the state machine.</p> <h2 id="parting-thoughts">Parting Thoughts</h2> <p>Using a sort of <em>extended MTL style</em>, with conventions for state machine encodings, gives us more type safety in state transitions. In addition to having turned our state machine program <em>inside-out</em>, into a protocol separated from the automaton, we have guarded side effects with types in the form of type class methods. Abstract state values, impossible to create outside the instance, are now passed explicitly in state transitions.</p> <p>But we still have a rather loud elephant in the room.</p> <p>Suppose I’d write the following function, wherein I place the order <em>twice</em>. Do you think it would typecheck?</p> <div class="sourceCode" id="cb33"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="ot">doBadThings ::</span></span> <span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a> (<span class="dt">Checkout</span> m, <span class="dt">MonadIO</span> m)</span> <span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">State</span> m <span class="dt">CardConfirmed</span></span> <span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">State</span> m <span class="dt">OrderPlaced</span>)</span> <span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a>doBadThings cardConfirmed <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb33-6"><a href="#cb33-6" aria-hidden="true" tabindex="-1"></a> _ <span class="ot">&lt;-</span> placeOrder cardConfirmed</span> <span id="cb33-7"><a href="#cb33-7" aria-hidden="true" tabindex="-1"></a> placeOrder cardConfirmed</span></code></pre></div> <p>The answer is yes, it would typecheck. With the <code>Checkout</code> instance we have, the customer’s card would be <em>charged twice</em>, without doubt departing from our business model, and likely hurting our brand.</p> <p>The problem is that we are allowed to discard the state transitioned to, a value of type <code>(State m OrderPlaced)</code>, returned by the first <code>placeOrder</code> expression. Then, we can place the order again, using the old state value of type <code>(State m CardConfirmed)</code>. The ability to reuse, or never use, state values is the <em>Achilles’ heel</em> of this post’s state machine encoding.</p> <p>We could venture into the land of <em>linear types</em>, a feature <a href="https://github.com/ghc-proposals/ghc-proposals/pull/91/files">recently proposed to be added to GHC</a>. With linear types, we could ensure state values are used <em>exactly once</em>, making our current approach safer.</p> <p>I’d like to step back for a moment, however, and remind you that the techniques we encounter along this journey are not ordered as increasingly “better”, in terms of what you should apply in your work. I show more and more advanced encodings, using various GHC language extensions, but it doesn’t mean you should necessarily use the most advanced one. Simplicity is powerful, something <a href="https://twitter.com/thumphriees/status/932137942222385153">Tim Humphries tweet thread reminded me about this morning</a>, and I recommend you start out simple.</p> <p>As demonstrated, the extended MTL style for encoding state machines presented in this post has a type safety flaw. That doesn’t mean the technique is useless and should be forever rejected. At least not in my opinion. It gives additional type safety around state transitions, it composes well with MTL style programs in general, and it uses a modest collection of type system features and language extensions. We can write alternative instances, without any IO, and use them to test our state machines programs in a pure setting.</p> <p>If you still feel that all hope is lost, then I’m happy to announce that there will be more posts coming in this series! To demonstrate a possible next step, in terms of even greater type safety, in the next post we will be exploring <em>indexed monads</em> and <em>row kinds</em> as a way of armoring the Achilles heel.</p> <p>Happy hacking!</p> <h2 id="revisions">Revisions</h2> <p><strong>November 20, 2017:</strong> Based on a Reddit comment, on the lack of error handling in event type class methods, I added a small note about that below the <code>selectCard</code> type signature.</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2017-11-10-finite-state-machines-part-1-modeling-with-haskell.html</id>
    <title>Finite-State Machines, Part 1: Modeling with Haskell Data Types</title>
    <link href="https://wickstrom.tech/2017-11-10-finite-state-machines-part-1-modeling-with-haskell.html"/>
    <published>2017-11-10T00:00:00+01:00</published>
    <updated>2017-11-10T00:00:00+01:00</updated>
    <summary>Stateful programs often become complex beasts as they grow. Program
state incohesively spread across a bunch of variables, spuriously
guarded by even more variables, is what I refer to as implicit state.
When working with such code, we have to reconstruct a model mentally,
identifying possible states and transitions between them, to modify the
program with confidence. Even if a test suite can help, the process is
tedious and error-prone, and I insist we should have our tools do the
heavy lifting instead.</summary>
    <content type="html"><![CDATA[
<p>Stateful programs often become complex beasts as they grow. Program state incohesively spread across a bunch of variables, spuriously guarded by even more variables, is what I refer to as <em>implicit state</em>. When working with such code, we have to reconstruct a model mentally, identifying possible states and transitions between them, to modify the program with confidence. Even if a test suite can help, the process is tedious and error-prone, and I insist we should have our tools do the heavy lifting instead.</p> <p>By teaching the type system about possible states and state transitions in our program, it can verify that we follow our own business rules, both when we write new code, and when we modify existing code. It is not merely a process of asking the compiler “did I do okay?” Our workflow can be a conversation with the compiler, a process known as <em>type-driven development.</em> Moreover, the program <em>encodes</em> the state machine as living machine-verified documentation.</p> <p>After having given my talk at Code Mesh on this topic, and having spent a lot of time researching and preparing examples, I want to share the material in the form of a blog post series. Each post will cover increasingly advanced techniques that give greater static safety guarantees. That is not to say that the latter techniques are inherently better, nor that they are the ones that you should use. This series is meant as a small à la carte of event-driven state machine encodings and type safety, where you can choose from the menu based on your taste and budget. I will, however, present the techniques in a linear form. Also, note that these posts do not claim to exhaust all options for state machine encodings.</p> <p>There are many trade-offs, including type safety and strictness, implementation complexity, and how language, technique, and library choices affect your team. Taking one step towards <em>explicit state</em>, in an area where it leverages your project in doing so, can be the best choice. You don’t have to go nuts with type systems to use explicit states in your program! Furthermore, most mainstream languages today let you encode states as data types in some way.</p> <figure> <img src="/assets/fsm-map.png" alt="Our journey begins in the Valley of Programmer Death, deep in the lands of Implicit State. Follow along for as long as you like, and settle down in a place of your choice." /> <figcaption aria-hidden="true">Our journey begins in the Valley of Programmer Death, deep in the lands of Implicit State. Follow along for as long as you like, and settle down in a place of your choice.</figcaption> </figure> <p>This is the introductory post, in which I’ll show the first step on our way from implicit state and despair to writing stateful and effectful programs with great confidence. We will use Haskell and <em>algebraic data types</em> (ADTs) to encode possible states as data types. You should be able to read and understand this post without knowing much Haskell. If not, tell me, and I will try to explain better.</p> <h2 id="finite-state-machines">Finite-State Machines</h2> <p>First, we should have a clear understanding of what a finite-state machine is. There are many variations and definitions, and I’m sure you, especially if coming from an engineering background, have some relation to state machines.</p> <p>In general, a finite-state machine can be described as an abstract machine with a finite set of states, being in one state at a time. <em>Events</em> trigger state transitions; that is, the machine changes from being in one state to being in another state. The machine defines a set of legal transitions, often expressed as associations from a state and event pair to a state.</p> <p>For the domains we will be exploring, the <a href="http://erlang.org/documentation/doc-4.8.2/doc/design_principles/fsm.html">Erlang documentation’s definition</a> of a finite-state machine is simple and useful:</p> <blockquote> <p> <p><em>State(S) × Event(E) → Actions (A), State(S’)</em></p> </p> <p> <p>If we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S’.</p> </p> </blockquote> <p>That is the basis for the coming posts. I will not categorize as Mealy or Moore machines, or use UML state charts, at least not to any greater extent. Some diagrams will use the notation for hierarchical state machines for convenience.</p> <h2 id="example-shopping-cart-checkout">Example: Shopping Cart Checkout</h2> <p>The running example we will use in these posts is a <em>shopping cart checkout</em>, modeled as an event-driven finite-state machine. This stems from a real-world project I worked on, where the lack of explicit states in code became a real problem as requirements evolved. It’s the use-case that inspired me to look for more robust methods.</p> <figure> <img src="/assets/fsm-checkout.svg" alt="The running example, a shopping cart checkout." /> <figcaption aria-hidden="true">The running example, a shopping cart checkout.</figcaption> </figure> <p>As shown graphically in the state diagram above, we start in “NoItems”, selecting one or more items, staying in “HasItems”, until we begin the checkout. We enter the nested “Checkout” machine on the “checkout” event. Modeling it as a hierarchically nested machine we can have all its states accept the “cancel” event. We select and confirm a card, and eventually place an order, if not canceled.</p> <h2 id="states-and-events-as-data-types">States and Events as Data Types</h2> <p>Let’s get started. We will use <code>Text</code> instead of <code>String</code>, and <code>NonEmpty</code> lists. The two modules <code>PaymentProvider</code> and <code>Checkout</code> hide some implementation detail of lesser importance.</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE OverloadedStrings #-}</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">StateMachinesWithAdts</span> <span class="kw">where</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Monad</span> (foldM)</span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.List.NonEmpty</span></span> <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Text</span> (<span class="dt">Text</span>)</span> <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Text.Printf</span> (printf)</span> <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">PaymentProvider</span></span> <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Checkout</span> ( <span class="dt">Card</span>(..)</span> <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> , <span class="dt">CartItem</span>(<span class="op">..</span>)</span> <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> , calculatePrice</span> <span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> )</span></code></pre></div> <p><code>CheckoutState</code> is a sum type, with one constructor for each valid state. Some constructors are <em>nullary</em>, meaning they have no arguments. Others have arguments, for the data they carry.</p> <div class="sourceCode" id="cb2"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">CheckoutState</span></span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">NoItems</span></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">HasItems</span> (<span class="dt">NonEmpty</span> <span class="dt">CartItem</span>)</span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">NoCard</span> (<span class="dt">NonEmpty</span> <span class="dt">CartItem</span>)</span> <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">CardSelected</span> (<span class="dt">NonEmpty</span> <span class="dt">CartItem</span>)</span> <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">Card</span></span> <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">CardConfirmed</span> (<span class="dt">NonEmpty</span> <span class="dt">CartItem</span>)</span> <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">Card</span></span> <span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">OrderPlaced</span></span> <span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span> (<span class="dt">Show</span>, <span class="dt">Eq</span>)</span></code></pre></div> <p>Looking at the state constructors in the definition of <code>CheckoutState</code>, we can see how they accumulate state as the machine makes progress, right up until the order is placed.</p> <p>Note that <code>CartItem</code> and <code>Card</code> are imported from the shared <code>Checkout</code> module.</p> <p>Similar to the data type for states, the data type for events, called <code>CheckoutEvent</code>, defines one constructor for each valid event. The non-nullary constructors carry some data with the event.</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">CheckoutEvent</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">Select</span> <span class="dt">CartItem</span></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Checkout</span></span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">SelectCard</span> <span class="dt">Card</span></span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Confirm</span></span> <span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">PlaceOrder</span></span> <span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Cancel</span></span> <span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span> (<span class="dt">Show</span>, <span class="dt">Eq</span>)</span></code></pre></div> <p>We have now translated the diagram to Haskell data types, and we can implement the state transitions and actions.</p> <h2 id="a-pure-state-reducer-function">A Pure State Reducer Function</h2> <p>Now, we might consider the simplest possible implementation of a state machine a function from state and event to the next state, very much like the definition from Erlang’s documentation quoted above. Such a function could have the following type:</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">CheckoutState</span> <span class="ot">-&gt;</span> <span class="dt">CheckoutEvent</span> <span class="ot">-&gt;</span> <span class="dt">CheckoutState</span></span></code></pre></div> <p>In a state machine that itself can be regarded a pure function, such as a parser, or a calculator, the above signature would be fine. For our purposes, however, we need to interleave side effects with state transitions. We might want to validate that the selected items exist using external database queries, and send requests to a third-party payment provider when placing the order.</p> <h2 id="reaching-for-io">Reaching for IO</h2> <p>Some systems built around the concept of a state reducer function, such as <a href="https://guide.elm-lang.org/architecture/">The Elm Architecture</a> or <a href="https://github.com/alexmingoia/purescript-pux">Pux</a>, support a way of specifying the side effects together with the next state. A starting point to achieve this in Haskell, for our checkout state machine, is the following type signature:</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>checkout</span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">CheckoutState</span></span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">CheckoutEvent</span></span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">CheckoutState</span></span></code></pre></div> <p>A state transition then returns <code>IO</code> of the next state, meaning that we can interleave side effects with transitions. We create a type alias for such a function type, named <code>FSM</code>.</p> <div class="sourceCode" id="cb6"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">FSM</span> s e <span class="ot">=</span></span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> s <span class="ot">-&gt;</span> e <span class="ot">-&gt;</span> <span class="dt">IO</span> s</span></code></pre></div> <p>Then we can write the type signature for <code>checkout</code> using our data types as parameters.</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ot">checkout ::</span> <span class="dt">FSM</span> <span class="dt">CheckoutState</span> <span class="dt">CheckoutEvent</span></span></code></pre></div> <p>The definition of <code>checkout</code> pattern matches on the current state and the event. The first five cases simply builds up the state values based on the events, and transitions appropriately. We could add validation of selected items, and validation of the selected credit card, but we would then need explicit error states, or terminate the entire state machine on such invalid inputs. I’ll err on the side of keeping this example simple.</p> <div class="sourceCode" id="cb8"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>checkout <span class="dt">NoItems</span> (<span class="dt">Select</span> item) <span class="ot">=</span></span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">HasItems</span> (item <span class="op">:|</span> []))</span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>checkout (<span class="dt">HasItems</span> items) (<span class="dt">Select</span> item) <span class="ot">=</span></span> <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">HasItems</span> (item <span class="op">&lt;|</span> items))</span> <span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>checkout (<span class="dt">HasItems</span> items) <span class="dt">Checkout</span> <span class="ot">=</span></span> <span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">NoCard</span> items)</span> <span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>checkout (<span class="dt">NoCard</span> items) (<span class="dt">SelectCard</span> card) <span class="ot">=</span></span> <span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">CardSelected</span> items card)</span> <span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a></span> <span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>checkout (<span class="dt">CardSelected</span> items card) <span class="dt">Confirm</span> <span class="ot">=</span></span> <span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="dt">CardConfirmed</span> items card)</span></code></pre></div> <p>Remember the state diagram? The nested “Checkout” machine accepts the “cancel” event in all its states, and so does our implementation. We switch on the current state, and cancel in the correct ones, otherwise remaining in the current state.</p> <div class="sourceCode" id="cb9"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>checkout state <span class="dt">Cancel</span> <span class="ot">=</span></span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> state <span class="kw">of</span></span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">NoCard</span> items <span class="ot">-&gt;</span> <span class="fu">return</span> (<span class="dt">HasItems</span> items)</span> <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">CardSelected</span> items _ <span class="ot">-&gt;</span> <span class="fu">return</span> (<span class="dt">HasItems</span> items)</span> <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">CardConfirmed</span> items _ <span class="ot">-&gt;</span> <span class="fu">return</span> (<span class="dt">HasItems</span> items)</span> <span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> _ <span class="ot">-&gt;</span> <span class="fu">return</span> state</span></code></pre></div> <p>To demonstrate how an interleaved side effect is performed, we use the imported <code>chargeCard</code> and <code>calculatePrice</code> to charge the card. The implementations of <code>chargeCard</code> and <code>calculatePrice</code> are not important.</p> <div class="sourceCode" id="cb10"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>checkout (<span class="dt">CardConfirmed</span> items card) <span class="dt">PlaceOrder</span> <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> PaymentProvider.chargeCard card (calculatePrice items)</span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> <span class="dt">OrderPlaced</span></span></code></pre></div> <p>The last case is a fall-through pattern, for unaccepted events in the current state, which effectively has the machine remain in its current state.</p> <div class="sourceCode" id="cb11"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>checkout state _ <span class="ot">=</span> <span class="fu">return</span> state</span></code></pre></div> <p>That is it for <code>checkout</code>, our state reducer function using <code>IO</code>.</p> <h2 id="running-the-state-machine">Running the State Machine</h2> <p>To run our machine, we can rely on <code>foldM</code>. Given a machine, an initial state, and a foldable sequence of events, we get back the terminal state inside <code>IO</code>.</p> <div class="sourceCode" id="cb12"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ot">runFsm ::</span> <span class="dt">Foldable</span> f <span class="ot">=&gt;</span> <span class="dt">FSM</span> s e <span class="ot">-&gt;</span> s <span class="ot">-&gt;</span> f e <span class="ot">-&gt;</span> <span class="dt">IO</span> s</span> <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>runFsm <span class="ot">=</span> foldM</span></code></pre></div> <p>Just getting back the terminal state might be too much of a black box. To see what happens as we run a machine, we can <em>decorate</em> it with logging. The <code>withLogging</code> function runs the state machine it receives as an argument, logs its transition, and returns the next state.</p> <div class="sourceCode" id="cb13"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>withLogging</span> <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> (<span class="dt">Show</span> s, <span class="dt">Show</span> e)</span> <span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">FSM</span> s e</span> <span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">FSM</span> s e</span> <span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>withLogging fsm s e <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> s&#39; <span class="ot">&lt;-</span> fsm s e</span> <span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> printf <span class="st">&quot;- %s × %s → %s\n&quot;</span> (<span class="fu">show</span> s) (<span class="fu">show</span> e) (<span class="fu">show</span> s&#39;)</span> <span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> s&#39;</span></code></pre></div> <p>Combining these building blocks and running them in GHCi, with a list of events as input, we see the transitions logged and our side-effecting <code>chargeCard</code> operation.</p> <pre><code>*StateMachinesWithAdts&gt; runFsm (withLogging checkout) NoItems [ Select (CartItem &quot;potatoes&quot; 23.95) , Select (CartItem &quot;fish&quot; 168.50) , Checkout , SelectCard (Card &quot;0000-0000-0000-0000&quot;) , Confirm , PlaceOrder ] - NoItems × Select (CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95}) → HasItems (CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95} :| []) - HasItems (CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95} :| []) × Select (CartItem {itemId = &quot;fish&quot;, itemPrice = 168.5}) → HasItems (CartItem {itemId = &quot;fish&quot;, itemPrice = 168.5} :| [CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95}]) - HasItems (CartItem {itemId = &quot;fish&quot;, itemPrice = 168.5} :| [CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95}]) × Checkout → NoCard (CartItem {itemId = &quot;fish&quot;, itemPrice = 168.5} :| [CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95}]) - NoCard (CartItem {itemId = &quot;fish&quot;, itemPrice = 168.5} :| [CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95}]) × SelectCard (Card &quot;0000-0000-0000-0000&quot;) → CardSelected (CartItem {itemId = &quot;fish&quot;, itemPrice = 168.5} :| [CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95}]) (Card &quot;0000-0000-0000-0000&quot;) - CardSelected (CartItem {itemId = &quot;fish&quot;, itemPrice = 168.5} :| [CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95}]) (Card &quot;0000-0000-0000-0000&quot;) × Confirm → CardConfirmed (CartItem {itemId = &quot;fish&quot;, itemPrice = 168.5} :| [CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95}]) (Card &quot;0000-0000-0000-0000&quot;) Charging card 0000-0000-0000-0000 $192.45 - CardConfirmed (CartItem {itemId = &quot;fish&quot;, itemPrice = 168.5} :| [CartItem {itemId = &quot;potatoes&quot;, itemPrice = 23.95}]) (Card &quot;0000-0000-0000-0000&quot;) × PlaceOrder → OrderPlaced OrderPlaced</code></pre> <p>Yes, the logging is somewhat verbose, but there we have it; a simplified event-driven state machine using ADTs for states and events. The data types protect us from constructing illegal values, they bring the code closer to our conceptual model, and they make state transitions explicit.</p> <h2 id="side-effects-and-illegal-transitions">Side Effects and Illegal Transitions</h2> <p>This is a great starting point, and as I argued in the introduction of this post, probably the leg on our journey with the highest return on investment. It is, however, still possible to implement illegal state transitions! We would not get any compile-time error bringing our attention to such mistakes. Another concern is that the state machine implementation is tightly coupled with IO, making it hard to test.</p> <p>We could factor out the side effects in <code>checkout</code> using <a href="https://ocharles.org.uk/blog/posts/2016-01-26-transformers-free-monads-mtl-laws.html">MTL-style type classes or free monads</a>, , or perhaps using <a href="https://hackage.haskell.org/package/extensible-effects-1.11.1.0">extensible-effects</a>. That said, in the next post I will show you a technique to tackle both the side effect and testability concerns, using MTL-style abstraction of the state machine itself.</p> <p>Why don’t you go on and <a href="/2017-11-19-finite-state-machines-part-2.html">read part 2</a> next!</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2017-10-27-motor-finite-state-machines-haskell.html</id>
    <title>Motor: Finite-State Machines in Haskell</title>
    <link href="https://wickstrom.tech/2017-10-27-motor-finite-state-machines-haskell.html"/>
    <published>2017-10-27T00:00:00+02:00</published>
    <updated>2017-10-27T00:00:00+02:00</updated>
    <summary>While writing my talk “Finite-state machines? Your compiler wants in!”,
I have worked on porting the Idris ST library to Haskell. I call it
Motor.</summary>
    <content type="html"><![CDATA[
<p>While writing my talk “Finite-state machines? Your compiler wants in!”, I have worked on porting the Idris <a href="http:/docs.idris-lang.org/en/latest/st/state.html">ST</a> library to Haskell. I call it <em>Motor</em>.</p> <p>Motor is an <em>experimental</em> Haskell library for building finite-state machines with type-safe transitions and effects. I have just published it on <a href="http://hackage.haskell.org/package/motor">Hackage</a>, written a bunch of documentation with Haddock, and put the source code on <a href="https://github.com/owickstrom/motor">GitHub</a>.</p> <p>This blog post is very similar to the Hackage documentation, and aims to pique your interest. The library and documentation will probably evolve and outdate this description, though.</p> <h2 id="state-machines-using-row-types">State Machines using Row Types</h2> <p>The central finite-state machine abstraction in Motor is the <code>MonadFSM</code> type class. <code>MonadFSM</code> is an <em>indexed monad</em> type class, meaning that it has not one, but <em>three</em> type parameters:</p> <ol type="1"> <li>A <code>Row</code> of input resource states</li> <li>A <code>Row</code> of output resource states</li> <li>A return type (just as in <code>Monad</code>)</li> </ol> <p>The <code>MonadFSM</code> parameter kinds might look a bit scary, but they state the same:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">IxMonad</span> m <span class="ot">=&gt;</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">MonadFSM</span> (<span class="ot">m ::</span> (<span class="dt">Row</span> <span class="op">*</span>) <span class="ot">-&gt;</span> (<span class="dt">Row</span> <span class="op">*</span>) <span class="ot">-&gt;</span> <span class="op">*</span> <span class="ot">-&gt;</span> <span class="op">*</span>) <span class="kw">where</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span></span></code></pre></div> <p>The rows describe how the FSM computation will affect the state of its resources when evaluated. A row is essentially a type-level map, from resource names to state types, and the FSM computation's rows describe the resource states <em>before</em> and <em>after</em> the computation.</p> <p>An FSM computation <code>newConn</code> that adds a resource named <code>"connection"</code> with state <code>Idle</code> could have the following type:</p> <div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">newConn ::</span> <span class="dt">MonadFSM</span> m <span class="ot">=&gt;</span></span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> m r (<span class="st">&quot;connection&quot;</span> <span class="op">::=</span> <span class="dt">Idle</span> <span class="op">:|</span> r) ()</span></code></pre></div> <p>A computation <code>spawnTwoPlayers</code> that adds two resources could have this type:</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">spawnTwoPlayers ::</span> <span class="dt">MonadFSM</span> m <span class="ot">=&gt;</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> m r (<span class="st">&quot;hero2&quot;</span> <span class="op">::=</span> <span class="dt">Standing</span> <span class="op">:|</span> <span class="st">&quot;hero1&quot;</span> <span class="op">::=</span> <span class="dt">Standing</span> <span class="op">:|</span> r) ()</span></code></pre></div> <p>Motor uses the extensible records in <code>Data.OpenRecords</code>, provided by the <a href="https://wiki.haskell.org/CTRex" title="https://wiki.haskell.org/CTRex">CTRex</a> library, for row kinds. Have a look at it's documentation to learn more about the type-level operators available for rows.</p> <h2 id="building-on-indexed-monads">Building on Indexed Monads</h2> <p>As mentioned above, <code>MonadFSM</code> is an indexed monad. It uses the definition from <code>Control.Monad.Indexed</code>, in the <a href="https://hackage.haskell.org/package/indexed-0.1.3">indexed</a> package. This means that you can use <code>ibind</code> and friends to compose FSM computations.</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- &#39;c1&#39; and &#39;c2&#39; are FSM computations</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>c1 <span class="op">&gt;&gt;&gt;=</span> \_ <span class="ot">-&gt;</span> c2</span></code></pre></div> <p>You can combine this with the <code>RebindableSyntax</code> language extension to get do-syntax for FSM programs:</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">test ::</span> <span class="dt">MonadFSM</span> m <span class="ot">=&gt;</span> m <span class="dt">Empty</span> <span class="dt">Empty</span> ()</span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>test <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> c1</span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> c2</span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> r <span class="ot">&lt;-</span> c3</span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> c4 r</span> <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> (<span class="op">&gt;&gt;</span>) a <span class="ot">=</span> (<span class="op">&gt;&gt;&gt;=</span>) a <span class="op">.</span> <span class="fu">const</span></span> <span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> (<span class="op">&gt;&gt;=</span>) <span class="ot">=</span> (<span class="op">&gt;&gt;&gt;=</span>)</span></code></pre></div> <p>See <a href="https://ocharles.org.uk/blog/guest-posts/2014-12-06-rebindable-syntax.html" title="https://ocharles.org.uk/blog/guest-posts/2014-12-06-rebindable-syntax.html">24 Days of GHC Extensions: Rebindable Syntax</a> for some more information on how to use <code>RebindableSyntax</code>.</p> <h2 id="state-actions">State Actions</h2> <p>To make it easier to read and write FSM computation types, there is some syntax sugar available.</p> <p><em>State actions</em> allow you to describe state changes of named resources with a <em>single</em> list, as opposed two writing two rows. They also take care of matching the CTRex row combinators with the expectations of Motor, which can be tricky to do by hand.</p> <p>There are three state actions:</p> <ul> <li><code>Add</code> adds a new resource.</li> <li><code>To</code> transitions the state of a resource.</li> <li><code>Delete</code> deletes an existing resource.</li> </ul> <p>A mapping between a resource name is written using the <code>:-&gt;</code> type operator, with a <code>Symbol</code> on the left, and a state action type on the right. Here are some examples:</p> <div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;container&quot;</span> <span class="op">:-&gt;</span> <span class="dt">Add</span> <span class="dt">Empty</span></span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;list&quot;</span> <span class="op">:-&gt;</span> <span class="dt">To</span> <span class="dt">Empty</span> <span class="dt">NonEmpty</span></span> <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;game&quot;</span> <span class="op">:-&gt;</span> <span class="dt">Delete</span> <span class="dt">GameEnded</span></span></code></pre></div> <p>So, the list of mappings from resource names to state actions describe what happens to each resource. Together with an initial row of resources <code>r</code>, and a return value <code>a</code>, we can declare the type of an FSM computation using the <code>Actions</code> type:</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">MonadFSM</span> m <span class="ot">=&gt;</span> <span class="dt">Actions</span> m &#39;[ n1 <span class="op">:-&gt;</span> a1, n2 <span class="op">:-&gt;</span> a2, <span class="op">...</span> ] r a</span></code></pre></div> <p>A computation that adds two resources could have the following type:</p> <div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">addingTwoThings ::</span></span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">MonadFSM</span> m <span class="ot">=&gt;</span></span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">Actions</span> m &#39;[ <span class="st">&quot;container&quot;</span> <span class="op">:-&gt;</span> <span class="dt">Add</span> <span class="dt">Empty</span></span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> , <span class="st">&quot;game&quot;</span> <span class="op">:-&gt;</span> <span class="dt">Add</span> <span class="dt">Started</span></span> <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> ] r ()</span></code></pre></div> <h2 id="infix-operators">Infix Operators</h2> <p>As an alternative to the <code>Add</code>, <code>To</code>, and <code>Delete</code> types, Motor offers infix operator aliases. These start with <code>!</code> to indicate that they can be effectful.</p> <p>The <code>!--&gt;</code> operator is an infix alias for <code>To</code>:</p> <div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ot">useStateMachines ::</span></span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">MonadFSM</span> m <span class="ot">=&gt;</span></span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">Actions</span> m &#39;[ <span class="st">&quot;program&quot;</span> <span class="op">:-&gt;</span> <span class="dt">NotCool</span> <span class="op">!--&gt;</span> <span class="dt">Cool</span> ] r ()</span></code></pre></div> <p>The <code>!+</code> and <code>!-</code> are infix aliases for mappings from resource names to <code>Add</code> and <code>Delete</code> state actions, respectively:</p> <div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ot">startNewGame ::</span></span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">MonadFSM</span> m <span class="ot">=&gt;</span></span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">Actions</span> m &#39;[ <span class="st">&quot;game&quot;</span> <span class="op">!+</span> <span class="dt">Started</span> ] r ()</span></code></pre></div> <div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="ot">endGameWhenWon ::</span></span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">MonadFSM</span> m <span class="ot">=&gt;</span></span> <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">Actions</span> m &#39;[ <span class="st">&quot;game&quot;</span> <span class="op">!-</span> <span class="dt">Won</span> ] r ()</span></code></pre></div> <h2 id="row-polymorphism">Row Polymorphism</h2> <p>Because of how CTRex works, FSM computations that have a free variable as their input row of resources, i.e. that are polymorphic in the sense of other resource states, must list <em>all their actions in reverse order</em>.</p> <div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ot">doFourThings ::</span></span> <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">Game</span> m</span> <span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">Actions</span> m &#39;[ <span class="st">&quot;hero2&quot;</span> <span class="op">!-</span> <span class="dt">Standing</span></span> <span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> , <span class="st">&quot;hero1&quot;</span> <span class="op">!-</span> <span class="dt">Standing</span></span> <span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a> , <span class="st">&quot;hero2&quot;</span> <span class="op">!+</span> <span class="dt">Standing</span></span> <span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a> , <span class="st">&quot;hero1&quot;</span> <span class="op">!+</span> <span class="dt">Standing</span></span> <span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a> ] r ()</span> <span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>doFourThings <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a> spawn hero1</span> <span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a> spawn hero2</span> <span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a> perish hero1</span> <span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a> perish hero</span> <span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a></span> <span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> <span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a> (<span class="op">&gt;&gt;</span>) a <span class="ot">=</span> (<span class="op">&gt;&gt;&gt;=</span>) a <span class="op">.</span> <span class="fu">const</span></span> <span id="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a> (<span class="op">&gt;&gt;=</span>) <span class="ot">=</span> (<span class="op">&gt;&gt;&gt;=</span>)</span></code></pre></div> <p>This is obviously quite clumsy. If anyone has ideas on how to fix or work around it, <em>please get in touch</em>. Had the <code>r</code> been replaced by <code>Empty</code> in the type signature above, it could have had type <code>NoActions m Empty ()</code> instead.</p> <h2 id="running-the-state-machine">Running the State Machine</h2> <p>The <code>runFSM</code> function in <code>Motor.FSM</code> runs an FSM computation in some base monad:</p> <div class="sourceCode" id="cb13"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="ot">runFSM ::</span> <span class="dt">Monad</span> m <span class="ot">=&gt;</span> <span class="dt">FSM</span> m <span class="dt">Empty</span> <span class="dt">Empty</span> a <span class="ot">-&gt;</span> m a</span></code></pre></div> <p><code>FSM</code> has instances for <code>IxMonadTrans</code> and a bunch of other type classes. More might be added as they are needed.</p> <h2 id="examples">Examples</h2> <p>There is only <a href="https://github.com/owickstrom/motor/blob/master/motor/examples/Door.hs">one small Door example</a> in the repository, along with some test programs. I haven’t had much time to write examples, but hopefully I will soon. The door example does feature most of the relevant concepts, though.</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2017-09-24-automating-the-build-of-your-technical-presentation.html</id>
    <title>Automating the Build of your Technical Presentation</title>
    <link href="https://wickstrom.tech/2017-09-24-automating-the-build-of-your-technical-presentation.html"/>
    <published>2017-09-24T00:00:00+02:00</published>
    <updated>2017-09-24T00:00:00+02:00</updated>
    <summary>Writing technical presentations that include code samples and diagrams
can be really tedious. In mainstream presentation software, such as
Keynote and PowerPoint, your workflow is likely to manually
copy-and-paste source code from your editor into your slides. If you’re
not using the drawing capabilities of your presentation software, you
have to perform similar steps to include diagrams.</summary>
    <content type="html"><![CDATA[
<p>Writing technical presentations that include code samples and diagrams can be really tedious. In mainstream presentation software, such as Keynote and PowerPoint, your workflow is likely to manually copy-and-paste source code from your editor into your slides. If you’re not using the drawing capabilities of your presentation software, you have to perform similar steps to include diagrams.</p> <p>In my process of writing a technical presentation, code samples and diagrams are not written first, and included in the slides at the last minute – I work iteratively on slide content, source code, and diagrams, all at the same time. Having to repeat the time-consuming and error-prone process of updating code samples in slides, each time my original source code changes, breaks my creative flow completely. I also want to have my source code <em>compiled and executable</em>, so that I can be confident it is correct.</p> <p>The main features I’m looking for in a technical presentation setup includes:</p> <ul> <li>Text-based sources for everything (slides, code samples, diagrams, presentation template, styling, and build script)</li> <li>The ability to include sections of external source code files into slides</li> <li>Repeatable and fully-automated builds</li> <li>PDF output with and without notes</li> </ul> <p>I’m less interested in:</p> <ul> <li>Slide transitions and animation</li> <li>Videos and GIFs</li> </ul> <p>This article demonstrates a setup that fulfills these goals, using Pandoc Markdown, Beamer, Graphviz and Make. I have also created <a href="https://github.com/owickstrom/automating-the-build-of-your-technical-presentation-template">a template</a> based on my setup, that you can use if you like this approach.</p> <h2 id="writing-slides-with-pandoc-markdown">Writing Slides with Pandoc Markdown</h2> <p>One of my favorite tools in technical writing is <a href="https://pandoc.org">Pandoc</a>. I use it for documentation, talks, Markdown preview, this website, and for converting existing documents to more desirable formats<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</p> <p>A very nice feature of Pandoc is slideshow output formats. You can write your slides in Markdown using regular headings, with the slide content below:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="an">title:</span><span class="co"> My Awesome Topic</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="an">subtitle:</span><span class="co"> Ramblings on the Subject</span></span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="an">author:</span><span class="co"> Alice</span></span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="an">date:</span><span class="co"> September 2017</span></span> <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span> <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="fu"># Introduction</span></span> <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="ss">- </span>Something</span> <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="ss">- </span>Another thing</span> <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="ss">- </span>The last one</span> <span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="fu"># I Can LaTeX</span></span> <span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>\centerline{\Large{\textit{I can embed LaTeX as well.}}}</span> <span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="fu"># A Program</span></span> <span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="in">``` javascript</span></span> <span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">coolTools</span>() {</span> <span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> [<span class="st">&quot;pandoc&quot;</span><span class="op">,</span> <span class="st">&quot;beamer&quot;</span>]<span class="op">;</span></span> <span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a>}</span> <span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a><span class="in">```</span></span></code></pre></div> <p>Build the LaTeX source code using Pandoc and the <code>beamer</code> target, and then generate the PDF using <code>pdflatex</code>:</p> <pre class="shell"><code>pandoc -t beamer -o slides.tex slides.md pdflatex slides.tex</code></pre> <p>Voilà! You have a PDF, such as <a href="https://wickstrom.tech/assets/slides-no-notes.pdf">this one</a>.</p> <p>You might want to customize some of the Beamer styling, which is done by including a <code>.tex</code> file using the <code>-H</code> command line parameter of Pandoc. The full template described below uses this technique to change the styling.</p> <h2 id="including-source-code-from-external-files">Including Source Code from External Files</h2> <p>As stated in the introduction of this article, I want my source code samples to compile, and possibly be executable. If I have to write code directly in the slides, I will most likely make mistakes, and there will be no compiler or toolchain to tell me about it.</p> <p>There are a number of ways to include code from external files with Pandoc, but I will shamelessly refer to my own filter called <a href="https://github.com/owickstrom/pandoc-include-code">pandoc-include-code</a>, which I use extensively. To include a source code file, write an empty fenced code block and use the <code>include</code> attribute to specify the path to the external file:</p> <div class="sourceCode" id="cb3"><pre class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="in">``` {.javascript include=my-program.js}</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="in">```</span></span></code></pre></div> <p>Now, suppose you have a Haskell program in a file <code>Sample.hs</code>.</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">Sample</span> <span class="kw">where</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Animal</span> <span class="ot">=</span> <span class="dt">Dog</span> <span class="op">|</span> <span class="dt">Cat</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="ot">isAfraidOf ::</span> <span class="dt">Animal</span> <span class="ot">-&gt;</span> <span class="dt">Animal</span> <span class="ot">-&gt;</span> <span class="dt">Bool</span></span> <span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>isAfraidOf <span class="dt">Cat</span> <span class="dt">Dog</span> <span class="ot">=</span> <span class="dt">True</span></span> <span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>isAfraidOf _ _ <span class="ot">=</span> <span class="dt">False</span></span> <span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="ot">result ::</span> <span class="dt">Bool</span></span> <span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>result <span class="ot">=</span> <span class="dt">Dog</span> <span class="ot">`isAfraidOf`</span> <span class="dt">Cat</span></span></code></pre></div> <p>The issue is you want to include just the <code>Animal</code> data type and the <code>isAfraidOf</code> definition, not the top module declaration and the <code>result</code> definition. By wrapping the content in two special comments, <code>start snippet &lt;name&gt;</code> and <code>end snippet &lt;name&gt;</code>, you create a named snippet:</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">Sample2</span> <span class="kw">where</span></span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- start snippet animals</span></span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Animal</span> <span class="ot">=</span> <span class="dt">Dog</span> <span class="op">|</span> <span class="dt">Cat</span></span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="ot">isAfraidOf ::</span> <span class="dt">Animal</span> <span class="ot">-&gt;</span> <span class="dt">Animal</span> <span class="ot">-&gt;</span> <span class="dt">Bool</span></span> <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>isAfraidOf <span class="dt">Cat</span> <span class="dt">Dog</span> <span class="ot">=</span> <span class="dt">True</span></span> <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>isAfraidOf _ _ <span class="ot">=</span> <span class="dt">False</span></span> <span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- end snippet animals</span></span> <span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="ot">result ::</span> <span class="dt">Bool</span></span> <span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>result <span class="ot">=</span> <span class="dt">Dog</span> <span class="ot">`isAfraidOf`</span> <span class="dt">Cat</span></span></code></pre></div> <p>In the Markdown source, refer to the snippet’s name when including:</p> <div class="sourceCode" id="cb6"><pre class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="in">``` {.haskell include=Sample2.hs snippet=animals}</span></span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="in">```</span></span></code></pre></div> <p>The included code will be only that in your snippet:</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Animal</span> <span class="ot">=</span> <span class="dt">Dog</span> <span class="op">|</span> <span class="dt">Cat</span></span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="ot">isAfraidOf ::</span> <span class="dt">Animal</span> <span class="ot">-&gt;</span> <span class="dt">Animal</span> <span class="ot">-&gt;</span> <span class="dt">Bool</span></span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>isAfraidOf <span class="dt">Cat</span> <span class="dt">Dog</span> <span class="ot">=</span> <span class="dt">True</span></span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>isAfraidOf _ _ <span class="ot">=</span> <span class="dt">False</span></span></code></pre></div> <p>You can still compile the code, load it in the REPL, and write tests for it, while including interesting parts into your slides. You are not depending on specific line number ranges, which of course becomes a nightmare once you edit your source code.</p> <p>The last feature of <a href="https://github.com/owickstrom/pandoc-include-code">pandoc-include-code</a> I want to demonstrate is the <code>dedent</code> attribute. Let’s say we have a Javascript file with a class method that you’re interested in:</p> <div class="sourceCode" id="cb8"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Foo {</span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// start snippet bar</span></span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">bar</span>() {</span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="st">&quot;bar&quot;</span><span class="op">;</span></span> <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> }</span> <span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a> <span class="co">// end snippet bar</span></span> <span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <p>When including snippets of indented source code, you might want to “dedent”, i.e. remove extra leading whitespace. This is easily accomplished with the <code>dedent</code> attribute, specifying how many whitespace characters you want removed:</p> <div class="sourceCode" id="cb9"><pre class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="in">``` {.javascript include=sample1.js snippet=bar dedent=2}</span></span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="in">```</span></span></code></pre></div> <p>The included code will be “dedented” to the first column:</p> <div class="sourceCode" id="cb10"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bar</span>() {</span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="st">&quot;bar&quot;</span><span class="op">;</span></span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div> <h2 id="generating-diagrams">Generating Diagrams</h2> <p>Often I want a couple of diagrams in a presentation, to illustrate some design or flow in a program. I enjoy generating diagrams from plain text sources, instead of drawing by hand or using special drawing software with binary formats. Both <a href="http://graphviz.org">Graphviz</a> and <a href="http://plantuml.com">PlantUML</a> are powerful tools that are relatively easy to integrate with the presentation build in a Makefile.</p> <p>Let’s say I want to generate a state diagram. The following Graphviz source code generates a simple yet beautiful diagram:</p> <div class="sourceCode" id="cb11"><pre class="sourceCode dot"><code class="sourceCode dot"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">digraph</span> <span class="va">door_states</span> <span class="ot">{</span></span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="va">graph</span><span class="co"> </span><span class="ot">[</span><span class="co"> </span><span class="va">dpi</span><span class="co"> </span><span class="ot">=</span><span class="co"> </span><span class="dv">300</span><span class="co"> </span><span class="ot">];</span></span> <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="va">splines</span><span class="ot">=</span><span class="va">true</span><span class="ot">;</span></span> <span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="va">esep</span><span class="ot">=</span><span class="dv">5</span><span class="ot">;</span></span> <span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="at">rankdir</span><span class="ot">=</span><span class="va">LR</span><span class="ot">;</span></span> <span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="at">size</span><span class="ot">=</span><span class="st">&quot;8,5&quot;</span><span class="ot">;</span></span> <span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="kw">edge</span><span class="co"> </span><span class="ot">[</span><span class="co"> </span><span class="at">fontname</span><span class="co"> </span><span class="ot">=</span><span class="co"> </span><span class="st">&quot;Ubuntu&quot;</span><span class="co"> </span><span class="ot">];</span></span> <span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="kw">node</span><span class="co"> </span><span class="ot">[</span><span class="co"> </span><span class="at">fontname</span><span class="co"> </span><span class="ot">=</span><span class="co"> </span><span class="st">&quot;Ubuntu Bold&quot;</span><span class="co"> </span><span class="ot">];</span></span> <span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a></span> <span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="kw">node</span><span class="co"> </span><span class="ot">[</span><span class="at">shape</span><span class="co"> </span><span class="ot">=</span><span class="co"> </span><span class="va">point</span><span class="co">, </span><span class="at">width</span><span class="co"> </span><span class="ot">=</span><span class="co"> .</span><span class="dv">25</span><span class="co">, </span><span class="at">height</span><span class="co"> </span><span class="ot">=</span><span class="co"> .</span><span class="dv">25</span><span class="co"> </span><span class="ot">];</span></span> <span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="va">Start</span><span class="ot">;</span></span> <span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a></span> <span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="kw">node</span><span class="co"> </span><span class="ot">[</span><span class="at">shape</span><span class="co"> </span><span class="ot">=</span><span class="co"> </span><span class="va">circle</span><span class="ot">];</span></span> <span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="va">Opened</span><span class="ot">;</span></span> <span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="va">Closed</span><span class="ot">;</span></span> <span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a></span> <span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="va">Start</span><span class="co"> </span><span class="ot">-&gt;</span><span class="co"> </span><span class="va">Closed</span></span> <span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="va">Closed</span><span class="co"> </span><span class="ot">-&gt;</span><span class="co"> </span><span class="va">Opened</span><span class="co"> </span><span class="ot">[</span><span class="co"> </span><span class="at">label</span><span class="co"> </span><span class="ot">=</span><span class="co"> </span><span class="st">&quot;open&quot;</span><span class="co"> </span><span class="ot">];</span></span> <span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a><span class="co"> </span><span class="va">Opened</span><span class="co"> </span><span class="ot">-&gt;</span><span class="co"> </span><span class="va">Closed</span><span class="co"> </span><span class="ot">[</span><span class="co"> </span><span class="at">label</span><span class="co"> </span><span class="ot">=</span><span class="co"> </span><span class="st">&quot;close&quot;</span><span class="co"> </span><span class="ot">];</span></span> <span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a><span class="ot">}</span></span></code></pre></div> <p>Generate a PNG file using the <code>dot</code> command:</p> <pre class="shell"><code>dot -Tpng -o door.png door.dot</code></pre> <p>The generated PNG image looks like this:</p> <figure> <img src="/assets/door.png" alt="The state diagram generated by Graphviz." /> <figcaption aria-hidden="true">The state diagram generated by Graphviz.</figcaption> </figure> <p>To automate this process with Make, you can find all <code>.dot</code> files, transform those paths into a list of target paths, and have Make run the <code>dot</code> command to generate all targets.</p> <div class="sourceCode" id="cb13"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="dt">DIAGRAM_SRCS</span><span class="ch">=$(</span><span class="kw">shell</span><span class="st"> find src -name &#39;*.dot&#39;</span><span class="ch">)</span></span> <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="dt">DIAGRAMS</span><span class="ch">=$(</span><span class="dt">DIAGRAM_SRCS</span><span class="kw">:</span><span class="ss">src/%.dot</span><span class="kw">=</span><span class="ss">target/%.png</span><span class="ch">)</span></span> <span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> all</span></span> <span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="dv">all:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DIAGRAMS</span><span class="ch">)</span></span> <span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a><span class="dv">target/%.png:</span><span class="dt"> src/%.dot</span></span> <span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a><span class="er"> </span>mkdir -p <span class="ch">$(</span><span class="kw">shell</span><span class="st"> dirname </span><span class="ch">$@)</span></span> <span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a> dot -Tpng <span class="ch">$&lt;</span> -o <span class="ch">$@</span></span></code></pre></div> <p>A similar setup can be used with <a href="http://plantuml.com">PlantUML</a>, although you might want the JAR file to download automatically:</p> <div class="sourceCode" id="cb14"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="dt">PLANTUML</span><span class="ch">=</span><span class="st">deps/plantuml.jar</span></span> <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="dt">UML_SRCS</span><span class="ch">=$(</span><span class="kw">shell</span><span class="st"> find src -name &#39;*.uml.txt)</span></span> <span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="st">UMLS=</span><span class="ch">$(</span><span class="dt">UML_SRCS</span><span class="kw">:</span><span class="ss">src/%.uml.txt</span><span class="kw">=</span><span class="ss">target/%.png</span><span class="ch">)</span></span> <span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a><span class="st">.PHONY: all</span></span> <span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a><span class="st">all: </span><span class="ch">$(</span><span class="dt">UMLS</span><span class="ch">)</span></span> <span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a><span class="st">target/%.png: src/%.uml.txt </span><span class="ch">$(</span><span class="dt">PLANTUML</span><span class="ch">)</span></span> <span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a><span class="st"> mkdir -p </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> dirname </span><span class="ch">$@)</span></span> <span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a><span class="st"> cat </span><span class="ch">$&lt;</span><span class="st"> | java -jar </span><span class="ch">$(</span><span class="dt">PLANTUML</span><span class="ch">)</span><span class="st"> -tpng -pipe &gt; </span><span class="ch">$@</span></span> <span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a></span> <span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a><span class="ch">$(</span><span class="dt">PLANTUML</span><span class="ch">)</span><span class="st">:</span></span> <span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a><span class="st"> mkdir -p </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> dirname </span><span class="ch">$@)</span></span> <span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a><span class="st"> wget http://sourceforge.net/projects/plantuml/files/plantuml.jar/download -O </span><span class="ch">$@</span></span></code></pre></div> <p>I have used PlantUML in this blog, just as described above, to generate diagrams for posts. See the post <a href="/programming/2017/01/06/hyper-elegant-weapons-for-a-more-civilized-page.html">Hyper: Elegant Weapons for a More Civilized Page</a> for an example.</p> <h2 id="wrapping-up">Wrapping Up</h2> <p>Based on the techniques described in this post, I have created a template that you can use for your own presentations. It is <a href="https://github.com/owickstrom/automating-the-build-of-your-technical-presentation-template">published at GitHub</a>. I hope this will be useful to someone, and that it can be a good complement to this article.</p> <p>What I really like about the tools and techniques demonstrated in this article is that they are not limited to writing presentations. I use the same tools for writing documentation, and for writing this blog. Pandoc is an amazing piece of software, and I have just scratched the surface of what it can do. For instance, if you do not want PDF output for your talk, there’s a number of Javascript-based formats for slideshows available.</p> <p>Now go on and write some cool tech talks!</p> <h2 id="footnotes">Footnotes</h2> <section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"> <hr /> <ol> <li id="fn1"><p>I once needed to convert a technical manual from ODF to reStructuredText. A single Pandoc command later, and I had the sources for a proper Sphinx build.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li> </ol> </section>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2017-06-05-tagless-final-encoding-of-a-test-language.html</id>
    <title>Tagless Final Encoding of a Test Language</title>
    <link href="https://wickstrom.tech/2017-06-05-tagless-final-encoding-of-a-test-language.html"/>
    <published>2017-06-05T00:00:00+02:00</published>
    <updated>2017-06-05T00:00:00+02:00</updated>
    <summary>I have experimented with a test language encoded in tagless final style,
instead of algebraic data types, to support the typed combinators
beforeEach and beforeAll. Although the intended use is for PureScript
Spec, I want to share the Haskell prototype I ended up with, and explain
how I got there.</summary>
    <content type="html"><![CDATA[
<p>I have experimented with a test language encoded in tagless final style, instead of algebraic data types, to support the typed combinators <em>beforeEach</em> and <em>beforeAll</em>. Although the intended use is for <a href="https://purescript-spec.github.io/purescript-spec/">PureScript Spec</a>, I want to share the Haskell prototype I ended up with, and explain how I got there.</p> <h2 id="the-algebraic-data-type-approach">The Algebraic Data Type Approach</h2> <p>The PureScript Spec project, inspired by Haskell’s <a href="http://hspec.github.io/">hspec</a>, provides an EDSL and framework for writing, organizing, and running PureScript tests. Combinators use a State monad collecting tests into an algebraic data structure, representing the test language tree structure.</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>describe <span class="st">&quot;My Module&quot;</span> <span class="op">$</span> <span class="kw">do</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> describe <span class="st">&quot;Feature #1&quot;</span> <span class="op">$</span> <span class="kw">do</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> it <span class="st">&quot;does addition&quot;</span> (<span class="dv">1</span> <span class="op">+</span> <span class="dv">1</span> <span class="ot">`shouldEqual`</span> <span class="dv">2</span>)</span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> it <span class="st">&quot;does subtraction&quot;</span> (<span class="dv">1</span> <span class="op">-</span> <span class="dv">1</span> <span class="ot">`shouldEqual`</span> <span class="dv">0</span>)</span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> describe <span class="st">&quot;Feature #2&quot;</span></span> <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> it <span class="st">&quot;does multiplication&quot;</span> (<span class="dv">2</span> <span class="op">*</span> <span class="dv">2</span> <span class="ot">`shouldEqual`</span> <span class="dv">4</span>)</span></code></pre></div> <p>The <code>Group</code> data type holds <em>describe</em> groups and <em>it</em> tests, here shown in a simplified form, and translated to Haskell. The test <em>body</em> has the parameterized type <code>t</code>, making the <code>Group</code> structure suitable for representing not only tests to be run, but also for test results.</p> <div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Group</span> t</span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">Describe</span> <span class="dt">String</span> [<span class="dt">Group</span> t]</span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">It</span> <span class="dt">String</span> t</span></code></pre></div> <p>A test suite to be run can have type <code>[Group (IO ())]</code>, and a test result can have type <code>[Group TestResult]</code>.</p> <p>In a GitHub pull request for PureScript Spec, we discussed how to implement setup and tear-down functions around tests, and how to make them type safe. I started poking around in the code base, but soon realized that the required change was larger than I first imagined, and so I began on a clean slate prototype. The reason I used Haskell was to focus more on modeling different alternatives, and less time on hand-written instances for newtypes.</p> <p>I wanted a setup function to provide a return value, and all tests run with the setup to receive the return value as a parameter. Thus, <em>n</em> setup functions would require test functions of <em>n</em> arguments. A test with an incorrect number of arguments would give a type error at compile-time.</p> <p>My first attempt was to extend the current design by adding a new constructor <code>BeforeEach</code> to the <code>Group</code> data type. Using the already parameterized test body, tests inside a <code>BeforeEach</code> would be functions from the return value of type <code>b</code>, to some test body of type <code>t</code>. For each nesting of <code>BeforeEach</code>, test body types would get an additional argument.</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Group</span> b t</span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">Describe</span> <span class="dt">String</span> [<span class="dt">Group</span> b t]</span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">It</span> <span class="dt">String</span> t</span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">BeforeEach</span> (<span class="dt">IO</span> b) (<span class="dt">Group</span> b (b <span class="ot">-&gt;</span> t))</span></code></pre></div> <p>While this structure can hold multiple nested <code>BeforeEach</code> values, and enforce the correct number of arguments to <code>It</code> body functions, the type <code>b</code> cannot vary throughout the structure. Requiring all setup functions in a test suite to return values of the same type was not acceptable. I suspect that there might be a way to solve this in Haskell using GADTs and existential types, but I’m not sure how it would translate to PureScript.</p> <p>Following the idea of parameterizing <code>Group</code> further, I’d probably end up close to a specialized version of the Free monad. <a href="http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html">Why free monads matter</a> explains a similar path, arriving at the Free monad, most eloquently. I decided, however, to try out a <a href="http://okmij.org/ftp/tagless-final/index.html">tagless final style</a> encoding for the test language in my Haskell prototype.</p> <h2 id="exploring-tagless-final-encoding">Exploring Tagless Final Encoding</h2> <p>Having kept an eye out for practical examples of tagless final style, I was keen on trying it out for the test language. The discussion started on a local meetup in Malmö, where I presented the problem, together with my suspicion that a combination of tagless final style encoding and Applicative would solve it elegantly. The following design is the result of my own explorations after the meetup, and will hopefully be of use for the real implementation in PureScript Spec in the future.</p> <p>We begin by declaring a bunch of language extensions, the module, and our imports.</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE DeriveFunctor #-}</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE FlexibleContexts #-}</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE FlexibleInstances #-}</span></span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE GeneralizedNewtypeDeriving #-}</span></span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE MultiParamTypeClasses #-}</span></span> <span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">Test.Spec</span> <span class="kw">where</span></span> <span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a></span> <span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Monad.Identity</span></span> <span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Monad.IO.Class</span></span> <span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Monad.Writer</span></span> <span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Function</span></span> <span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.List</span></span> <span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">System.IO.Memoize</span></span></code></pre></div> <p>Encoding the test language in tagless final style means that all operations are overloaded functions in a type class. <code>MonadSpec</code> takes two type arguments; <code>m</code> and <code>f</code>, constrained to <code>Monad</code> and <code>Applicative</code>, respectively. The class includes the operations <code>it</code>, <code>describe</code>, <code>beforeEach</code>, and <code>beforeAll</code>.</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> (<span class="dt">Monad</span> m, <span class="dt">Applicative</span> f) <span class="ot">=&gt;</span> <span class="dt">MonadSpec</span> m f <span class="kw">where</span></span></code></pre></div> <p>The operations of the type class constitute the whole language. Beginning with the leaf operation <code>it</code>, we see that it takes a string description, some test of type <code>a</code>, and returns a <code>Spec</code> parameterized by <code>m</code> and <code>(f a)</code>.</p> <div class="sourceCode" id="cb6"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ot"> it ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">Spec</span> m (f a)</span></code></pre></div> <p>Having the test, a value of type <code>a</code>, wrapped up inside the <code>(Applicative f)</code> is essential for our operations to compose.</p> <p>The <code>describe</code> operation takes a string describing a group of tests, and another Spec, with any type of tests, as long as they are inside the <code>(Applicative f)</code>.</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ot"> describe ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Spec</span> m (f a) <span class="ot">-&gt;</span> <span class="dt">Spec</span> m (f a)</span></code></pre></div> <p>The setup combinators <code>beforeEach</code> and <code>beforeAll</code> have identical type signatures. They take a setup value of type <code>(f a)</code>, and a Spec with tests of type <code>(f (a -&gt; b))</code>, returning a Spec with tests of type <code>(f b)</code>. The type shows that the applicative test functions are applied by the setup combinators.</p> <div class="sourceCode" id="cb8"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot"> beforeEach ::</span> f a <span class="ot">-&gt;</span> <span class="dt">Spec</span> m (f (a <span class="ot">-&gt;</span> b)) <span class="ot">-&gt;</span> <span class="dt">Spec</span> m (f b)</span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> beforeAll ::</span> f a <span class="ot">-&gt;</span> <span class="dt">Spec</span> m (f (a <span class="ot">-&gt;</span> b)) <span class="ot">-&gt;</span> <span class="dt">Spec</span> m (f b)</span></code></pre></div> <p>What is a <code>Spec</code>? A Writer monad transformer, collecting <code>Group</code> values. Using an explicit <code>WriterT</code> is needed for the interpreter, explained shortly, to capture nested tests, apply test functions to setup combinators’ return values, and change the type of the test structure during interpretation.</p> <div class="sourceCode" id="cb9"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Spec</span> m a <span class="ot">=</span> <span class="dt">WriterT</span> [<span class="dt">Group</span> a] m ()</span></code></pre></div> <p>The <code>Collector</code> interpreter collects a Spec into a data structure, much like the original approach, but <em>with all test functions fully applied</em> inside the applicative.</p> <div class="sourceCode" id="cb10"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Collector</span> m a <span class="ot">=</span> <span class="dt">Collector</span> {<span class="ot"> unCollector ::</span> m a }</span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span> ( <span class="dt">Functor</span></span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Applicative</span></span> <span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Monad</span></span> <span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> , <span class="dt">MonadIO</span></span> <span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> )</span></code></pre></div> <p>The <code>Group</code> data structure holds the applied test functions, and thus has no constructors for <code>BeforeEach</code> and <code>BeforeAll</code>.</p> <div class="sourceCode" id="cb11"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Group</span> a</span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=</span> <span class="dt">Describe</span> <span class="dt">String</span> [<span class="dt">Group</span> a]</span> <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">It</span> <span class="dt">String</span> a</span> <span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span> (<span class="dt">Functor</span>)</span></code></pre></div> <p>In effect, the <code>Collector</code> interpreter loses information when collecting the test suite as a <code>Group</code> data structure. Other interpreters, e.g. a test suite pretty-printer, would provide its own instance of the <code>beforeEach</code> and <code>beforeAll</code> operations, and retain the setup information for its particular purpose.</p> <p>The <code>MonadSpec</code> instance for the <code>Collector</code> interpreter defines the applicative type parameter as <code>IO</code>. This means that all setup combinator values must have type <code>(IO a)</code>, where the nested test functions have type <code>(IO (a -&gt; b))</code>.</p> <div class="sourceCode" id="cb12"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> (<span class="dt">Monad</span> m, <span class="dt">MonadIO</span> m)</span> <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">MonadSpec</span> (<span class="dt">Collector</span> m) <span class="dt">IO</span> <span class="kw">where</span></span></code></pre></div> <p>The <code>it</code> instance wraps the test body inside the applicative, and reports the <code>Group</code> in the <code>WriterT</code> monad.</p> <div class="sourceCode" id="cb13"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a> it name body <span class="ot">=</span></span> <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> tell [<span class="dt">It</span> name (<span class="fu">return</span> body)]</span></code></pre></div> <p>To wrap test groups in a <code>Describe</code> value, the instance of <code>describe</code> runs the nested <code>WriterT</code> to collect all groups.</p> <div class="sourceCode" id="cb14"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a> describe name spec <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> groups <span class="ot">&lt;-</span> lift <span class="op">$</span> execWriterT spec</span> <span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> tell [<span class="dt">Describe</span> name groups]</span></code></pre></div> <p>The interesting part is the <code>beforeEach</code> instance, where the inner test function is applied using <code>(&lt;*&gt;)</code> and <code>(&amp;)</code>. As the <code>Group</code> data type has an instance of <code>Functor</code>, the application can be mapped recursively over the structure using <code>fmap</code>.</p> <div class="sourceCode" id="cb15"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a> beforeEach setup spec <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> groups <span class="ot">&lt;-</span> lift <span class="op">$</span> execWriterT spec</span> <span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a> tell <span class="op">$</span> <span class="fu">fmap</span> ((<span class="op">&amp;</span>) <span class="op">&lt;$&gt;</span> setup <span class="op">&lt;*&gt;</span>) <span class="op">&lt;$&gt;</span> groups</span></code></pre></div> <p>This is where <code>WriterT</code> must be explicit in the <code>MonadSpec</code> operations. In a previous attempt, I had a <code>MonadWriter</code> constraint on the interpreter, and no mention of <code>WriterT</code> in <code>MonadSpec</code>. That approach prohibited the monoidal type of <code>MonadWriter</code> to change during interpretation, a change required to apply test functions when interpreting setup combinators. Instead, by making <code>WriterT</code> explicit in the <code>MonadSpec</code> operations, the <code>Collector</code> instance can collect groups using <code>lift</code> and <code>execWriterT</code>, and freely change the test function types.</p> <p>As a slight variation on <code>beforeEach</code>, the <code>beforeAll</code> combinator must only run the setup action once, applying all test functions with the same return value. Using the <a href="https://hackage.haskell.org/package/io-memoize">io-memoize</a> package, and the existing <code>beforeEach</code> combinator, we can do this neat trick:</p> <div class="sourceCode" id="cb16"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a> beforeAll setup spec <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a> s <span class="ot">&lt;-</span> liftIO <span class="op">$</span> once setup</span> <span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> beforeEach s spec</span></code></pre></div> <p>Collecting all tests, fully applied with setup return values, is a matter of running the <code>WriterT</code> and <code>Collector</code> instances, and joining the applicative values with the test body values, here constrained to be the same monadic type <code>m</code>. Note that Applicative is a super class of Monad since GHC 7.10.</p> <div class="sourceCode" id="cb17"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>collect</span> <span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Monad</span> m</span> <span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">Spec</span> (<span class="dt">Collector</span> m) (m (m a))</span> <span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m [<span class="dt">Group</span> (m a)]</span> <span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>collect spec <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a> groups <span class="ot">&lt;-</span> unCollector <span class="op">$</span> execWriterT spec</span> <span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (<span class="fu">map</span> (<span class="fu">fmap</span> join) groups)</span></code></pre></div> <p>Using <code>collect</code>, the <code>run</code> function traverses the group of tests, printing and running all tests, in the <code>(MonadIO m)</code> instance.</p> <div class="sourceCode" id="cb18"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="ot">run ::</span> <span class="dt">MonadIO</span> m <span class="ot">=&gt;</span> <span class="dt">Spec</span> (<span class="dt">Collector</span> m) (m (m ())) <span class="ot">-&gt;</span> m ()</span> <span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>run spec <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> groups <span class="ot">&lt;-</span> collect spec</span> <span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">mapM_</span> (go []) groups</span> <span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> <span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a> go ctx (<span class="dt">Describe</span> name groups) <span class="ot">=</span></span> <span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">mapM_</span> (go (ctx <span class="op">++</span> [name])) groups</span> <span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a> go ctx (<span class="dt">It</span> name body) <span class="ot">=</span> <span class="kw">do</span></span> <span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> heading <span class="ot">=</span> intercalate <span class="st">&quot; &gt; &quot;</span> ctx <span class="op">++</span> <span class="st">&quot; &gt; it &quot;</span> <span class="op">++</span> name</span> <span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a> liftIO <span class="op">$</span> <span class="fu">putStrLn</span> heading</span> <span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a> body</span></code></pre></div> <p>We can now nest setup combinators, describe groupings, and tests, with different types, while retaining type safety. The test suite type signature is somewhat verbose, but that could be hidden with a type alias. The only drawback, as I see it, is that the outer setup combinators provide the right-most test function parameters, which feels a bit backwards from a user point of view.</p> <div class="sourceCode" id="cb19"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="ot">mySpec ::</span> <span class="dt">MonadSpec</span> m <span class="dt">IO</span> <span class="ot">=&gt;</span> <span class="dt">Spec</span> m (<span class="dt">IO</span> (<span class="dt">IO</span> ()))</span> <span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>mySpec <span class="ot">=</span></span> <span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a> beforeAll (<span class="fu">putStrLn</span> <span class="st">&quot;before all!&quot;</span> <span class="op">&gt;&gt;</span> <span class="fu">return</span> <span class="st">&quot;foo&quot;</span>) <span class="op">$</span> <span class="kw">do</span></span> <span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a> describe <span class="st">&quot;module 1&quot;</span> <span class="op">$</span></span> <span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a> beforeEach (<span class="fu">putStrLn</span> <span class="st">&quot;before each 1!&quot;</span> <span class="op">&gt;&gt;</span> <span class="fu">return</span> <span class="dv">20</span>) <span class="op">$</span></span> <span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a> describe <span class="st">&quot;feature A&quot;</span> <span class="op">$</span> <span class="kw">do</span></span> <span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a> it <span class="st">&quot;works!&quot;</span> (\x y <span class="ot">-&gt;</span> assert (x <span class="op">==</span> <span class="dv">20</span>))</span> <span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a> it <span class="st">&quot;works again!&quot;</span> (\x y <span class="ot">-&gt;</span> assert (y <span class="op">==</span> <span class="st">&quot;foo&quot;</span>))</span> <span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a></span> <span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a> describe <span class="st">&quot;module 2&quot;</span> <span class="op">$</span></span> <span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a> beforeEach (<span class="fu">putStrLn</span> <span class="st">&quot;before each 2!&quot;</span> <span class="op">&gt;&gt;</span> <span class="fu">return</span> <span class="dv">30</span>) <span class="op">$</span></span> <span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a> describe <span class="st">&quot;feature B&quot;</span> <span class="op">$</span> <span class="kw">do</span></span> <span id="cb19-14"><a href="#cb19-14" aria-hidden="true" tabindex="-1"></a> it <span class="st">&quot;works!&quot;</span> (\x y <span class="ot">-&gt;</span> assert (x <span class="op">==</span> <span class="dv">30</span>))</span> <span id="cb19-15"><a href="#cb19-15" aria-hidden="true" tabindex="-1"></a> it <span class="st">&quot;works again!&quot;</span> (\x y <span class="ot">-&gt;</span> assert (y <span class="op">==</span> <span class="st">&quot;foo&quot;</span>))</span> <span id="cb19-16"><a href="#cb19-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> <span id="cb19-17"><a href="#cb19-17" aria-hidden="true" tabindex="-1"></a> assert <span class="dt">True</span> <span class="ot">=</span> <span class="fu">return</span> ()</span> <span id="cb19-18"><a href="#cb19-18" aria-hidden="true" tabindex="-1"></a> assert <span class="dt">False</span> <span class="ot">=</span> <span class="fu">fail</span> <span class="st">&quot;Test failure!&quot;</span></span></code></pre></div> <p>Using <code>IO</code> and <code>fail</code> to report test failures would be less then ideal a real test suite, but it serves our purposes in this experiment. OK, let’s run the test suite, already!</p> <div class="sourceCode" id="cb20"><pre class="sourceCode haskell literate"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span> <span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> run mySpec</span></code></pre></div> <p>Looking at the output, we see that “once, before all!” is printed only once, and that “before each …” is printed for every test.</p> <pre><code>*Test.Spec&gt; :main module 1 &gt; feature A &gt; it works! before all! before each 1! module 1 &gt; feature A &gt; it works again! before each 1! module 2 &gt; feature B &gt; it works! before each 2! module 2 &gt; feature B &gt; it works again! before each 2!</code></pre> <h2 id="summary">Summary</h2> <p>Using tagless final style for embedded languages is fun and powerful. I hope this demonstration can serve as a motivating example for readers interested in EDSLs and tagless final style, in addition to it perhaps influencing or finding its way into the PureScript Spec project. It would also be interesting to explore a Free monad approach, and to compare the two.</p> <h2 id="revisions">Revisions</h2> <p><strong>June 5, 2017:</strong> Based on feedback from <a href="https://twitter.com/mheinzel_">Matthias Heinzel</a>, regarding the order of execution of setup actions, the expression <code>(&lt;*&gt; setup)</code> in the <code>beforeEach</code> instance was changed to <code>((&amp;) &lt;$&gt; setup &lt;*&gt;)</code>.</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2017-01-06-hyper-elegant-weapons-for-a-more-civilized-page.html</id>
    <title>Hyper: Elegant Weapons for a More Civilized Page</title>
    <link href="https://wickstrom.tech/2017-01-06-hyper-elegant-weapons-for-a-more-civilized-page.html"/>
    <published>2017-01-06T00:00:00+01:00</published>
    <updated>2017-01-06T00:00:00+01:00</updated>
    <summary>Since laying Oden aside, I have been getting back into PureScript, and
started working on a project called Hyper that I would like to introduce
you to.</summary>
    <content type="html"><![CDATA[
<p>Since laying Oden aside, I have been getting back into PureScript, and started working on a project called <em>Hyper</em> that I would like to introduce you to.</p> <p>Other than experimenting with extensible records and row typing, and spamming my followers on Twitter, I have tried to <a href="http://hyper.wickstrom.tech">document the design and purpose</a> of the project so that it will be approachable for others. Now, however, I feel that a blog post introducing the project without diving too deep into implementation details would be helpful and interesting.</p> <p><em>If you have any feedback, or like me to write more about Hyper here, please let me know in the comments at the bottom of this page.</em></p> <h2 id="background-motivation">Background &amp; Motivation</h2> <p>I have been working with NodeJS for web servers, on and off, for the last 3-4 years. As projects grow, and even at the start of a project, programming errors creep in around the middleware setup. Many things can go wrong, and they are often hard to debug. You have no static guarantees that third-party middleware and your handlers are correctly setup — that they cover all cases, execute in the correct order, do not interfere with each other, and handle errors properly.</p> <p>After an inspiring talk by Edwin Brady on dependent types for state machines, I decided to try to improve this situation for web server middleware using <em>extensible records</em> and <em>row types</em> in PureScript, to see if I could provide similar safety guarantees to those demonstrated in Idris. It turns out that extensible records work really well for this case!</p> <p>Another thing circling around in my head, to the point of real concern, is the amount of energy that gets directed towards pure functional programming in single-page applications. The ideas are great, and very cool designs emerge, but I am afraid we completely abandon progressive enhancement and “regular” server-side rendered web applications. Thus, I wanted to direct some of the FP love towards good ol’ web server software.</p> <p>I messed around a bit in Haskell with GADTs and phantom types, but PureScript records really stood out, so I decided to go ahead with PureScript. I think it deserves a web server library like the one Hyper aims to become. With alternative PureScript backends emerging, Hyper could also run on Erlang and C++ servers, not only NodeJS.</p> <h2 id="design">Design</h2> <p>The basic building blocks of Hyper are <code>Conn</code> and <code>Middleware</code>. They are heavily inspired by other middleware libraries, like <em>Plug</em> in Elixir and <em>connect</em> in NodeJS. The Conn represents the entirety of an HTTP connection, both the request and response.</p> <div class="sourceCode" id="cb1"><pre class="sourceCode haskell purescript"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Conn</span> req res components <span class="ot">=</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> {<span class="ot"> request ::</span> req</span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> response ::</span> res</span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> components ::</span> components</span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> }</span></code></pre></div> <p>A Conn is a record containing some request <code>req</code>, and some response <code>res</code>. What are those values? Well, it depends on the middleware. Usually they are also records, where middleware specify the fields that they require, or provide, in both the request and response records. The structure of request and response records are <em>open</em>, and the compiler guarantees that the composition of middleware is correct.</p> <p>You might wonder what the purpose of <code>components</code> is. For the sake of this blog post, let us just say that it is a place for user-defined things not directly related to HTTP.</p> <p>The following type requires the request to have <em>at least</em> a body of type <code>String</code>, and headers of type <code>StrMap String</code>. The request can have more fields, this type does not care. The response and components are not constrained at all by this type.</p> <div class="sourceCode" id="cb2"><pre class="sourceCode haskell purescript"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">forall</span> req res c<span class="op">.</span></span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="dt">Conn</span> {<span class="ot"> body ::</span> <span class="dt">String</span></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> ,<span class="ot"> headers ::</span> <span class="dt">StrMap</span> <span class="dt">String</span></span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> req</span> <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> }</span> <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> res</span> <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> c</span></code></pre></div> <p>The second building block, middleware, are simply functions transforming connection values. They take a pure connection, and return another connection inside some type <code>m</code>, where <code>m</code> is usually an Applicative or a Monad.</p> <div class="sourceCode" id="cb3"><pre class="sourceCode haskell purescript"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Middleware</span> m c c&#39; <span class="ot">=</span> c <span class="ot">-&gt;</span> m c&#39;</span></code></pre></div> <p>As middleware are monadic functions, just as the computations used with Bind, they compose using Kleisli composition.</p> <div class="sourceCode" id="cb4"><pre class="sourceCode haskell purescript"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Compose three middleware functions sequentially,</span></span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- from left to right:</span></span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>authenticateUser <span class="op">&gt;=&gt;</span> parseForm <span class="op">&gt;=&gt;</span> saveTodo</span></code></pre></div> <p>As <code>m</code> is a parameter of Conn, you can customize the middleware chain to use a monad stack for tracking state, providing configuration, gathering metrics, and much more.</p> <h2 id="response-state-transitions">Response State Transitions</h2> <p>In Hyper, the state of a response is tracked in its type. This guarantees correctness in response handling, preventing incorrect ordering of headers and body writes, incomplete responses, or other similar mistakes.</p> <p>The contract of state transitions is encoded in a type class implemented by response writers for specific servers. This makes it possible to write reusable functions on top of the protocol that can run with different server implementations.</p> <p>To safe a few keystrokes, and your innocent eyes, let us begin by looking at the type alias for middleware transitioning between response states.</p> <div class="sourceCode" id="cb5"><pre class="sourceCode haskell purescript"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">ResponseStateTransition</span> m rw from to <span class="ot">=</span></span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">forall</span> req res c<span class="op">.</span></span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">Middleware</span></span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> m</span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> (<span class="dt">Conn</span> req {<span class="ot">writer ::</span> rw from <span class="op">|</span> res} c)</span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> (<span class="dt">Conn</span> req {<span class="ot">writer ::</span> rw to <span class="op">|</span> res} c)</span></code></pre></div> <p>Now, on to the encoding of state transitions, the <code>ResponseWriter</code> type class.</p> <div class="sourceCode" id="cb6"><pre class="sourceCode haskell purescript"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">ResponseWriter</span> rw m b <span class="op">|</span> rw <span class="ot">-&gt;</span> b <span class="kw">where</span></span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> writeStatus</span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Status</span></span> <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">ResponseStateTransition</span> m rw <span class="dt">StatusLineOpen</span> <span class="dt">HeadersOpen</span></span> <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> writeHeader</span> <span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Header</span></span> <span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">ResponseStateTransition</span> m rw <span class="dt">HeadersOpen</span> <span class="dt">HeadersOpen</span></span> <span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a></span> <span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> closeHeaders</span> <span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">ResponseStateTransition</span> m rw <span class="dt">HeadersOpen</span> <span class="dt">BodyOpen</span></span> <span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a></span> <span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> send</span> <span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> b</span> <span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">ResponseStateTransition</span> m rw <span class="dt">BodyOpen</span> <span class="dt">BodyOpen</span></span> <span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a></span> <span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a> end</span> <span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">ResponseStateTransition</span> m rw <span class="dt">BodyOpen</span> <span class="dt">ResponseEnded</span></span></code></pre></div> <p>I know, it looks a bit scary with all the types. Stay strong, or have a look at it rendered as a state diagram instead.</p> <figure> <img src="/assets/hyper-states.svg" width="400" alt="State diagram for Hyper responses" /> <figcaption aria-hidden="true">State diagram for Hyper responses</figcaption> </figure> <p>We can write a middleware, based on the state transition functions of the type class, that responds friendly to all requests.</p> <div class="sourceCode" id="cb7"><pre class="sourceCode haskell purescript"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>writeStatus (<span class="dt">Tuple</span> <span class="dv">200</span> <span class="st">&quot;OK&quot;</span>)</span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;=&gt;</span> writeHeader (<span class="dt">Tuple</span> <span class="st">&quot;Content-Type&quot;</span> <span class="st">&quot;text/plain&quot;</span>)</span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;=&gt;</span> closeHeaders</span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;=&gt;</span> write <span class="st">&quot;Greetings, friend!&quot;</span></span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;=&gt;</span> end</span></code></pre></div> <p>Say we forget the line with <code>closeHeaders</code>. What do we get? An informative type error, of course!</p> <pre><code>Could not match type HeadersOpen with type BodyOpen</code></pre> <p>There are easier-to-use functions written on top of the response API so that you do not have to write out all state transitions explicitly.</p> <div class="sourceCode" id="cb9"><pre class="sourceCode haskell purescript"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>writeStatus statusOK</span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;=&gt;</span> contentType textHTML</span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;=&gt;</span> closeHeaders</span> <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;=&gt;</span> respond (<span class="fu">div</span> [] [ h1 [] [ text <span class="st">&quot;Greetings!&quot;</span> ]</span> <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> , p [] [ text <span class="st">&quot;You are most welcome.&quot;</span> ]</span> <span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> ])</span></code></pre></div> <p>The <code>respond</code> function has the added benefit of decoupling the response type, in this case <code>HTML</code>, from the response type required by the server, which for NodeJS is a <code>Buffer</code>. The transformation between <code>HTML</code> and <code>Buffer</code> is done behind the scenes by a bit of type class machinery.</p> <p>The bits presented so far constitute the low level API for responding to HTTP requests in Hyper. I have plans for designing a simpler interface, based on <em>Ring</em> response maps in Clojure, where response handlers simply return a data structure describing the response to be written. Such an API can be built on top of the existing low-level API.</p> <h2 id="wrapping-up">Wrapping Up</h2> <p>We have looked at the core design of Hyper, and the motivation behind the project, but merely scratched the surface. The <a href="http://hyper.wickstrom.tech">documentation</a> describes in much greater detail the implementation and current components of Hyper. I hope, however, that I have caught your interest. If so, please go ahead and check out some of the <a href="https://github.com/purescript-hyper/hyper/tree/master/examples">provided examples at GitHub</a>.</p> <p>Also, note that the project is highly experimental, not nearly ready for any production use. But it could be! If you are interested in contributing, do not hesitate to send me a tweet or a PM. I need help writing the library, middleware, servers for different PureScript backends, more examples, documentation, etc. If you want to have a look at the source code, it’s <a href="https://github.com/owickstrom/hyper">also on GitHub</a>.</p> <p>Thanks for reading!</p> <hr /> <p><em><strong>Note at Feb 13, 2017:</strong> Since this blog post was published, the design of Hyper has changed. It is no longer based on simple monadic functions, but instead indexed monads, to provide safer interaction with response writing side effects. The documentation at <a href="http://hyper.wickstrom.tech">hyper.wickstrom.tech</a> should always be up-to-date, so please have a look there as well.</em></p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2016-10-10-taking-a-step-back-from-oden.html</id>
    <title>Taking a Step Back from Oden</title>
    <link href="https://wickstrom.tech/2016-10-10-taking-a-step-back-from-oden.html"/>
    <published>2016-10-10T00:00:00+02:00</published>
    <updated>2016-10-10T00:00:00+02:00</updated>
    <summary>I have decided to stop working on Oden.</summary>
    <content type="html"><![CDATA[
<p>I have decided to stop working on Oden.</p> <p>Why? Building a programming language, even a small one as Oden where I can piggy-back on the success and engineering efforts of Go, takes a lot of time. <em>A lot of time.</em> I currently work professionally at a startup called <a href="https://codescene.com/">Empear</a> where we develop products for analyzing and understanding large software projects based on their history. The work is a lot of fun, but also takes much of my time and energy. Working with something as demanding as Oden on nights and weekends, at least in the long run, is unrealistic for me. I managed to keep a good pace during the spring of 2016, but since this summer the tempo has steadily declined into spurious efforts every other week or so. While that may be fine in many open-source projects, I think Oden needs a far better momentum if it is to be usable in any foreseeable future.</p> <p>I have wrestled with these thoughts for some time now, trying to find ways not to put the project on the shelf. Gathering and building a community of people is not easy. Neither is having the stamina to work on a project alone for an extended period of time. If I cannot build it alone, and if I cannot rally the group of people needed to build it together, then I should focus my efforts elsewhere. In other words, I should find something to work on that fits my current work situation and private life better.</p> <p>Summarizing the year of 2016, it has been a hell of a ride. I went from only having talked at internal company events, to presenting at a local user group, and finally being a speaker on PolyConf in Poznań, Curry On in Rome, and Lambda World in Cádiz! I have learned a lot about programming language design, compiler implementation, open-source project management, project marketing, and blogging. Regardless if you build something that “succeeds”, whatever that means, I can highly recommend building your own language and compiler. It such a joy seeing your own little creature taking shape, running the first <em>Hello World</em>, writing the documentation, and meeting people that are excited about your work. There are negative sides as well, at least if you publish your work. The haters will find you and do their best to bring you down. I was shocked by that experience, loosing sleep and feeling really bad about myself. Eventually, though, I got out of it and started seeing all the wonderful reactions and encouragements from people around me, especially at conferences. That almost became a shock in itself! “Are people really this excited about my little language project?”</p> <p>All of this has come together as a challenging balance for me – a balance between doing Oden for my own sake, a way of learning more about designing and building a programming language, and doing it for other people and for the Go community. I have reached a turning point and I will stop here. This post may seem as an attempt of creating some drama, which is not the point. I think it is reasonable for me to be clear why I am doing this, and to avoid any false expectations on the project’s future.</p> <p>The Oden source code will remain at GitHub, with a big disclaimer that it is no longer in active development. I will shut down <a href="https://oden-lang.org/">oden-lang.org</a> and associated sites this week as I am paying for the servers myself. I might transfer the latest User Guide to a GitHub Pages site for free hosting.</p> <p>As a final note, I still think the Go ecosystem deserves a decent functional programming language. Maybe some new language will emerge and fill those shoes, or a backend for PureScript or OCaml can win some ground. What ever the turnout, if Oden played even the smallest part in that story, I would be very happy.</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2016-07-10-custom-formatting-in-html-and-latex-code-listings-using-pandoc.html</id>
    <title>Custom Formatting in HTML and LaTeX Code Listings using Pandoc</title>
    <link href="https://wickstrom.tech/2016-07-10-custom-formatting-in-html-and-latex-code-listings-using-pandoc.html"/>
    <published>2016-07-10T00:00:00+02:00</published>
    <updated>2016-07-10T00:00:00+02:00</updated>
    <summary>I have worked intensively on the Oden User Guide lately, primarily on
improving content, but also on providing high-quality PDF and HTML
output formats with detailed control over typesetting.</summary>
    <content type="html"><![CDATA[
<p>I have worked intensively on the Oden User Guide lately, primarily on improving content, but also on providing high-quality PDF and HTML output formats with detailed control over typesetting.</p> <p>For some code listings and syntax examples I want the typesetting to convey the meaning of text in the listing – user input, command output, placeholders, etc. The User Guide build uses <a href="http://pandoc.org/">Pandoc</a> to transform Markdown documents to PDF (through LaTeX) and to HTML. I could not find a good way to express the custom formatting in a way that worked with both LaTeX and HTML using standard Pandoc functionality.</p> <p>Therefore, I created <a href="https://github.com/oden-lang/oden/blob/master/doc/user-guide/filters/include-code/Main.hs">a filter</a> to handle my input format. Using this filter, I can write code listings in separate files, using a small subset of HTML. Here’s an example of a shell command listing source file from the User Guide:</p> <div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>$ <span class="dt">&lt;</span><span class="kw">strong</span><span class="dt">&gt;</span>GOPATH=PWD/target/go:$GOPATH go build -o hello hello/main<span class="dt">&lt;/</span><span class="kw">strong</span><span class="dt">&gt;</span></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>$ <span class="dt">&lt;</span><span class="kw">strong</span><span class="dt">&gt;</span>./hello<span class="dt">&lt;/</span><span class="kw">strong</span><span class="dt">&gt;</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>Hello, world!</span></code></pre></div> <p>The <code>strong</code> tags in the listing are part of the HTML subset I use. To include listings in the Pandoc Markdown source I use regular code block syntax and add the custom <code>include</code> and <code>formatted</code> attributes:</p> <pre><code>```{include=src/listings/hello-world-go-build-and-run.html formatted=true} ```</code></pre> <p>The output, both in HTML and PDF, looks like this:</p> <pre><code>$ <strong>GOPATH=PWD/target/go:$GOPATH go build -o hello hello/main</strong> $ <strong>./hello</strong> Hello, world!</code></pre> <p>For listings explaining the syntax of the Oden language I want placeholders to be typeset in italic text. Where the language supports a sequence of forms I want to express that using placeholder expressions with subscripts. The following listing source file explains the <em>let binding</em> syntax of Oden.</p> <div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>let <span class="dt">&lt;</span><span class="kw">em</span><span class="dt">&gt;</span>identifier<span class="dt">&lt;</span><span class="kw">sub</span><span class="dt">&gt;</span>1<span class="dt">&lt;/</span><span class="kw">sub</span><span class="dt">&gt;&lt;/</span><span class="kw">em</span><span class="dt">&gt;</span> = <span class="dt">&lt;</span><span class="kw">em</span><span class="dt">&gt;</span>expression<span class="dt">&lt;</span><span class="kw">sub</span><span class="dt">&gt;</span>1<span class="dt">&lt;/</span><span class="kw">sub</span><span class="dt">&gt;&lt;/</span><span class="kw">em</span><span class="dt">&gt;</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">em</span><span class="dt">&gt;</span>identifier<span class="dt">&lt;</span><span class="kw">sub</span><span class="dt">&gt;</span>2<span class="dt">&lt;/</span><span class="kw">sub</span><span class="dt">&gt;&lt;/</span><span class="kw">em</span><span class="dt">&gt;</span> = <span class="dt">&lt;</span><span class="kw">em</span><span class="dt">&gt;</span>expression<span class="dt">&lt;</span><span class="kw">sub</span><span class="dt">&gt;</span>2<span class="dt">&lt;/</span><span class="kw">sub</span><span class="dt">&gt;&lt;/</span><span class="kw">em</span><span class="dt">&gt;</span></span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> ...</span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">em</span><span class="dt">&gt;</span>identifier<span class="dt">&lt;</span><span class="kw">sub</span><span class="dt">&gt;</span>n<span class="dt">&lt;/</span><span class="kw">sub</span><span class="dt">&gt;&lt;/</span><span class="kw">em</span><span class="dt">&gt;</span> = <span class="dt">&lt;</span><span class="kw">em</span><span class="dt">&gt;</span>expression<span class="dt">&lt;</span><span class="kw">sub</span><span class="dt">&gt;</span>n<span class="dt">&lt;/</span><span class="kw">sub</span><span class="dt">&gt;&lt;/</span><span class="kw">em</span><span class="dt">&gt;</span></span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>in <span class="dt">&lt;</span><span class="kw">em</span><span class="dt">&gt;</span>body-expression<span class="dt">&lt;/</span><span class="kw">em</span><span class="dt">&gt;</span></span></code></pre></div> <p>When included in the document, just like in the example before, the output looks like this:</p> <pre><code>let <em>identifier<sub>1</sub></em> = <em>expression<sub>1</sub></em> <em>identifier<sub>2</sub></em> = <em>expression<sub>2</sub></em> ... <em>identifier<sub>n</sub></em> = <em>expression<sub>n</sub></em> in <em>body-expression</em></code></pre> <p>The filter is very simplistic in that it only supports <code>em</code>, <code>strong</code>, and <code>sub</code> elements, but it suits the needs of the Oden User Guide. I find extending Pandoc with filters very powerful, and I hope you find this technique and the blog post useful. If you are interested in the complete solution, including the Makefile compiling the filter and running Pandoc, please see <a href="https://github.com/oden-lang/oden/tree/master/doc/user-guide">doc/user-guide</a> in the Oden repository.</p> <p>Long live Pandoc!</p>
]]></content>
  </entry>
  <entry>
    <id>https://wickstrom.tech/2016-05-15-paramount-color-scheme-for-vim.html</id>
    <title>Paramount Color Scheme for Vim</title>
    <link href="https://wickstrom.tech/2016-05-15-paramount-color-scheme-for-vim.html"/>
    <published>2016-05-15T00:00:00+02:00</published>
    <updated>2016-05-15T00:00:00+02:00</updated>
    <summary>Having tried a lot of color schemes for editors, especially for Vim, I
have gotten quite picky. All right, very picky. Most of the time Vim has
been configured to use Tomorrow Night or Gruvbox. Although they’re
great, they have felt a bit over the top. Also, depending on where I’m
sitting at the moment I use both dark and light backgrounds for best
contrast.</summary>
    <content type="html"><![CDATA[
<p>Having tried a lot of color schemes for editors, especially for Vim, I have gotten quite picky. All right, <em>very</em> picky. Most of the time Vim has been configured to use <a href="https://github.com/chriskempson/tomorrow-theme">Tomorrow Night</a> or <a href="https://github.com/morhetz/gruvbox">Gruvbox</a>. Although they’re great, they have felt a bit over the top. Also, depending on where I’m sitting at the moment I use both dark and light backgrounds for best contrast.</p> <p>The last couple of days I’ve tried <a href="https://github.com/pbrisbin/vim-colors-off">off</a> with some small modifications for accent colors, e.g. number, strings and escape sequences. This setup fit my taste very well so I decided to package that as a color scheme for Vim. I call it <a href="https://github.com/owickstrom/vim-colors-paramount">Paramount</a>.</p> <p>The goal of Paramount is to not clutter your editor with all colors of the rainbow, just to keep it simple. It uses three monochrome colors for most text together with a purple accent color. Diffs and some errors use red and green colors.</p> <p>Paramount is based on the <a href="https://github.com/reedes/vim-colors-pencil">pencil</a> and <a href="https://github.com/pbrisbin/vim-colors-off">off</a> color schemes. Thanks for the great work on those projects!</p> <h2 id="screenshots">Screenshots</h2> <p>The following screenshots show some Go code together with the <em>Latin Modern Mono</em> font on light and dark backgrounds.</p> <p><img src="https://raw.githubusercontent.com/owickstrom/vim-colors-paramount/master/screenshots/latin-modern-light.png" /> <img src="https://raw.githubusercontent.com/owickstrom/vim-colors-paramount/master/screenshots/latin-modern-dark.png" /></p> <p>…and if you use the <em>Monaco</em> font to show the Ruby code from <a href="http://vimcolors.com/">vimcolors.com</a>:</p> <p><img src="https://raw.githubusercontent.com/owickstrom/vim-colors-paramount/master/screenshots/monaco-light.png" /> <img src="https://raw.githubusercontent.com/owickstrom/vim-colors-paramount/master/screenshots/monaco-dark.png" /></p> <h2 id="installation">Installation</h2> <p>Simply copy <a href="https://raw.githubusercontent.com/owickstrom/vim-colors-paramount/master/colors/paramount.vim">the color scheme file</a> to your <code>~/.vim/colors directory</code> or use a plugin manager like Plug or Vundle and add <code>"owickstrom/vim-colors-paramount"</code> as a plugin.</p> <p>The source is available <a href="https://github.com/owickstrom/vim-colors-paramount">on GitHub</a>. You can preview the theme on the <a href="http://vimcolors.com/438/paramount/dark"><em>~/.vim/colors</em></a> site.</p>
]]></content>
  </entry>
</feed>
