<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://maxtaylor.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://maxtaylor.dev/" rel="alternate" type="text/html" /><updated>2026-09-15T11:08:45-04:00</updated><id>https://maxtaylor.dev/feed.xml</id><title type="html">Max Taylor’s Personal Site</title><subtitle>Assistant Professor @ Boise State University</subtitle><author><name>Max Taylor</name></author><entry><title type="html">Rust: Generics Considered Colorful</title><link href="https://maxtaylor.dev/posts/2023/09/rust-generics-considered-colorful" rel="alternate" type="text/html" title="Rust: Generics Considered Colorful" /><published>2023-09-01T00:00:00-04:00</published><updated>2023-09-01T00:00:00-04:00</updated><id>https://maxtaylor.dev/posts/2023/09/rust-generics-considered-colorful</id><content type="html" xml:base="https://maxtaylor.dev/posts/2023/09/rust-generics-considered-colorful"><![CDATA[<p>This post shows that Rust’s generics are colorful. I’ll demonstrate an
example to show what I mean, and what the problems are.</p>

<h1 id="motivating-example">Motivating Example</h1>

<p>Consider this silly code:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="n">MyTrait</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">foo</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">struct</span> <span class="n">S1</span><span class="p">;</span>

<span class="k">impl</span> <span class="n">MyTrait</span> <span class="k">for</span> <span class="n">S1</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">foo</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"S1::foo()"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="n">call_foo</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">T</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span><span class="p">:</span> <span class="n">MyTrait</span> <span class="p">{</span>
    <span class="n">t</span><span class="nf">.foo</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">s1</span> <span class="o">=</span> <span class="n">S1</span><span class="p">{};</span>
    <span class="nf">call_foo</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This seems fine so far.</p>

<p>Now, let’s suppose we have an collection of <code class="language-plaintext highlighter-rouge">MyTraits</code>, like this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Previous code not shown.</span>

<span class="k">struct</span> <span class="n">S2</span><span class="p">;</span>

<span class="k">impl</span> <span class="n">MyTrait</span> <span class="k">for</span> <span class="n">S2</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">foo</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"S2::foo()"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">v</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;&amp;</span><span class="k">dyn</span> <span class="n">MyTrait</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="o">&amp;</span><span class="n">S1</span><span class="p">{},</span> <span class="o">&amp;</span><span class="n">S2</span><span class="p">{}];</span>
    <span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="n">v</span> <span class="p">{</span>
        <span class="nf">call_foo</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This produces this compilation error:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Compiling playground v0.0.1 (/playground)
error[E0277]: the size for values of type `dyn MyTrait` cannot be known at compilation time
  --&gt; src/main.rs:28:18
   |
28 |         call_foo(x);
   |         -------- ^ doesn't have a size known at compile-time
   |         |
   |         required by a bound introduced by this call
   |
   = help: the trait `Sized` is not implemented for `dyn MyTrait`
</code></pre></div></div>
<p>The problem is that Rust generics are monomorphized, but monomorphization is not supported for trait objects.</p>

<p><code class="language-plaintext highlighter-rouge">call_foo</code> is a <a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/">colored function</a>.
The code doesn’t compile because trait objects are the wrong color.</p>

<h1 id="does-this-matter-in-real-life">Does this Matter In Real Life?</h1>
<p>Yes. Here’s an example: The <a href="https://docs.rs/z3/latest/z3/">Rust bindings for interacting with the Z3
theorem prover</a> have a trait
<code class="language-plaintext highlighter-rouge">z3::ast::Ast</code> to represent terms, constants, and expressions.  As
you’re building a theory, you may want to maintain a vector of your
constants in a <code class="language-plaintext highlighter-rouge">Vec&lt;Box&lt;dyn z3::ast::Ast&gt;&gt;</code>. Once Z3 has constructed a
model that satisfies your theory, you’ll probably want to query the model for
the values of constants via the method <code class="language-plaintext highlighter-rouge">pub fn get_const_interp&lt;T: Ast&lt;'ctx&gt;&gt;(&amp;self, ast: &amp;T) -&gt; Option&lt;T&gt;</code>.</p>

<p>Well, you just shot your foot off. You can’t call this method on a
trait object, so now you need to redo the work you just did. And the
new code is going to be a <em>whole</em> lot uglier.</p>

<h1 id="fix-1-prefer-trait-objects">Fix 1: Prefer Trait Objects</h1>
<p><a href="https://www.lurklurk.org/effective-rust/generics.html">In contrast to the orthodox Rust
opinion</a>, we
should prefer to use trait objects <em>unless</em> we explicitly need to
combine multiple trait bounds or dynamic dispatch is a performance issue. Here’s what I mean:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Previous code not shown.</span>

<span class="k">fn</span> <span class="nf">call_foo</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">dyn</span> <span class="n">MyTrait</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">x</span><span class="nf">.foo</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">v</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;&amp;</span><span class="k">dyn</span> <span class="n">MyTrait</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="o">&amp;</span><span class="n">S1</span><span class="p">{},</span> <span class="o">&amp;</span><span class="n">S2</span><span class="p">{}];</span>
    <span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="n">v</span> <span class="p">{</span>
        <span class="nf">call_foo</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note that this trait object is general enough to work with many data
structures. For example, we can still use a <code class="language-plaintext highlighter-rouge">Box</code> with this implementation:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Previous code not shown.</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">v2</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nn">std</span><span class="p">::</span><span class="nn">boxed</span><span class="p">::</span><span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span> <span class="n">MyTrait</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="nn">std</span><span class="p">::</span><span class="nn">boxed</span><span class="p">::</span><span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">S1</span><span class="p">{}),</span> 
                                                     <span class="nn">std</span><span class="p">::</span><span class="nn">boxed</span><span class="p">::</span><span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">S2</span><span class="p">{})];</span>
    <span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="o">&amp;</span><span class="n">v2</span> <span class="p">{</span>
        <span class="nf">call_foo</span><span class="p">(</span><span class="n">x</span><span class="nf">.as_ref</span><span class="p">());</span>
    <span class="p">}</span>
    
    <span class="nf">call_foo</span><span class="p">(</span><span class="n">v2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="nf">.as_ref</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And, of course, we can still use <code class="language-plaintext highlighter-rouge">call_foo</code> on a specific instance:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Previous code not shown.</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="n">S1</span><span class="p">{};</span>
    <span class="nf">call_foo</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h1 id="fix-2-always-implement-your-traits-for-trait-objects">Fix 2: Always Implement Your Traits for Trait Objects</h1>
<p>You should just always implement your traits for trait objects:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Previous code not shown.</span>

<span class="k">impl</span> <span class="n">MyTrait</span> <span class="k">for</span> <span class="o">&amp;</span><span class="k">dyn</span> <span class="n">MyTrait</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">foo</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="p">(</span><span class="o">**</span><span class="k">self</span><span class="p">)</span><span class="nf">.foo</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="n">call_foo</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">T</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span><span class="p">:</span> <span class="n">MyTrait</span> <span class="p">{</span>
    <span class="n">x</span><span class="nf">.foo</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">v</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;&amp;</span><span class="k">dyn</span> <span class="n">MyTrait</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="o">&amp;</span><span class="n">S1</span><span class="p">{},</span> <span class="o">&amp;</span><span class="n">S2</span><span class="p">{}];</span>
    <span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="n">v</span> <span class="p">{</span>
        <span class="nf">call_foo</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note that this code also works on other kinds of trait objects:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Previous code not shown.</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">v2</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nn">std</span><span class="p">::</span><span class="nn">boxed</span><span class="p">::</span><span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span> <span class="n">MyTrait</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="nn">std</span><span class="p">::</span><span class="nn">boxed</span><span class="p">::</span><span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">S1</span><span class="p">{}),</span> 
                                                     <span class="nn">std</span><span class="p">::</span><span class="nn">boxed</span><span class="p">::</span><span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">S2</span><span class="p">{})];</span>
    <span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="o">&amp;</span><span class="n">v2</span> <span class="p">{</span>
        <span class="nf">call_foo</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="nf">.as_ref</span><span class="p">());</span>
    <span class="p">}</span>

    <span class="nf">call_foo</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="nf">.as_ref</span><span class="p">());</span>

    <span class="k">let</span> <span class="n">v3</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nn">std</span><span class="p">::</span><span class="nn">rc</span><span class="p">::</span><span class="nb">Rc</span><span class="o">&lt;</span><span class="k">dyn</span> <span class="n">MyTrait</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="nn">std</span><span class="p">::</span><span class="nn">rc</span><span class="p">::</span><span class="nn">Rc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">S1</span><span class="p">{}),</span> 
                                                 <span class="nn">std</span><span class="p">::</span><span class="nn">rc</span><span class="p">::</span><span class="nn">Rc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">S2</span><span class="p">{})];</span>
    <span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="o">&amp;</span><span class="n">v3</span> <span class="p">{</span>
        <span class="nf">call_foo</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="nf">.as_ref</span><span class="p">());</span>
    <span class="p">}</span>

    <span class="nf">call_foo</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v3</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="nf">.as_ref</span><span class="p">());</span>
<span class="p">}</span>

</code></pre></div></div>

<p>If you create a trait then you must be the one that implements it for
trait objects. Per the <a href="https://doc.rust-lang.org/book/ch10-02-traits.html">coherence
rule</a> a trait can
only be implemented for a type by the crate that defines the trait or
defines the type.</p>

<h1 id="fix-3-fix-rust">Fix 3: Fix Rust</h1>
<p>There’s a lot of code in the wild that share the same pain-point as
the Z3 example I mentioned. It shouldn’t be difficult to use
generics. <a href="https://www.lurklurk.org/effective-rust/generics.html">Effective
Rust</a> does
explain the reason for the current design rather well. But I feel like
this is an area that can be improved on.</p>]]></content><author><name>Max Taylor</name></author><category term="types" /><category term="rust" /><summary type="html"><![CDATA[This post shows that Rust’s generics are colorful. I’ll demonstrate an example to show what I mean, and what the problems are.]]></summary></entry><entry><title type="html">A Concept and Template Meta-programming Approach to Session Types in C++</title><link href="https://maxtaylor.dev/posts/2023/05/cpp-session-types" rel="alternate" type="text/html" title="A Concept and Template Meta-programming Approach to Session Types in C++" /><published>2023-08-19T00:00:00-04:00</published><updated>2023-08-19T00:00:00-04:00</updated><id>https://maxtaylor.dev/posts/2023/05/c++-session-types</id><content type="html" xml:base="https://maxtaylor.dev/posts/2023/05/cpp-session-types"><![CDATA[<h1 id="introduction">Introduction</h1>
<p>Programs communicate – whether with other programs or
humans. Software developers write programs with a protocol in
mind. Sometimes there’s documentation for the protocol. But there’s no
mechanism that keeps implementation and documentation in sync. Bugs
occur when protocols diverge.</p>

<p>Many of us already use type systems. But naive approaches to typing
fall short of guaranteeing that an implementation speaks a
protocol. For example: Suppose two threads <code class="language-plaintext highlighter-rouge">T1</code> and <code class="language-plaintext highlighter-rouge">T2</code> communicate
over a channel <code class="language-plaintext highlighter-rouge">chan</code>. <code class="language-plaintext highlighter-rouge">T1</code> and <code class="language-plaintext highlighter-rouge">T2</code> play a guessing game. <code class="language-plaintext highlighter-rouge">T1</code>
guesses a number (<code class="language-plaintext highlighter-rouge">int</code>) and <code class="language-plaintext highlighter-rouge">T2</code> informs <code class="language-plaintext highlighter-rouge">T1</code> if the guess is right
(<code class="language-plaintext highlighter-rouge">bool</code>). We might type <code class="language-plaintext highlighter-rouge">chan</code> as <code class="language-plaintext highlighter-rouge">Chan&lt;std::variant&lt;int,
bool&gt;&gt;</code>. This isn’t helpful, though. If <code class="language-plaintext highlighter-rouge">T1</code> sends a <code class="language-plaintext highlighter-rouge">bool</code> the
program should not compile, yet it does.</p>

<p><a href="https://en.wikipedia.org/wiki/Session_type">Session types</a> are a tool
that solves this problem. This post discusses an implementation of
session types in C++. You’ll learn more about how you can use session
types to specify protocols. You’ll also see some features in C++
(concepts and template meta-programming) you might not know how to use
today.</p>

<p>All code is available <a href="https://github.com/obicons/session-types">on GitHub</a>.</p>

<h2 id="motivating-example-io">Motivating Example: IO</h2>
<p>Instead of two threads playing a guessing game, let’s make a game for
humans. First, the computer generates a random number between 1
and 100. Second, the computer prompts the user to guess the
number. Then, the user enters a guess. Next, the computer evaluates
the user’s guess. If the guess is correct then the program sends a
congratulatory message and exits. If the guess is wrong then the
program asks the user if they give up. The user keeps guessing the
generated number until they get it right or give up.</p>

<p>This listing shows how we might specify this protocol with session types:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>using GuessingGameProtocol =
    Rec&lt;Choose&lt;QueryUserProtocol&lt;Choose&lt;KeepPlayingProtocol, Var&lt;Z&gt;&gt;&gt;,
               ExitProtocol&gt;&gt;;

template &lt;HasDual P&gt;
using QueryUserProtocol = Send&lt;std::string, Recv&lt;int, P&gt;&gt;;

using KeepPlayingProtocol = Send&lt;std::string, Recv&lt;std::string, Var&lt;Z&gt;&gt;&gt;;

using ExitProtocol = Choose&lt;ExitUserLost, ExitUserWon&gt;;
using ExitUserLost = Send&lt;std::string, Send&lt;int, Send&lt;std::string, Send&lt;std::ostream&amp;(std::ostream&amp;), Z&gt;&gt;&gt;&gt;;
using ExitUserWon = Send&lt;std::string, Send&lt;std::ostream&amp;(std::ostream &amp;), Z&gt;&gt;;
</code></pre></div></div>

<p>Let’s unpack:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Rec&lt;P&gt;</code> introduces a recursive protocol. It allows the protocol to repeat itself using <code class="language-plaintext highlighter-rouge">Var</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">Choose&lt;P1, P2&gt;</code> allows the implementation to make a choice between
protocols <code class="language-plaintext highlighter-rouge">P1</code> and <code class="language-plaintext highlighter-rouge">P2</code>. <code class="language-plaintext highlighter-rouge">Choose&lt;QueryUserProtocol&lt;...&gt;,
ExitProtocol&gt;</code> represents a choice between asking the user for
another guess and terminating.</li>
  <li><code class="language-plaintext highlighter-rouge">Send&lt;T1, P&gt;</code> represents that the implementation sends a value of
type <code class="language-plaintext highlighter-rouge">T1</code> then executes the protocol <code class="language-plaintext highlighter-rouge">P</code>. Similarly, <code class="language-plaintext highlighter-rouge">Recv</code> receives.</li>
  <li><code class="language-plaintext highlighter-rouge">Var&lt;N&gt;</code> accepts a natural number – either <code class="language-plaintext highlighter-rouge">Z</code> or <code class="language-plaintext highlighter-rouge">Succ&lt;M&gt;</code> –
and returns to the recursive environment <code class="language-plaintext highlighter-rouge">N</code> levels out.</li>
</ul>

<p>Here’s what an implementation of this protocol might look like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int main() {
  std::default_random_engine generator;
  generator.seed(time(nullptr));

  std::uniform_int_distribution distribution(1,10);
  const auto the_number = distribution(generator);

  auto keep_going = true;
  auto guess = 0;

  Chan&lt;GuessingGameProtocol, decltype(&amp;std::cin), decltype(&amp;std::cout)&gt; chan(&amp;std::cin, &amp;std::cout);

  while (keep_going) {
    auto c1 = chan.enter().choose1();
    auto c2 = c1 &lt;&lt; "Guess: ";
    auto c3 = c2 &gt;&gt; guess;

    keep_going = guess != the_number;
    if (keep_going) {
      auto c4 = c3.choose1();
      auto c5 = c4 &lt;&lt; "Incorrect. Keep playing? (y/n) ";
      std::string response;
      auto c6 = c5 &gt;&gt; response;
      keep_going = response != "n";

      chan = c6.ret();
    } else {
      chan = c3.choose2().ret();
    }
  }

  if (guess != the_number) {
    auto ce = chan.enter().choose2().choose1();
    ce &lt;&lt; "You lose. I was thinking of " &lt;&lt; the_number &lt;&lt; "." &lt;&lt; std::endl;
  } else {
    auto ce = chan.enter().choose2().choose2();
    ce &lt;&lt; "You win!" &lt;&lt; std::endl;
  }
}
</code></pre></div></div>

<p>Some explanations are in order:</p>
<ul>
  <li>The <code class="language-plaintext highlighter-rouge">Chan</code> type represents a session typed communication channel. It
encapsulates some other input and output mechanisms. In this case,
<code class="language-plaintext highlighter-rouge">cin</code> and <code class="language-plaintext highlighter-rouge">cout</code>.</li>
  <li>Programs operate on a <code class="language-plaintext highlighter-rouge">Chan</code> by calling methods. Following a method
call, it is illegal to reuse the <code class="language-plaintext highlighter-rouge">Chan</code> – doing so triggers a
run time error. Operations return new channels that speak the proper protocol.</li>
  <li><code class="language-plaintext highlighter-rouge">chan.enter()</code> enters a recursive context.</li>
  <li><code class="language-plaintext highlighter-rouge">Chan&lt;Choose&lt;P1, P2&gt;&gt;::choose1()</code> returns a channel that speaks
<code class="language-plaintext highlighter-rouge">P1</code>. <code class="language-plaintext highlighter-rouge">Chan&lt;Choose&lt;P1, P2&gt;&gt;::choose2()</code> returns a channel that speaks <code class="language-plaintext highlighter-rouge">P2</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">Chan&lt;Recv&lt;T, P&gt;&gt;::operator&gt;&gt;(T &amp;t)</code> reads a value from the
channel’s input stream into <code class="language-plaintext highlighter-rouge">t</code>. It returns a channel that speaks
<code class="language-plaintext highlighter-rouge">P</code>. <code class="language-plaintext highlighter-rouge">operator&lt;&lt;(const T &amp;t)</code> behaves similarly.</li>
  <li><code class="language-plaintext highlighter-rouge">Chan&lt;Var&lt;N&gt;&gt;::ret()</code> returns a channel that speaks the <code class="language-plaintext highlighter-rouge">N</code>th recursive protocol defined in the original type.</li>
</ul>

<p>Combined, this provides a stronger guarantee than what we had before:
<strong>Programs always send the right shaped data for the protocol, or send nothing.</strong></p>

<h2 id="motivating-example-multithreaded-communication">Motivating Example: Multithreaded Communication</h2>
<p>When two threads communicate over a channel it’s important that they
speak the same protocol. Our intuition tells us that every <code class="language-plaintext highlighter-rouge">Send&lt;T, ...&gt;</code> 
should have a corresponding <code class="language-plaintext highlighter-rouge">Recv&lt;T, ...&gt;</code>, etc. We call this
<em>duality</em>. We desire that our type system only allow two threads to
communicate over the channel if they are each other’s duals.</p>

<p>This next listing shows part an implementation of program with two
threads: <code class="language-plaintext highlighter-rouge">T1</code> and <code class="language-plaintext highlighter-rouge">T2</code>. <code class="language-plaintext highlighter-rouge">T1</code> sends a value to <code class="language-plaintext highlighter-rouge">T2</code>, who responds with
that value doubled.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#include &lt;cstdio&gt;
#include &lt;iostream&gt;
#include &lt;memory&gt;
#include "sesstypes.hh"
#include "concurrentmedium.hh"

using Protocol = Rec&lt;Send&lt;int, Recv&lt;int, Var&lt;Z&gt;&gt;&gt;&gt;;

void log(const std::string &amp;tname, const std::string &amp;action, int val) {
    printf("%s %s %d\n", tname.c_str(), action.c_str(), val);
}

void log(const std::string &amp;tname, const std::string &amp;action) {
    printf("%s %s\n", tname.c_str(), action.c_str());
}

struct {
    template &lt;typename CommunicationMedium&gt;
    void operator()(Chan&lt;Protocol, CommunicationMedium, CommunicationMedium&gt; chan) {
        int val;

        auto c = chan.enter();
        for (int i = 0; i &lt; 5; i++) {
            auto c1 = c &lt;&lt; i;
            log("T1", "sent", i);

            int val;
            auto c2 = c1 &gt;&gt; val;
            log("T1", "received", val);

            c = c2.ret().enter();
        }
        log("T1", "done", -1);
    }
} t1;


int main() {
    auto chan = std::make_shared&lt;ConcurrentMedium&lt;ProtocolTypes&lt;Protocol&gt;&gt;&gt;();
    auto threads = connect&lt;Protocol&gt;(t1, t2, chan);
    threads.first.join();
    threads.second.join();
}
</code></pre></div></div>

<p>Critically, we are only allowed to call <code class="language-plaintext highlighter-rouge">connect&lt;Protocol&gt;(t1, t2)</code> if
<code class="language-plaintext highlighter-rouge">t2</code> is the dual of <code class="language-plaintext highlighter-rouge">t1</code>. This requirement is enforced at compile time.</p>

<h1 id="a-c-implementation">A C++ Implementation</h1>
<p>Now that we have a better idea about what session types are, let’s see
how they are implemented.</p>

<h2 id="session-types">Session Types</h2>

<h3 id="duality-with-c-concepts">Duality with C++ Concepts</h3>
<p>Duality is critical to our concurrent motivating example. The idea
that a type has a dual can be captured using a
<code class="language-plaintext highlighter-rouge">concept</code>. <a href="https://en.cppreference.com/w/cpp/language/constraints">Concepts</a>
are named boolean predicates that restrict template parameters.</p>

<p>Take the definition of the <code class="language-plaintext highlighter-rouge">Recv</code> type:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;typename T, HasDual P&gt;
struct Recv {
    using dual = Send&lt;T, typename P::dual&gt;;
};
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Recv</code> defines <code class="language-plaintext highlighter-rouge">dual</code> as its opposite, <code class="language-plaintext highlighter-rouge">Send</code>. Since <code class="language-plaintext highlighter-rouge">Recv</code> requires
that the protocol <code class="language-plaintext highlighter-rouge">P</code> has a dual, we constrain <code class="language-plaintext highlighter-rouge">P</code> to types where
<code class="language-plaintext highlighter-rouge">HasDual</code> evaluates to true.</p>

<p>Here’s the implementation of <code class="language-plaintext highlighter-rouge">HasDual</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;typename T&gt;
concept HasDual = requires { typename T::dual; };
</code></pre></div></div>

<p>This introduces another new feature of C++: The <code class="language-plaintext highlighter-rouge">requires</code>
expression. <code class="language-plaintext highlighter-rouge">requires { typename T::dual; }</code> evaluates to <code class="language-plaintext highlighter-rouge">true</code> if
<code class="language-plaintext highlighter-rouge">typename T::dual</code> compiles. Otherwise, it evaluates to <code class="language-plaintext highlighter-rouge">false</code>. (By
the way, it’s illegal for a requires expression to always fail to
compile.)</p>

<p>Concepts are great because they improve compiler error messages. We’ve
all seen the error vomit C++ compilers produce when template expansion
fails. Concepts eliminate much of the noise to help us debug.</p>

<h3 id="natural-numbers-with-template-meta-programming">Natural Numbers with Template Meta-Programming</h3>
<p>Remember that <code class="language-plaintext highlighter-rouge">Var</code> uses a natural number to decide how many levels of
recursion to return from. Let’s see how our natural numbers are implemented.</p>

<p>Here’s a naive way to implement natural numbers:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct Z {};

template &lt;typename T&gt;
struct Succ {};
</code></pre></div></div>

<p>This definition allows us to write real natural numbers like
<code class="language-plaintext highlighter-rouge">Succ&lt;Succ&lt;Z&gt;&gt;</code>. The problem is that it also allows us to write things
that aren’t natural numbers, like <code class="language-plaintext highlighter-rouge">Succ&lt;int&gt;</code>. Given that this post is
about radical type checking, we should not be satisfied with this.</p>

<p>Instead, we use template metaprogramming to enforce that a type is a
natural number. There are two ways for a type to be a natural number:</p>
<ol>
  <li>It is <code class="language-plaintext highlighter-rouge">Z</code>.</li>
  <li>It is <code class="language-plaintext highlighter-rouge">Succ&lt;M&gt;</code> and <code class="language-plaintext highlighter-rouge">M</code> is a natural number.</li>
</ol>

<p>Here’s how we define a concept <code class="language-plaintext highlighter-rouge">IsNat</code> to check that a type is a natural number:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;typename T&gt;
struct IsNatImpl : std::false_type {};

template &lt;&gt;
struct IsNatImpl&lt;Z&gt; : std::true_type {};

template &lt;typename M&gt;
struct IsNatImpl&lt;Succ&lt;M&gt;&gt;
    : std::conditional_t&lt;
                IsNatImpl&lt;M&gt;::value,
                std::true_type,
                std::false_type
      &gt; {};

template &lt;typename T&gt;
concept IsNat = IsNatImpl&lt;T&gt;::value;
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">type_traits</code> header provides <code class="language-plaintext highlighter-rouge">std::true_type</code> and
<code class="language-plaintext highlighter-rouge">std::false_type</code> as canonical representations of <code class="language-plaintext highlighter-rouge">true</code> and <code class="language-plaintext highlighter-rouge">false</code>
at the type level. The default implementation of <code class="language-plaintext highlighter-rouge">IsNatImpl</code> inherits
from <code class="language-plaintext highlighter-rouge">false_type</code>, so its <code class="language-plaintext highlighter-rouge">value</code> member is <code class="language-plaintext highlighter-rouge">false</code>. The <code class="language-plaintext highlighter-rouge">Z</code>
specialization inherits from <code class="language-plaintext highlighter-rouge">true_type</code>, so its <code class="language-plaintext highlighter-rouge">value</code> member is
true.</p>

<p>The last specialization is kind of tricky. <code class="language-plaintext highlighter-rouge">conditional_t&lt;Condition,  A, B&gt;</code> 
is <code class="language-plaintext highlighter-rouge">A</code> when <code class="language-plaintext highlighter-rouge">Condition</code> is <code class="language-plaintext highlighter-rouge">true</code> and <code class="language-plaintext highlighter-rouge">B</code> otherwise. So we recursively check that 
<code class="language-plaintext highlighter-rouge">IsNatImpl&lt;M&gt;::value</code> is <code class="language-plaintext highlighter-rouge">true</code>. If so, then <code class="language-plaintext highlighter-rouge">Succ&lt;M&gt;</code> is a natural number,
and so we inherit from <code class="language-plaintext highlighter-rouge">true_type</code>.</p>

<p>This lets us write a more correct version of natural numbers:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;typename T&gt;
struct Succ;

// Code for IsNat.

template &lt;&gt;
struct Succ&lt;Z&gt; {};

template &lt;IsNat M&gt;
struct Succ&lt;M&gt; {};
</code></pre></div></div>

<h3 id="the-chan-type">The Chan Type</h3>
<p>Here we discuss the implementation of the <code class="language-plaintext highlighter-rouge">Chan</code> type. 
Since recursion is the hardest thing that we have to support
we’ll describe it first. It has far-reaching implications.</p>

<p>The idea is to represent a channel as a <code class="language-plaintext highlighter-rouge">Chan&lt;Protocol, E&gt;</code>.
<code class="language-plaintext highlighter-rouge">Protocol</code> is the protocol type. For example, <code class="language-plaintext highlighter-rouge">Recv&lt;int, Send&lt;int, Z&gt;&gt;</code>. 
<code class="language-plaintext highlighter-rouge">E</code> (for environment) is kind of like a stack. Here’s what I mean:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;HasDual P, typename IT, typename OT, typename E&gt;
class Chan&lt;Rec&lt;P&gt;, IT, OT, E&gt; : ChanBase&lt;IT, OT&gt; {
public:
    using ChanBase&lt;IT, OT&gt;::ChanBase;

    Chan&lt;P, IT, OT, std::pair&lt;Rec&lt;P&gt;, E&gt;&gt; enter() {
        // Implementation not shown.
    }
};
</code></pre></div></div>

<p>So, <code class="language-plaintext highlighter-rouge">Chan</code> is specialized on recursive protocols. It provides only one method, <code class="language-plaintext highlighter-rouge">enter</code>.
This makes it impossible to try to read from a recursive protocol, for example. 
The <code class="language-plaintext highlighter-rouge">enter</code> method for a protcol <code class="language-plaintext highlighter-rouge">Rec&lt;P&gt;</code> pushes <code class="language-plaintext highlighter-rouge">P</code> onto a stack. Since this all occurs in 
the type system, we represent the stack as a <code class="language-plaintext highlighter-rouge">std::pair</code>.</p>

<p>This allows us to define <code class="language-plaintext highlighter-rouge">Var&lt;N&gt;</code>, which pops <code class="language-plaintext highlighter-rouge">N</code> levels from the environment:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;HasDual P, typename IT, typename OT, typename E&gt;
class Chan&lt;Var&lt;Z&gt;, IT, OT, std::pair&lt;P, E&gt;&gt; : ChanBase&lt;IT, OT&gt; {
public:
    using ChanBase&lt;IT, OT&gt;::ChanBase;

    Chan&lt;P, IT, OT, E&gt; ret() {
        // Implementation not shown.
    }
};

template &lt;typename T, HasDual P, typename IT, typename OT, typename E&gt;
class Chan&lt;Var&lt;Succ&lt;T&gt;&gt;, IT, OT, std::pair&lt;P, E&gt;&gt; : ChanBase&lt;IT, OT&gt; {
public:
    using ChanBase&lt;IT, OT&gt;::ChanBase;

    Chan&lt;Var&lt;T&gt;, IT, OT, E&gt; ret() {
        // Implementation not shown.
    }
};

</code></pre></div></div>
<p>This is sort of recursive. In the base case, <code class="language-plaintext highlighter-rouge">ret</code> returns a channel whose 
protocol is the top of the environment stack. Otherwise, for <code class="language-plaintext highlighter-rouge">Var&lt;N&gt;</code>, <code class="language-plaintext highlighter-rouge">ret</code> returns a channel 
that also speaks <code class="language-plaintext highlighter-rouge">Var</code>. Only this time, it’s <code class="language-plaintext highlighter-rouge">Var&lt;N - 1&gt;</code>.</p>

<p><code class="language-plaintext highlighter-rouge">Chan</code> is specialized for all of the types with duals. For example, here’s <code class="language-plaintext highlighter-rouge">Chan&lt;Recv&lt;...&gt;, ...&gt;</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;typename T, HasDual P, typename IT, typename OT, typename E&gt;
class Chan&lt;Recv&lt;T, P&gt;, IT, OT, E&gt; : ChanBase&lt;IT, OT&gt; {
public:
    using ChanBase&lt;IT, OT&gt;::ChanBase;

    Chan&lt;P, IT, OT, E&gt; operator&gt;&gt;(T &amp;t) {
        if (ChanBase&lt;IT, OT&gt;::used) {
            throw ChannelReusedError();
        }

        ChanBase&lt;IT, OT&gt;::used = true;
        (*ChanBase&lt;IT, OT&gt;::input) &gt;&gt; t;
        return Chan&lt;P, IT, OT, E&gt;(ChanBase&lt;IT, OT&gt;::input, ChanBase&lt;IT, OT&gt;::output);
    }
};
</code></pre></div></div>

<p>Since it’s specialized, the only thing we can do with a <code class="language-plaintext highlighter-rouge">Chan&lt;Recv&lt;...&gt;&gt;</code> is 
use <code class="language-plaintext highlighter-rouge">operator&gt;&gt;</code>. This prevents a large number of mistakes – we can’t send
an integer at an unexpected time, for example.</p>

<h2 id="concurrent-communication-primitive">Concurrent Communication Primitive</h2>
<p>The second motivating example uses <code class="language-plaintext highlighter-rouge">ConcurrentMedium</code> to create a
<code class="language-plaintext highlighter-rouge">Chan</code>, instead of <code class="language-plaintext highlighter-rouge">cin</code> and <code class="language-plaintext highlighter-rouge">cout</code>. This allows two threads to
communicate over a channel. This section describes the design of <code class="language-plaintext highlighter-rouge">ConcurrentMedium</code>.</p>

<h3 id="guarantees">Guarantees</h3>
<ol>
  <li>Two threads can both read and write data to a <code class="language-plaintext highlighter-rouge">Chan</code>.</li>
  <li>Threads do not read their own write. If a thread attempts to read
its own write, it blocks until another write is available.</li>
  <li>Threads may only read a write once. If a thread attempts to read a
write twice, it blocks until a new write is available.</li>
  <li>Every write is observed by the next read. If a thread attempts to
write data before the last write is read, it blocks until a read occurs.</li>
</ol>

<h3 id="storage">Storage</h3>
<p>We store writes in a <code class="language-plaintext highlighter-rouge">std::variant</code>. This is a type-safe union. So,
the type <code class="language-plaintext highlighter-rouge">ConcurrentMedium&lt;std::variant&lt;int, std::string&gt;&gt;</code> can
communicate values with types of <code class="language-plaintext highlighter-rouge">int</code> or <code class="language-plaintext highlighter-rouge">std::string</code>.</p>

<p>This listing shows this implementation:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;typename... Ts&gt;
class ConcurrentMedium&lt;std::variant&lt;Ts...&gt;&gt; {
public:
    ConcurrentMedium()
        : was_read(true), writers_waiting(0), readers_waiting(0) {}

    template &lt;typename T&gt;
    ConcurrentMedium&amp; operator&lt;&lt;(const T &amp;value) {
        std::unique_lock held_lock(lock);
        while (!was_read) {
            // Needs to be in a while loop to ignore "spurious wakeups".
            // https://en.cppreference.com/w/cpp/thread/condition_variable/wait
            writers_waiting++;
            writer_cv.wait(held_lock);
            writers_waiting--;
        }

        data = value;
        was_read = false;
        write_source = std::this_thread::get_id();

        if (readers_waiting &gt; 0) {
            reader_cv.notify_one();
        }

        return *this;
    }

    template &lt;typename T&gt;
    ConcurrentMedium&amp; operator&gt;&gt;(T &amp;datum) {
        std::unique_lock held_lock(lock);
        while (write_source == std::this_thread::get_id() || was_read) {
            readers_waiting++;
            reader_cv.wait(held_lock);
            readers_waiting--;
        }

        datum = std::get&lt;T&gt;(data);
        was_read = true;

        if (writers_waiting &gt; 0) {
            writer_cv.notify_one();
        }

        return *this;
    }

private:
    std::mutex lock;

    int readers_waiting;
    std::condition_variable reader_cv;

    int writers_waiting;
    std::condition_variable writer_cv;

    std::variant&lt;Ts...&gt; data;
    std::thread::id write_source;
    bool was_read;
};
</code></pre></div></div>

<h3 id="problem-1-how-to-ensure-readswrites-are-type-safe">Problem 1: How to Ensure Reads/Writes are Type Safe?</h3>
<p>You may notice a small problem with <code class="language-plaintext highlighter-rouge">operator&gt;&gt;</code> and <code class="language-plaintext highlighter-rouge">operator&lt;&lt;</code>:
They accept <em>any</em> type <code class="language-plaintext highlighter-rouge">T</code>, but we are only able to read/write <code class="language-plaintext highlighter-rouge">T</code> if
it is part of the variant.</p>

<p>The way we’re going to solve this problem is to create a concept
<code class="language-plaintext highlighter-rouge">AssignableToVariant&lt;T, V&gt;</code> that is true whenever <code class="language-plaintext highlighter-rouge">T</code> can be written
to the variant <code class="language-plaintext highlighter-rouge">V</code>. <code class="language-plaintext highlighter-rouge">AssignableToVariant</code> is written by using a
template meta-program called <code class="language-plaintext highlighter-rouge">OneOf</code>.  Here are the implementations:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;typename T, typename V&gt;
struct OneOf : public std::false_type {};

template &lt;typename T, typename... Ts&gt;
struct OneOf&lt;T, std::variant&lt;Ts...&gt;&gt; : public std::conditional_t&lt;
        (std::is_same_v&lt;T, Ts&gt; || ...),
        std::true_type,
        std::false_type
    &gt;
{};

template &lt;typename T, typename V&gt;
concept AssignableToVariant = OneOf&lt;T, V&gt;::value;
</code></pre></div></div>

<p>This is similar to <code class="language-plaintext highlighter-rouge">IsNatImpl</code>. The syntax <code class="language-plaintext highlighter-rouge">(std::is_same_v&lt;T, Ts&gt; || ...)</code>
is called a <a href="https://en.cppreference.com/w/cpp/language/fold"><em>fold expression</em></a>. 
It essentially rewrites the original expression into
<code class="language-plaintext highlighter-rouge">(std::is_same_v&lt;T, Ts[0]&gt; || ... || std::is_same_v&lt;T, Ts[N]&gt;)</code>,
although <code class="language-plaintext highlighter-rouge">Ts[0]</code> is not real syntax.</p>

<p>These are the updated signatures for <code class="language-plaintext highlighter-rouge">operator&lt;&lt;</code> and <code class="language-plaintext highlighter-rouge">operator&gt;&gt;</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;AssignableToVariant&lt;std::variant&lt;Ts...&gt;&gt; T&gt;
ConcurrentMedium&amp; operator&lt;&lt;(const T &amp;value);

template &lt;AssignableToVariant&lt;std::variant&lt;Ts...&gt;&gt; T&gt;
ConcurrentMedium&amp; operator&gt;&gt;(T &amp;value);
</code></pre></div></div>

<h3 id="problem-2-how-to-create-a-concurrentmedium-for-an-arbitrary-protocol">Problem 2: How to Create a ConcurrentMedium For an Arbitrary Protocol?</h3>
<p><code class="language-plaintext highlighter-rouge">ConcurrentMedium</code> is hard to use. If we have a protocol <code class="language-plaintext highlighter-rouge">Send&lt;int, Read&lt;std::string, ...&gt;&gt;</code>, 
it is time-consuming and error-prone to keep writing <code class="language-plaintext highlighter-rouge">ConcurrentMedium&lt;std::variant&lt;int, std::string, ...&gt;&gt;</code>.
Plus, we have to exert effort to keep the variant and the protocol in sync.
To solve this problem, we’ll create another template meta-program called <code class="language-plaintext highlighter-rouge">ProtocolTypes</code>. 
<code class="language-plaintext highlighter-rouge">ProtocolTypes&lt;Send&lt;int, Read&lt;std::string, ...&gt;&gt;&gt;</code> automatically creates a <code class="language-plaintext highlighter-rouge">std::variant&lt;int, std::string, ...&gt;</code>.</p>

<p>Here’s the implementation:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;typename Variant, typename T&gt;
struct ProtocolTypesImpl;

template &lt;typename... Ts, typename T, HasDual P&gt;
struct ProtocolTypesImpl&lt;std::variant&lt;Ts...&gt;, Recv&lt;T, P&gt;&gt; {
    using type = typename ProtocolTypesImpl&lt;std::variant&lt;T, Ts...&gt;, P&gt;::type;
};

template &lt;typename... Ts&gt;
struct ProtocolTypesImpl&lt;std::variant&lt;Ts...&gt;, Z&gt; {
  using type = std::variant&lt;Z, Ts...&gt;;
};

template &lt;typename... Ts, typename T, HasDual P&gt;
struct ProtocolTypesImpl&lt;std::variant&lt;Ts...&gt;, Send&lt;T, P&gt;&gt; {
    using type = typename ProtocolTypesImpl&lt;std::variant&lt;T, Ts...&gt;, P&gt;::type;
};

template &lt;typename... Ts, HasDual P&gt;
struct ProtocolTypesImpl&lt;std::variant&lt;Ts...&gt;, Rec&lt;P&gt;&gt; {
    using type = typename ProtocolTypesImpl&lt;std::variant&lt;Ts...&gt;, P&gt;::type;
};

template &lt;typename... Ts, IsNat N&gt;
struct ProtocolTypesImpl&lt;std::variant&lt;Ts...&gt;, Var&lt;N&gt;&gt; {
    using type = std::variant&lt;Ts...&gt;;
};

template &lt;HasDual P&gt;
using ProtocolTypes =  ProtocolTypesImpl&lt;std::variant&lt;&gt;, P&gt;::type;
</code></pre></div></div>

<p>Of course, there’s a small problem with this implementation. Namely, 
if we have a protocol <code class="language-plaintext highlighter-rouge">Send&lt;int, Recv&lt;int, Z&gt;&gt;</code>, we create
a <code class="language-plaintext highlighter-rouge">std::variant&lt;int, int&gt;</code>. Then, <code class="language-plaintext highlighter-rouge">std::get&lt;int&gt;(data)</code> is illegal because
the type <code class="language-plaintext highlighter-rouge">int</code> does not uniquely index the variant. We need <em>all</em> types to be
unique.</p>

<p>Once again, we use a template meta-program to implement this idea:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template &lt;typename T, typename... Ts&gt;
struct Unique : std::type_identity&lt;T&gt; {};

template &lt;typename... Ts, typename U, typename... Us&gt;
struct Unique&lt;std::variant&lt;Ts...&gt;, U, Us...&gt;
    : std::conditional_t&lt;(std::is_same_v&lt;U, Ts&gt; || ...),
                         Unique&lt;std::variant&lt;Ts...&gt;, Us...&gt;,
                         Unique&lt;std::variant&lt;Ts..., U&gt;, Us...&gt;&gt; {};

template &lt;typename T&gt;
struct MakeUniqueVariantImpl;

template &lt;typename... Ts&gt;
struct MakeUniqueVariantImpl&lt;std::variant&lt;Ts...&gt;&gt; {
    using type = typename Unique&lt;std::variant&lt;&gt;, Ts...&gt;::type;
};

template &lt;typename T&gt;
using MakeUniqueVariant = typename MakeUniqueVariantImpl&lt;T&gt;::type;
</code></pre></div></div>

<p>And we revise <code class="language-plaintext highlighter-rouge">ProtocolTypes</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>using ProtocolTypes = MakeUniqueVariant&lt;ProtocolTypesImpl&lt;std::variant&lt;&gt;, P&gt;::type&gt;;
</code></pre></div></div>

<p>Now we can easily type a <code class="language-plaintext highlighter-rouge">ConcurrentMedium</code>: <code class="language-plaintext highlighter-rouge">ConcurrentMedium&lt;ProtocolTypes&lt;Protocol&gt;&gt;</code>.</p>

<h1 id="reflections">Reflections</h1>
<h2 id="soundness">Soundness</h2>
<p>There are (at least) two important ways this approach is not sound:</p>
<ul>
  <li><strong>Users can accidentally reuse channels</strong>. We mitigate this risk by
raising an exception in this event. An affine type system could help
solve this problem. Someone <a href="https://github.com/IFeelBloated/Type-System-Zoo/blob/master/substructural%20type%20(affine%20type).cxx">has already shown how to implement this
in
C++</a>.
I left this unimplemented for this post for two reasons. First, the
approach isn’t sound either. It relies on automatic template type
inference. But if users manually specify template types then the code
passes all type checking incorrectly. Second, it’s complex. It’s too distracting 
for this blog post.</li>
  <li><strong>Liveliness is not guaranteed</strong>. Programs could simply never send
(or receive) values over a channel. This can be mitigated by linear
types, which require that a variable be used exactly once.
    <h2 id="completeness">Completeness</h2>
    <p>This approach does help us describe communications between exactly
two entities. But here are some scenarios that this specific approach doesn’t
help:</p>
  </li>
  <li><strong>Communications between several threads</strong>.</li>
  <li><strong>Asynchronous communication</strong>.</li>
</ul>]]></content><author><name>Max Taylor</name></author><category term="types" /><category term="session types" /><category term="c++" /><category term="template metaprogramming" /><summary type="html"><![CDATA[Introduction Programs communicate – whether with other programs or humans. Software developers write programs with a protocol in mind. Sometimes there’s documentation for the protocol. But there’s no mechanism that keeps implementation and documentation in sync. Bugs occur when protocols diverge.]]></summary></entry><entry><title type="html">Using F* to Formally Verify Programs</title><link href="https://maxtaylor.dev/posts/2023/05/fstar-leetcode" rel="alternate" type="text/html" title="Using F* to Formally Verify Programs" /><published>2023-05-20T00:00:00-04:00</published><updated>2023-05-20T00:00:00-04:00</updated><id>https://maxtaylor.dev/posts/2023/05/leetcode-fstar</id><content type="html" xml:base="https://maxtaylor.dev/posts/2023/05/fstar-leetcode"><![CDATA[<p>Formal methods are currently not widely embraced due to their
perceived difficulty. However, the landscape is changing with the
emergence of new technologies that make formal methods more accessible
than ever before. <a href="https://www.fstar-lang.org/">F*</a>, developed by Microsoft Research, is a
groundbreaking functional language that combines dependent types and
proof-oriented features. By bridging the gap between programming and
proving, F* facilitates a gradual adoption of formal methods by
software engineers. In this post, I will provide an introduction to the
basics of F* and demonstrate how we can leverage its
capabilities to verify a solution to a LeetCode problem. I can’t cover
all the background material needed to understand F* in this post. I
assume that you have some experience in a language like Haskell or
OCaml.</p>

<p>This writing has three goals. First, I want to showcase how far formal
methods have come. Second, there is not a lot of material discussing
how to use formal methods, and particularly F*. I hope others are
able to learn from my mistakes, and newcomers can pick up some
proof-engineering strategies. Finally, I want to draw attention to
some current pain-points for the sake of improving current formal
methods research.</p>

<p><a href="https://www.fstar-lang.org/tutorial/">The F* tutorial</a> has an editor
you can interact with in your browser. You can follow along with these
examples there, without downloading any additional software.</p>

<p>Contents:</p>
<ol>
  <li><a href="#fstar-basics">Basics of F*</a>
    <ul>
      <li><a href="#fstar-basics-functions">Functions</a></li>
      <li><a href="#dependent-types">Dependent Types</a></li>
      <li><a href="#assertions-and-tactics">Assertions and Tactics</a></li>
    </ul>
  </li>
  <li><a href="#leetcode-problem">LeetCode Problem</a>
    <ul>
      <li><a href="#solution-idea">Solution Design</a></li>
      <li><a href="#fstar-modeling">Modeling the Problem in F*</a></li>
      <li><a href="#fstar-proof">Proof of Correctness</a></li>
    </ul>
  </li>
  <li><a href="#takeaways">Takeaways</a></li>
</ol>

<h2 id="basics-of-f-">Basics of F* <a name="fstar-basics"></a></h2>

<p>F* is a complex language, and I am but a journeyman. The purpose of
this section is only to familiarize you, gentle reader, with enough
F* to broadly understand this post’s verification efforts. If you are
interested in learning more, check out the <a href="http://www.fstar-lang.org/tutorial/">F* tutorial</a>.</p>

<h3 id="functions-">Functions <a name="fstar-basics-functions"></a></h3>

<p>F* is inspired by ML languages. You can define simple functions like
this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let double (x: int) : int
    = x + x
</code></pre></div></div>
<p>This just defines a function called <code class="language-plaintext highlighter-rouge">double</code> that accepts an <code class="language-plaintext highlighter-rouge">int</code> as
a parameter, and returns an <code class="language-plaintext highlighter-rouge">int</code>. Note that in F* <code class="language-plaintext highlighter-rouge">int</code> refers to a
<em>mathematical integer</em>, not a fixed-size integer as in C. This means
that the value of <code class="language-plaintext highlighter-rouge">x</code> can be arbitrarily large (small).</p>

<p>Note that we may want to define double like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let double (x: int) : int
    = x * 2
</code></pre></div></div>
<p>But this simple definition won’t work because <code class="language-plaintext highlighter-rouge">*</code> is reserved by F*
for constructing tuples. F* tells us this fact with an informative
error message:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(Error 189) Expected expression of type "Type"; got expression "x" of
type "Prims.int"
</code></pre></div></div>

<p>Instead, we have to import a definition that
redfines <code class="language-plaintext highlighter-rouge">*</code> to refer to multiplication. We do this by <em>opening</em> a
module. This definition works:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>open FStar.Mul

let double (x: int) : int
    = x * 2
</code></pre></div></div>

<h3 id="dependent-types-">Dependent Types <a name="dependent-types"></a></h3>

<p>In a dependently typed programming language, types are permitted to
depend on values. Let’s consider the <code class="language-plaintext highlighter-rouge">double</code> example:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let double (x: int) : (result: int{result = x + x})
    = x * 2
</code></pre></div></div>
<p>We changed the return type of <code class="language-plaintext highlighter-rouge">double</code> to <code class="language-plaintext highlighter-rouge">(result: int{result = x +
x})</code>. This is called a <em>refinement type</em>. This is a dependent type
because the type <em>depends</em> on the value of <code class="language-plaintext highlighter-rouge">x</code> (as well as the return
value of <code class="language-plaintext highlighter-rouge">double</code>). Note that there is nothing special about the name
<code class="language-plaintext highlighter-rouge">result</code> – we just needed a name to refer to the return value of
<code class="language-plaintext highlighter-rouge">double</code> in the refinement type. Any name would work.</p>

<p>Interestingly, notice that <code class="language-plaintext highlighter-rouge">x + x</code> is not syntactically the same as
<code class="language-plaintext highlighter-rouge">x * 2</code>. F* is aware of the semantics of the <code class="language-plaintext highlighter-rouge">*</code> operator and the <code class="language-plaintext highlighter-rouge">+</code>
operator, and automatically proved that <code class="language-plaintext highlighter-rouge">x * 2 = x + x</code>. This
highlights the power of F*: Many facts can be proven with little
effort.</p>

<h3 id="assertions-and-tactics-">Assertions and Tactics <a name="assertions-and-tactics"></a></h3>
<p>In F*, <code class="language-plaintext highlighter-rouge">assert</code> statements check that a condition is true at
<em>proof-time</em> (i.e., before the code runs). This is done by proving the
condition asserted. Here is a simple example:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let _ = assert (true)
</code></pre></div></div>
<p>Of course, the proposition <code class="language-plaintext highlighter-rouge">true</code> is always provable (<code class="language-plaintext highlighter-rouge">true</code> is
<code class="language-plaintext highlighter-rouge">true</code>).</p>

<p>Here’s an example of a proposition that cannot be proved:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let _ = assert (false)
</code></pre></div></div>
<p>This produces this error message:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(Error 19) assertion failed; The SMT solver could not prove the query. Use --query_stats for more details.
</code></pre></div></div>
<p>Of course <code class="language-plaintext highlighter-rouge">false</code> cannot be proved (<code class="language-plaintext highlighter-rouge">false</code> is never <code class="language-plaintext highlighter-rouge">true</code>).</p>

<p>These examples are rather boring. Let’s consider an example that uses
more interesting pieces of logic:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let _ = assert (forall (x: nat) (y: nat) .
                y &gt;= x ==&gt; 
                    (exists (z: nat) .
                        y = x + z))
</code></pre></div></div>
<p>In more familiar logic, we’d write this as $\forall x, y . y &gt;= x
\implies \exists z . y = x + z$.</p>

<p>But if we try to verify our assertion with F*, it fails:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(Error 19) assertion failed; The SMT solver could not prove the query. Use --query_stats for more details.
</code></pre></div></div>

<p>Under the hood, F* uses the <a href="https://github.com/Z3Prover/z3">Z3 SMT
solver</a> to perform proofs. While Z3 is
powerful, no theorem prover can automatically prove all theorems. Z3
appears stuck here. Let’s try adding hints to help Z3 get unstuck:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>open FStar.Mul
open FStar.Tactics

let _ = assert (forall (x: nat) (y: nat) .
                y &gt;= x ==&gt; 
                    (exists (z: nat) .
                        y = x + z))
        by (
            let x = forall_intro () in
            let y = forall_intro () in
            let imp = implies_intro () in
                witness (`(`#y - `#x));
                dump "after witness"
        )
</code></pre></div></div>
<p>We provide hints by using <em>tactics</em>, which are programs that
manipulate proofs. Every proof has 1 or more <em>goals</em>, or statements
that we need to show are true. Tactics use known facts to simplify
goals. This example shows a few tactics:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">forall_intro</code> introduces the first variable quantified by forall to
the set of known facts (i.e., the variable exists and has the
specified type). As a really simple example, <code class="language-plaintext highlighter-rouge">forall_intro</code>
transforms a goal like $\vdash \forall (x: \mathbb{N}) . x = x$ into $(x : \mathbb{N})
\vdash x = x$.</li>
  <li><code class="language-plaintext highlighter-rouge">implies_intro</code> adds the antecedent of an implication to the set of facts known to
  the theorem prover. To prove $\Gamma \vdash a \implies b$, it is
  sufficient to show $\Gamma, a \vdash b$.</li>
  <li><code class="language-plaintext highlighter-rouge">witness</code> helps us manipulate existence quantifiers. <code class="language-plaintext highlighter-rouge">witness</code> adds
a term that shows an object with a given property exists. Here, our
witness to the existential quantifier is <code class="language-plaintext highlighter-rouge">y - x</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">dump</code> is an extremely useful tactic. It shows the current goals
that need to be proved.</li>
</ul>

<p>Dump shows us this message:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Goal 1/2:
(x: Prims.nat), (x'0: Prims.nat), (_: x'0 &gt;= x) |- _ : Prims.squash (x'0 - x &gt;= 0 == true)

Goal 2/2:
(x: Prims.nat), (x'0: Prims.nat), (_: x'0 &gt;= x) |- _ : Prims.squash
(x'0 = x + (x'0 - x))
</code></pre></div></div>

<p>If you read these goals for a second, they should seem obviously
true. F* is quite easy to use: If you think something is obvious,
just stop talking and see if F* completes the proof:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>open FStar.Mul
open FStar.Tactics

let _ = assert (forall (x: nat) (y: nat) .
                y &gt;= x ==&gt; 
                    (exists (z: nat) .
                        y = x + z))
        by (
            let x = forall_intro () in
            let y = forall_intro () in
            let imp = implies_intro () in
                witness (`(`#y - `#x))
        )
</code></pre></div></div>

<p>In this case, it does.</p>

<h2 id="leetcode-problem-capacity-to-ship-packages-within-d-days-"><a href="https://leetcode.com/problems/capacity-to-ship-packages-within-d-days/">LeetCode Problem: Capacity to Ship Packages Within D Days</a> <a name="leetcode-problem"></a></h2>
<p>The problem we’re going to solve and verify is the <a href="https://leetcode.com/problems/capacity-to-ship-packages-within-d-days/">Capacity to
Ship Packages within D
Days problem</a>. You’re
given <code class="language-plaintext highlighter-rouge">weights</code> (an array of positive numbers representing the weights of
items), and <code class="language-plaintext highlighter-rouge">days</code> (the maximum number of days you have to ship all
the items). These items must be loaded onto a ship with a capacity of
<code class="language-plaintext highlighter-rouge">capacity</code>. The challenge is to find the smallest value of <code class="language-plaintext highlighter-rouge">capacity</code>
so that the number of days required to ship the items is less than or
equal to <code class="language-plaintext highlighter-rouge">days</code>. Check out the LeetCode description for more details.</p>

<h3 id="solution-design-">Solution Design <a name="solution-idea"></a></h3>
<p>Clearly, the minimum capacity that <em>might</em> work is the maximum element
of <code class="language-plaintext highlighter-rouge">weights</code>. For, if the capacity were any smaller, it would be
impossible to ship the largest item. The largest capacity we should
consider is the sum of the item weights. Any larger capacity is
superfluous, since a ship with this capacity can already ship all the
items in 1 day. The correct capacity is therefore somewhere in the
range $[maximum\_element~ weights,~ sum~ weights]$.</p>

<p>The naive approach is to simply check every weight in this range. But
this number could be quite large – for instance, when the number of
items is large but the maximum weight is small. A smarter approach is
to use binary search to find the correct capacity.</p>

<p>To be frank, I find that getting the bounds of binary search right to
be a little tricky. For tricky loop bounds, I craft <a href="https://en.wikipedia.org/wiki/Loop_invariant">loop
invariants</a> to help me
write the code. Let $min\_elt$ denote the smallest capacity that
maybe could ship the items, and $max\_elt$ denote the largest
capacity we should consider. We will maintain two key invariants:</p>
<ol>
  <li>
    <p>$\forall x . x &lt; min\_elt \implies time\_to\_ship~ weights~ x &gt;
days$.</p>
  </li>
  <li>
    <p>$\forall x . x &gt;= max\_elt \implies time\_to\_ship~ weights~ x &lt;=
days$.</p>
  </li>
</ol>

<p>Under these invariants, when $min\_elt = max\_elt$, the correct
capacity to return is $min\_elt$ (or, equivalently, $max\_elt$).</p>

<h3 id="modeling-the-problem-in-f-">Modeling the Problem in F* <a name="fstar-modeling"></a></h3>

<h4 id="days-to-ship-items-given-a-capacity">Days to Ship Items Given a Capacity</h4>
<p>Let’s start by computing the number of days it takes to ship items
with <code class="language-plaintext highlighter-rouge">weights</code> weights given a capacity <code class="language-plaintext highlighter-rouge">capacity</code>. We’ll represent
<code class="language-plaintext highlighter-rouge">weights</code> as a non-empty list of natural numbers. F*
already provides a theory of lists, so we’ll use that.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module Capacity

open FStar.List
open FStar.List.Tot
open FStar.Tactics

let weight_list = (l:list nat{not (isEmpty l)})
</code></pre></div></div>

<p>The syntax <code class="language-plaintext highlighter-rouge">list nat</code> describes a list of natural numbers. We use a
refinement type to specify that the list is non-empty.</p>

<p>Here’s a function definition that returns the number of days it takes
to ship some items:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let rec max_elt (l: weight_list) : nat =
  match l with
    | [x] -&gt; x
    | (x::xs) -&gt; 
      let max' = max_elt xs in
        if x &gt;= max' then x
        else max'

let rec days_to_ship' (weights: weight_list)
                      (capacity: nat{capacity &gt;= (max_elt weights)}) 
                      (current_cap: nat) 
                      : (x: nat{x &gt;= 1})
  = 
  match weights with
    | [x] -&gt; 
      if x &lt;= current_cap then 1
      else 2
    | (x::xs) -&gt;
      if x &lt;= current_cap then
        days_to_ship' xs capacity (current_cap - x)
      else
        1 + (days_to_ship' xs capacity capacity)

let days_to_ship (weights: weight_list) 
                 (capacity: nat{capacity &gt;= (max_elt weights)}) 
                 : (x: nat{x &gt;= 1})
  = days_to_ship' weights capacity capacity
</code></pre></div></div>

<p>A few notes about these functions:</p>
<ul>
  <li>In F*, we must explicitly denote when functions are recursive by
using the <code class="language-plaintext highlighter-rouge">let rec</code> syntax.</li>
  <li>The <code class="language-plaintext highlighter-rouge">match</code> syntax performs pattern-matching. Inside of <code class="language-plaintext highlighter-rouge">max_elt</code>,
<code class="language-plaintext highlighter-rouge">[x]</code> matches with a list containing exactly 1 item. The second
match case <code class="language-plaintext highlighter-rouge">(x::xs)</code> matches with an item consed into any list. Note
that these patterns are exhaustive since a weight list is
non-empty. Also note that F* verifies this exhaustivity for us, automatically.</li>
  <li>Notice the use of the refinement type on the <code class="language-plaintext highlighter-rouge">capacity</code>
parameter. This is applying our earlier argument: The minimum
capacity we can use to ship the items is the maxmium weight of the
items.</li>
</ul>

<h4 id="defining-the-solution-function">Defining the Solution Function</h4>
<p>Here’s the implementation of our solution function in F*:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let nat_sum (a: nat) (b: nat) : nat = a + b

let sum_of_weights (weights: weight_list) : nat = 
  List.Tot.fold_left nat_sum (hd weights) (tl weights)

let rec lemma_sum_of_weights_is_gte_max (weights: weight_list) :
  Lemma (ensures (sum_of_weights weights) &gt;= max_elt weights)
  =
  match weights with
    | [w] -&gt; ()
    | (x::xs) -&gt;
        FStar.List.Tot.Properties.fold_left_monoid nat_sum 0 xs;
        lemma_sum_of_weights_is_gte_max xs

let min_bound (weights: weight_list) : nat = max_elt weights

let max_bound (weights: weight_list) : nat = sum_of_weights weights

// Returns the minimum capacity necessary to ship all the items in `days` days.
// Note that we have to specify that we decrease the difference between max_cap and min_cap.
let rec ship_within_days' (weights: weight_list) 
                          (days: nat{days &gt; 0})
                          (min_cap: nat{min_cap &gt;= min_bound weights})
                          (max_cap: nat{max_cap &gt;= min_cap})
                          : Tot (n:nat{n &gt;= min_cap /\ n &lt;= max_cap}) (decreases max_cap - min_cap)
    =
    if min_cap = max_cap then
      min_cap
    else
      let middle_cap = (min_cap + max_cap) / 2 in
      let total_days = days_to_ship weights middle_cap in
      if total_days &gt; days then
        ship_within_days' weights days (middle_cap + 1) max_cap
      else
        ship_within_days' weights days min_cap middle_cap

let ship_within_days (weights: weight_list) (days: nat{days &gt; 0}) 
  : (n:nat{n &gt;= min_bound weights /\ n &lt;= max_bound weights})
  = lemma_sum_of_weights_is_gte_max weights;
    ship_within_days' weights 
                      days
                      (max_elt weights)
                      (sum_of_weights weights)
</code></pre></div></div>

<p>The heart of the implementation is <code class="language-plaintext highlighter-rouge">ship_within_days'</code>, so we’ll start
there. This is a fairly simple binary search implementation. Again,
we’re just maintaining the 2 invariants discussed in the <a href="#solution-idea">Solution
Design</a> subsection. Try to go through the logic and see
why those invariants are maintained.</p>

<p>The first bit of new syntax we’ll discuss is the return type of
<code class="language-plaintext highlighter-rouge">ship_within_days'</code>. It returns <code class="language-plaintext highlighter-rouge">Tot (n:nat{n &gt;= min_cap /\ n &lt;=
max_cap}) (decreases max_cap - min_cap)</code>. In F*, all functions must
be total – meaning they must terminate. So, really, the type of <code class="language-plaintext highlighter-rouge">double</code> <a href="fstar-basics-functions">from
earlier</a> is</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>val double (x: int) : Tot int
</code></pre></div></div>
<p>But F* nicely writes <code class="language-plaintext highlighter-rouge">Tot</code> for us. Unfortunately, F* doesn’t know
<em>why</em> the function <code class="language-plaintext highlighter-rouge">ship_within_days'</code> terminates. We explain it:
Because <code class="language-plaintext highlighter-rouge">max_cap - min_cap</code> always decreases. F* <em>can</em> see that this
statement is true, and then accepts our function as terminating. If we
delete <code class="language-plaintext highlighter-rouge">(decreases max_cap - min_cap)</code> from our code, F* produces
this error:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Could not prove termination of this recursive call; The SMT solver could not prove the query. Use --query_stats for more details.
</code></pre></div></div>
<p>This is our cue to add the <code class="language-plaintext highlighter-rouge">decreases</code> expression.</p>

<p>Our primary solution function is <code class="language-plaintext highlighter-rouge">ship_within_days</code>. There’s one bit
of magic in it: The application of the lemma
<code class="language-plaintext highlighter-rouge">lemma_sum_of_weights_is_gte_max</code>. This is required because we used a
refinement type for <code class="language-plaintext highlighter-rouge">max_cap</code> that requires <code class="language-plaintext highlighter-rouge">max_cap &gt;=
min_cap</code>. Unfortunately, F* cannot automatically prove that
<code class="language-plaintext highlighter-rouge">(sum_of_weights weights) &gt;= (max_elt weights)</code>, so type checking
fails if we delete the application of the lemma:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Subtyping check failed; expected type max_cap: Prims.nat{max_cap &gt;= max_elt weights}; got type Prims.nat; The SMT solver could not prove the query.
</code></pre></div></div>
<p>In general, F* cannot automatically prove propositions that require
induction. But once we apply the lemma, F* can easily verify that the
types are correct.</p>

<p>Now, let’s discuss our <code class="language-plaintext highlighter-rouge">max_bound</code> implementation for a moment. As we
mentioned in <a href="#solution-idea">the Solution Design</a>, the maximum bound on the
weights is just the sum of all weights. To sum the weights, we use the standard
<code class="language-plaintext highlighter-rouge">fold_left</code> function that should be familiar to functional
programmers. Note that we <strong>cannot</strong> write <code class="language-plaintext highlighter-rouge">sum_of_weights</code> like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Error
let sum_of_weights (weights: weight_list) : nat = 
  List.Tot.fold_left (+) (hd weights) (tl weights)
</code></pre></div></div>
<p>This is because the type of <code class="language-plaintext highlighter-rouge">+</code> is <code class="language-plaintext highlighter-rouge">int -&gt; int -&gt; int</code>. While <code class="language-plaintext highlighter-rouge">nat</code> is
a subtype of <code class="language-plaintext highlighter-rouge">int</code>, F*’s type checking algorithm does not induce <code class="language-plaintext highlighter-rouge">int
-&gt; int -&gt; int</code> will produce a <code class="language-plaintext highlighter-rouge">nat</code>. To solve this problem, we
explicitly define <code class="language-plaintext highlighter-rouge">nat_sum</code>.</p>

<p>Finally, <code class="language-plaintext highlighter-rouge">lemma_sum_of_weights_is_gte_max</code> procedes by induction. We
use the <code class="language-plaintext highlighter-rouge">Lemma (...)</code> type because the function is a proof. In the
case where this is exactly 1 item in the list, we produce the value
<code class="language-plaintext highlighter-rouge">()</code>. This term has a type of <code class="language-plaintext highlighter-rouge">unit</code>. In F*, the type <code class="language-plaintext highlighter-rouge">Lemma (ensures
(sum_of_weights weights) &gt;= max_elt weights)</code> is really just a synonym
for the type <code class="language-plaintext highlighter-rouge">u:unit{(sum_of_weights weights) &gt;= max_elt
weights}</code>. So, F* will automatically try (and succeed!) to show our
lemma is true.</p>

<p>In the case when there is more than 1 item in the list, we first apply
<a href="https://github.com/FStarLang/FStar/blob/f781540098be4090ecca612fcb9cc1879aec6b62/ulib/FStar.List.Tot.Properties.fst#L840"><code class="language-plaintext highlighter-rouge">FStar.List.Tot.Properties.fold_left_monoid</code></a>. This
establishes the fact that <code class="language-plaintext highlighter-rouge">nat_sum (x::xs) = x + nat_sum xs</code>. The
following line (<code class="language-plaintext highlighter-rouge">lemma_sum_of_weights_is_gte_max xs</code>) convinces F*
that the lemma holds by induction. As an exercise: Look at lemma
<code class="language-plaintext highlighter-rouge">fold_left_monoid</code> provides and consider why we didn’t use this
definition:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Error
let sum_of_weights (weights: weight_list) : nat = 
  List.Tot.fold_left nat_sum 0 weights
</code></pre></div></div>

<h3 id="proof-of-correctness-">Proof of Correctness <a name="fstar-proof"></a></h3>
<p>There are two facts we want to prove:</p>
<ol>
  <li>Our solution ships all the items within <code class="language-plaintext highlighter-rouge">days</code> days.</li>
  <li>Any capacity smaller than the one returned by our solution does not
ship items within <code class="language-plaintext highlighter-rouge">days</code> days. I.e., our solution is minimal.</li>
</ol>

<p>In fact, these statements are direct consequences of the 2 invariants
we constructed in <a href="#solution-idea">our design subsection</a>. So, let’s
start by writing these invariants in F*:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let min_bound_invariant (weights: weight_list) 
                        (cap: nat{cap &gt;= min_bound weights})
                        (days: nat{days &gt; 0})
  = forall (x : nat) . x &gt;= min_bound weights /\ x &lt; cap ==&gt; days_to_ship weights x &gt; days

let max_bound_invariant (weights: weight_list)
                        (cap: nat{cap &gt;= min_bound weights}) 
                        (days: nat{days &gt; 0})
  = forall (x : nat) . x &gt;= cap ==&gt; days_to_ship weights x &lt;= days
</code></pre></div></div>

<p>Let’s also define the concept of minimality:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let is_minimal (w: weight_list) (c: nat{c &gt;= min_bound w}) (days: nat{days &gt; 0}) = 
  c = min_bound w \/ (c &gt; min_bound w /\ days_to_ship w (c - 1) &gt; days)
</code></pre></div></div>

<p>The proof follows from induction. We’ll start by drawing the outline
of the proof, then fill in details until it is complete. To start the
proof:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
let rec lemma_ship_within_days'_ships_within_days (weights: weight_list) 
                                                  (days: nat{days &gt; 0})
                                                  (min_cap: nat{min_cap &gt;= min_bound weights})
                                                  (max_cap: nat{max_cap &gt;= min_cap})
  : Lemma 
    (requires min_bound_invariant weights min_cap days /\ 
              max_bound_invariant weights max_cap days)
    (ensures (days_to_ship weights (ship_within_days' weights days min_cap max_cap)) &lt;= days /\
             is_minimal weights (ship_within_days' weights days min_cap max_cap) days)
    (decreases max_cap - min_cap)
  = 
  if min_cap = max_cap then
     ()
  else 
     admit ()
</code></pre></div></div>

<p>Notice the new <code class="language-plaintext highlighter-rouge">requires</code> component of the <code class="language-plaintext highlighter-rouge">Lemma</code> type. The requires
and ensures clauses of Lemma are
<a href="https://en.wikipedia.org/wiki/Precondition">preconditions</a> and
<a href="https://en.wikipedia.org/wiki/Postcondition">postconditions</a>
respectively. Our strategy is to require that our 2 invariants hold at
each call to <code class="language-plaintext highlighter-rouge">lemma_ship_within_days'_ships_within_days</code>. Then, it
is obvious that the postconditions hold. Indeed: Notice that F*
automatically finds a proof when <code class="language-plaintext highlighter-rouge">min_cap = max_cap</code>. On the other
hand, we use <code class="language-plaintext highlighter-rouge">admit ()</code> in the <code class="language-plaintext highlighter-rouge">else</code> branch. F* programs that
contain <code class="language-plaintext highlighter-rouge">admit ()</code> aren’t proofs at all - <code class="language-plaintext highlighter-rouge">admit ()</code> forces F* to
accept the current goals as true (even if it they are false). However, it’s
invaluable when building proofs.</p>

<p>Let’s zoom in further by applying the definition of <code class="language-plaintext highlighter-rouge">ship_within_days</code>
in the else branch:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Error
let rec lemma_ship_within_days'_ships_within_days (weights: weight_list) 
                                                  (days: nat{days &gt; 0})
                                                  (min_cap: nat{min_cap &gt;= min_bound weights})
                                                  (max_cap: nat{max_cap &gt;= min_cap})
  : Lemma 
    (requires min_bound_invariant weights min_cap days /\ 
              max_bound_invariant weights max_cap days)
    (ensures (days_to_ship weights (ship_within_days' weights days min_cap max_cap)) &lt;= days /\
             is_minimal weights (ship_within_days' weights days min_cap max_cap) days)
    (decreases max_cap - min_cap)
  = 
  if min_cap = max_cap then
     ()
  else 
    let middle_cap = (min_cap + max_cap) / 2 in
    let total_days = days_to_ship weights middle_cap in
    if total_days &gt; days then (
       lemma_ship_within_days'_ships_within_days weights days (middle_cap + 1) max_cap
    ) else (
        admit ()
    )
</code></pre></div></div>

<p>Unfortunately, verification fails at this point:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(Error 19) assertion failed; The SMT solver could not prove the query. Use --query_stats for more details.
</code></pre></div></div>

<p>Frankly, this error message is pretty awful. Hopefully, it is clear
that if <code class="language-plaintext highlighter-rouge">lemma_ship_within_days'_ships_within_days</code> <em>can</em> be applied
in the body of <code class="language-plaintext highlighter-rouge">if total_days &gt; days</code> then postcondition holds. This
should lead us to suspect that the problem is that F* cannot prove
the preconditions of <code class="language-plaintext highlighter-rouge">lemma_ship_within_days'_ships_within_days</code> holds
at this point. Let’s add a temporary assert statement to check on the
precondition:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Error
let rec lemma_ship_within_days'_ships_within_days (weights: weight_list) 
                                                  (days: nat{days &gt; 0})
                                                  (min_cap: nat{min_cap &gt;= min_bound weights})
                                                  (max_cap: nat{max_cap &gt;= min_cap})
  : Lemma 
    (requires min_bound_invariant weights min_cap days /\ 
              max_bound_invariant weights max_cap days)
    (ensures (days_to_ship weights (ship_within_days' weights days min_cap max_cap)) &lt;= days /\
             is_minimal weights (ship_within_days' weights days min_cap max_cap) days)
    (decreases max_cap - min_cap)
  = 
  if min_cap = max_cap then
     ()
  else 
    let middle_cap = (min_cap + max_cap) / 2 in
    let total_days = days_to_ship weights middle_cap in
    if total_days &gt; days then (
       assert (min_bound_invariant weights (middle_cap + 1) days);
       lemma_ship_within_days'_ships_within_days weights days (middle_cap + 1) max_cap
    ) else (
        admit ()
    )
</code></pre></div></div>

<p>F* still prints an assertion failed error, but now it points to the
line checking the precondition. So, we know that the problem is that
F* cannot prove <code class="language-plaintext highlighter-rouge">min_bound_invariant</code> on <code class="language-plaintext highlighter-rouge">(middle_cap + 1)</code>. We know
that <code class="language-plaintext highlighter-rouge">maximum_bound_invariant</code> must continue to hold.</p>

<p>Observe that <code class="language-plaintext highlighter-rouge">min_bound_invariant</code> holds because <code class="language-plaintext highlighter-rouge">days_to_ship</code> is
decreasing: If we decrease the capacity, we will increase the days to
ship, and the condition <code class="language-plaintext highlighter-rouge">if total_days &gt; days</code> already has proven that
we cannot ship at the capacity <code class="language-plaintext highlighter-rouge">middle_cap</code>. We just need to show F*
these facts are true:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let rec lemma_days_to_ship_is_decreasing'' (weights: weight_list) 
                                           (cap: nat{cap &gt;= (max_elt weights)})
                                           (ccap: nat{ccap &lt;= cap})
                                           (ccap1: nat{ccap1 &gt; ccap /\ ccap1 &lt;= cap + 1})
  : Lemma (ensures days_to_ship' weights (cap + 1) ccap1 &lt;= (days_to_ship' weights cap ccap))
  =
  match weights with
    | [w] -&gt; ()
    | x::xs -&gt; 
      if x &lt;= ccap &amp;&amp; x &lt;= ccap1 then
        lemma_days_to_ship_is_decreasing'' xs cap (ccap - x) (ccap1 - x)
      else if x &gt; ccap &amp;&amp; x &lt;= ccap1 then
        lemma_days_to_ship_is_decreasing' xs cap cap (ccap1 - x)
      else if x &gt; ccap &amp;&amp; x &gt;= ccap1 then
        lemma_days_to_ship_is_decreasing'' xs cap cap (cap + 1)

and lemma_days_to_ship_is_decreasing' (weights: weight_list) 
                                      (cap: nat{cap &gt;= (max_elt weights)})
                                      (ccap: nat{ccap &lt;= cap})
                                      (ccap1: nat{ccap1 &lt;= cap + 1})
  : Lemma (ensures days_to_ship' weights (cap + 1) ccap1 &lt;= 1 + (days_to_ship' weights cap ccap))
  =
  match weights with
    | [w] -&gt; ()
    | x::xs -&gt;
      if x &lt;= ccap &amp;&amp; x &lt;= ccap1 then
        lemma_days_to_ship_is_decreasing' xs cap (ccap - x) (ccap1 - x)
      else if x &gt; ccap &amp;&amp; x &gt; ccap1 then
        lemma_days_to_ship_is_decreasing' xs cap cap (cap + 1)
      else if x &gt; ccap &amp;&amp; x &lt;= ccap1 then
        lemma_days_to_ship_is_decreasing' xs cap cap (ccap1 - x)
      else
        // I.e., x &lt;= ccap &amp;&amp; x &gt; ccap1
        lemma_days_to_ship_is_decreasing'' xs cap (ccap - x) (cap + 1)

let lemma_days_to_ship_is_decreasing (weights: weight_list) 
                                     (cap: nat{cap &gt;= (max_elt weights)})
                                     (c_cap: nat{c_cap &lt;= cap})
  : Lemma (ensures days_to_ship' weights (cap + 1) (c_cap + 1) &lt;= days_to_ship' weights cap c_cap)
  =
  lemma_days_to_ship_is_decreasing'' weights cap c_cap (c_cap + 1)
</code></pre></div></div>

<p>Despite the coinductive proof, this is a simple argument. The theorem
that we are primarily interested in is
<code class="language-plaintext highlighter-rouge">lemma_days_to_ship_is_decreasing''</code>. This follows from
induction. There is a wrinkle, though: In the <code class="language-plaintext highlighter-rouge">else if x &gt; ccap &amp;&amp; x
&lt;= ccap1</code> branch. In this case, the preconditions of
<code class="language-plaintext highlighter-rouge">lemma_days_to_ship_is_decreasing''</code> are no longer met. So, we use
coinduction to show that <code class="language-plaintext highlighter-rouge">days_to_ship' weights (cap + 1) ccap1 &lt;= 1 +
(days_to_ship' weights cap ccap)</code>. Then, since <code class="language-plaintext highlighter-rouge">days_to_ship weights
cap ccap = 1 + days_to_ship xs cap cap</code>, F* is automatically
able to cancel the <code class="language-plaintext highlighter-rouge">1</code>s and prove our theorem. A similar argument
applies to <code class="language-plaintext highlighter-rouge">lemma_days_to_ship_is_decreasing'</code>.</p>

<p>But even armed with this theorem, F* <em>still</em> can’t prove the
precondition. Try it. We’ll have to go even further:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let lemma_days_to_ship_is_decreasing_full (weights: weight_list) (cap: nat{cap &gt;= (max_elt weights)})
  : Lemma (ensures days_to_ship weights (cap + 1) &lt;= days_to_ship weights cap)
  = 
  lemma_days_to_ship_is_decreasing weights cap cap


let rec lemma_days_to_ship_is_decreasing2 (weights: weight_list) (c: nat{c &gt;= min_bound weights})
  : Lemma (ensures (forall (x : nat) . x &gt;= min_bound weights /\ x &lt; c ==&gt; 
                      days_to_ship weights x &gt;= days_to_ship weights c))
  = if c &gt; min_bound weights then (
       lemma_days_to_ship_is_decreasing_full weights (c -1);
       lemma_days_to_ship_is_decreasing2 weights (c - 1)
    )

let rec lemma_ship_within_days'_ships_within_days (weights: weight_list) 
                                                  (days: nat{days &gt; 0})
                                                  (min_cap: nat{min_cap &gt;= min_bound weights})
                                                  (max_cap: nat{max_cap &gt;= min_cap})
  : Lemma 
    (requires min_bound_invariant weights min_cap days /\ 
              max_bound_invariant weights max_cap days)
    (ensures (days_to_ship weights (ship_within_days' weights days min_cap max_cap)) &lt;= days /\
             is_minimal weights (ship_within_days' weights days min_cap max_cap) days)
    (decreases max_cap - min_cap)
  = 
  if min_cap = max_cap then
     ()
  else 
    let middle_cap = (min_cap + max_cap) / 2 in
    let total_days = days_to_ship weights middle_cap in
    if total_days &gt; days then (
       lemma_days_to_ship_is_decreasing2 weights middle_cap;
       lemma_ship_within_days'_ships_within_days weights days (middle_cap + 1) max_cap
    ) else (
        admit ()
    )
</code></pre></div></div>

<p>As you might guess, F* has a similar problem with the
<code class="language-plaintext highlighter-rouge">max_bound_invariant</code>. The problem is that the invariant requires
<em>all</em> capacities greater than <code class="language-plaintext highlighter-rouge">max_cap</code> to ship in less than or equal
to <code class="language-plaintext highlighter-rouge">days</code>, but our decreasing lemma only applies to <code class="language-plaintext highlighter-rouge">max_cap + 1</code>. Our
proof strategy is to use induction to extend our original decreasing lemma to show
$\forall k : \mathbb{N} . days\_to\_ship~ weights~ (capacity + k) &lt;=
days\_to\_ship~ weights~ capacity$.
This argument convinces F*:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
let rec lemma_days_to_ship_is_decreasing3'' (w: weight_list) (c : nat{c &gt;= min_bound w}) (k: nat)
  : Lemma (ensures days_to_ship w (c + k) &lt;= days_to_ship w c)
  = 
  if k = 0 then ()
  else (
    lemma_days_to_ship_is_decreasing_full w (c + k - 1);
    lemma_days_to_ship_is_decreasing3'' w c (k - 1)
  )

let lemma_days_to_ship_is_decreasing3' (w: weight_list) (c : nat{c &gt;= min_bound w})
  : Lemma (ensures forall (k : nat) . days_to_ship w (c + k) &lt;= days_to_ship w c)
  = 
  assert (forall (w: weight_list) (c: nat{c &gt;= min_bound w}) (k : nat) . 
            days_to_ship w (c + k) &lt;= days_to_ship w c)
  by (
    let w = forall_intros () in
    mapply (`lemma_days_to_ship_is_decreasing3'' )
  )

let lemma_add_definition (c:nat)
  : Lemma (ensures (forall (x : nat) . x &gt;= c ==&gt; (exists (k : nat) . x = k + c)))
  =
  assert (forall (x : nat) . x &gt;= c ==&gt; x - c &gt;= 0 /\ x - c + c = x)

let lemma_days_to_ship_is_decreasing3 (weights: weight_list) (c: nat{c &gt;= min_bound weights})
  : Lemma (ensures forall (x : nat) . x &gt;= c ==&gt; days_to_ship weights x &lt;= days_to_ship weights c)
  =
    lemma_days_to_ship_is_decreasing3' weights c;
    lemma_add_definition c


let rec lemma_ship_within_days'_ships_within_days (weights: weight_list) 
                                                  (days: nat{days &gt; 0})
                                                  (min_cap: nat{min_cap &gt;= min_bound weights})
                                                  (max_cap: nat{max_cap &gt;= min_cap})
  : Lemma 
    (requires min_bound_invariant weights min_cap days /\ 
              max_bound_invariant weights max_cap days)
    (ensures (days_to_ship weights (ship_within_days' weights days min_cap max_cap)) &lt;= days /\
             is_minimal weights (ship_within_days' weights days min_cap max_cap) days)
    (decreases max_cap - min_cap)
  = 
  if min_cap = max_cap then
     ()
  else 
    let middle_cap = (min_cap + max_cap) / 2 in
    let total_days = days_to_ship weights middle_cap in
    if total_days &gt; days then (
       lemma_days_to_ship_is_decreasing2 weights middle_cap;
       lemma_ship_within_days'_ships_within_days weights days (middle_cap + 1) max_cap
    ) else (
      lemma_days_to_ship_is_decreasing3 weights middle_cap;
      lemma_ship_within_days'_ships_within_days weights days min_cap middle_cap
    )
</code></pre></div></div>

<p>As an exercise: It is up to the reader to demonstrate that the
<code class="language-plaintext highlighter-rouge">min_bound_invariant</code> and <code class="language-plaintext highlighter-rouge">max_bound_invariant</code> hold under the initial
conditions set by <code class="language-plaintext highlighter-rouge">ship_within_days</code>.</p>

<h2 id="takeaways-">Takeaways <a name="takeaways"></a></h2>

<h3 id="the-good">The Good</h3>
<p>F* has an amazing <a href="https://github.com/FStarLang/fstar-mode.el">Emacs
mode</a>. It uses unicode
symbols to make identifiers like <code class="language-plaintext highlighter-rouge">forall</code> and <code class="language-plaintext highlighter-rouge">exists</code> render as the
appropriate logic symbols. It also allows you to verify code as you
work inside of Emacs itself. Finally, it provides error squiggles.</p>

<p>F* can automatically find many proofs, more so than similar tools
that I’ve experimented with (e.g., Coq and Isabelle). In that sense,
F* seems easier to adopt than more mainstream tools.</p>

<h3 id="the-bad">The Bad</h3>
<p>Error messages are bad. <a href="https://maxtaylor.dev/publication/2022-08-01-sa4u">From my experience using
Z3</a>, this is
because Z3 does not generate very good unsatisfiable cores. To expand:
You provide Z3 a bunch of logical formulae. Z3 attempts to
find an <em>interpretation</em> (i.e., a mapping of variables to values) that
satisfies the formulae. When Z3 definitely cannot find an
interpretation, the formulae are <em>unsatisfiable</em>. For the sake of
error reporting, you might be interested in <em>why</em> formulae are
unsatisfiable. What is the smallest number of formulae you can remove
from the solver that makes the others satisfiable?</p>

<p>Unfortunately, things are not so simple for several reasons:</p>
<ol>
  <li>Z3 slows down when you enable the generation of unsatisfiable
cores.</li>
  <li>The unsatisfiable cores that Z3 generates are not minimal.</li>
  <li>Just because a formula appears in a minimal unsatisfiable core does
not mean that it necessarily is relevant to the fix.</li>
</ol>

<p>Meanwhile, tools that use Z3 have to somehow manage the relationship
between Z3 variables and their own semantic domain. This adds to the
challenge of making good error messages with Z3.</p>

<h3 id="the-ugly">The Ugly</h3>
<p>Z3 is sensitive to a lot more than you may expect. A common idiom in
F* is to test if adding a lemma helps you with a proof, like so:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let lemma_a (x: unit) : Lemma (ensures some_formula) = 
    admit ()

let lemma_b (x: unit) : Lemma (ensures some_formula) = 
    // Other lemmas not shown.
    lemma_a ();
    ()
</code></pre></div></div>

<p>Here, <code class="language-plaintext highlighter-rouge">lemma_b</code> uses <code class="language-plaintext highlighter-rouge">lemma_a</code> in its proof. Now, assume that Z3 is
able to find a proof of <code class="language-plaintext highlighter-rouge">lemma_b</code>. So, we proceed to prove
<code class="language-plaintext highlighter-rouge">lemma_a</code>. Very rarely, I have noticed that changing the proof of
<code class="language-plaintext highlighter-rouge">lemma_a</code> causes Z3 to no longer be able to prove
<code class="language-plaintext highlighter-rouge">lemma_b</code>. Obviously, this is surprising because the <code class="language-plaintext highlighter-rouge">lemma_b</code> does
not logically depend on the specific proof of <code class="language-plaintext highlighter-rouge">lemma_a</code>.</p>

<p>Documentation and examples are also lacking. There are not a lot of
high quality educational resources available today.</p>

<h2 id="conclusion">Conclusion</h2>
<p>I found F* to be immensely usable. While error messages are not the
best, this is really a limitation of the underlying SMT solver. From
experience, Z3’s unsatisfiable cores are complex to handle. And moving
back and forth from the high level language F* provides and SMT is
challenging. But this definitely an area that needs improvement.</p>

<p>The ecosystem of F* is young. The resources I’ve used are:</p>
<ul>
  <li>The source code on <a href="https://github.com/FStarLang/FStar">GitHub</a>. The
standard library is not really documented today. But, due to the
presence of preconditions/postconditions, the source is quite
readable. I have learned to make it a habit to consult the source
code for lemmas, like <code class="language-plaintext highlighter-rouge">FStar.List.Tot.Properties.fold_left_monoid</code>.</li>
  <li>The F* tutorial contains some decent examples.</li>
  <li>Read the papers. It’s okay to not understand everything – learn
what you can, save the paper, and eventually you’ll come back to and
things will make more sense.</li>
  <li>The book “Certified Programming with Dependent Types.”</li>
  <li>The book “Types and Programming Languages” provides good background
PL theory.</li>
  <li>The book “The Little Typer” provides a good background on dependent types.</li>
</ul>

<p>I hope that this post has inspired you to give F* a try.</p>]]></content><author><name>Max Taylor</name></author><category term="programming puzzles" /><category term="fstar" /><summary type="html"><![CDATA[Formal methods are currently not widely embraced due to their perceived difficulty. However, the landscape is changing with the emergence of new technologies that make formal methods more accessible than ever before. F*, developed by Microsoft Research, is a groundbreaking functional language that combines dependent types and proof-oriented features. By bridging the gap between programming and proving, F* facilitates a gradual adoption of formal methods by software engineers. In this post, I will provide an introduction to the basics of F* and demonstrate how we can leverage its capabilities to verify a solution to a LeetCode problem. I can’t cover all the background material needed to understand F* in this post. I assume that you have some experience in a language like Haskell or OCaml.]]></summary></entry><entry><title type="html">A LISP REPL Inside ChatGPT</title><link href="https://maxtaylor.dev/posts/2022/12/lisp-repl" rel="alternate" type="text/html" title="A LISP REPL Inside ChatGPT" /><published>2022-12-05T00:00:00-05:00</published><updated>2022-12-05T00:00:00-05:00</updated><id>https://maxtaylor.dev/posts/2022/12/lisp-in-gpt3</id><content type="html" xml:base="https://maxtaylor.dev/posts/2022/12/lisp-repl"><![CDATA[<p>TL;DR: ChatGPT is a LISP REPL.</p>

<p>Inspired by a <a href="https://www.engraved.blog/building-a-virtual-machine-inside/">recent post</a> where the author used ChatGPT as a virtual machine, I wanted to learn if <a href="https://chat.openai.com/chat">ChatGPT</a> can be a useful LISP interpreter. To my surprise, ChatGPT understands LISP remarkably well.</p>

<h2 id="lisp-basics">LISP Basics</h2>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/images/posts/2022-12-lisp-repl/basics.png" alt="Figure 1" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Figure 1: Initial prompt and basic LISP functions.</td>
    </tr>
  </tbody>
</table>

<p>Figure 1 shows the initial prompt I used. It’s very similar to the prompt in <em>Building A Virtual Machine inside ChatGPT</em>. We see a few interesting facts already:</p>
<ul>
  <li>ChatGPT understands that <code class="language-plaintext highlighter-rouge">NIL</code> evaluates to <code class="language-plaintext highlighter-rouge">NIL</code>.</li>
  <li>ChatGPT understands function application (at least, for arithmetic functions).</li>
  <li>ChatGPT understands some subtle semantics of Common Lisp: i.e., <code class="language-plaintext highlighter-rouge">(eq (car nil) nil)</code>.</li>
</ul>

<h2 id="cons-and-setf">CONS and SETF</h2>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/images/posts/2022-12-lisp-repl/more-complicated.png" alt="Figure 2" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Figure 2: <code class="language-plaintext highlighter-rouge">CONS</code>‘ing and <code class="language-plaintext highlighter-rouge">SETF</code>s.</td>
    </tr>
  </tbody>
</table>

<p>In LISP, we construct a <code class="language-plaintext highlighter-rouge">CONS</code> cell that contains two pointers (called <code class="language-plaintext highlighter-rouge">CAR</code> and <code class="language-plaintext highlighter-rouge">CDR</code>) with the <code class="language-plaintext highlighter-rouge">CONS</code> function. Continuing on to Figure 2, it seems like ChatGPT is aware of how <code class="language-plaintext highlighter-rouge">CONS</code> works. LISP also allows us to modify the value stored in a place with the the <code class="language-plaintext highlighter-rouge">SETF</code> macro. If the first argument to the <code class="language-plaintext highlighter-rouge">SETF</code> macro is a symbol (e.g., <code class="language-plaintext highlighter-rouge">my-list</code>), then <code class="language-plaintext highlighter-rouge">SETF</code> modifies the symbol table to associate the symbol-name with the value of the 2nd argument. ChatGPT seems aware of how <code class="language-plaintext highlighter-rouge">SETF</code> behaves. The first line of Figure 3 shows that ChatGPT can remember the state of the symbol table.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/images/posts/2022-12-lisp-repl/defun.png" alt="Figure 3" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Figure 3: Recursive Functions of Symbolic Expressions.</td>
    </tr>
  </tbody>
</table>

<h2 id="a-function-named-f">A Function Named <code class="language-plaintext highlighter-rouge">F</code></h2>
<p>Figure 3 shows the definition of a function named <code class="language-plaintext highlighter-rouge">f</code>. Here, <code class="language-plaintext highlighter-rouge">f</code> computes the factorial of a number. This might seem challenging, since <code class="language-plaintext highlighter-rouge">f</code> is a recursive function. But ChatGPT evaluates the function without any problems. Figure 4 shows <code class="language-plaintext highlighter-rouge">f</code> applied to a larger, challenging input. Once again, ChatGPT correctly evaluates the expression.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/images/posts/2022-12-lisp-repl/fig4.png" alt="Figure 4" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Figure 4: The persistence of memory.</td>
    </tr>
  </tbody>
</table>

<h2 id="the-persistence-of-memory">The Persistence of Memory</h2>
<p>Let’s see if ChatGPT still recalls the association between <code class="language-plaintext highlighter-rouge">my-list</code> and <code class="language-plaintext highlighter-rouge">(42)</code> we introduced in our symbol table. Figure 4 shows the results of evaluating <code class="language-plaintext highlighter-rouge">(setf (car my-list) 42)</code>. We see that:</p>
<ul>
  <li>ChatGPT understands <code class="language-plaintext highlighter-rouge">setf</code> works on arbitrary places, not just symbol names.</li>
  <li>ChatGPT remembers we associated <code class="language-plaintext highlighter-rouge">my-list</code> with a list containing a single element.</li>
</ul>

<h2 id="y-combinator">Y-Combinator</h2>

<p>Let’s try another challenge: The Y Combinator. I used <a href="https://gist.github.com/nitin-motiani/2379663">this implementation</a>. Figure 5 shows the results.</p>

<p>To my surprise, ChatGPT understands the function definition and correctly evaluates it. This is particularly challenging, since it shows:</p>
<ul>
  <li>ChatGPT knows about <code class="language-plaintext highlighter-rouge">FUNCALL</code> and understands what it means to be a LISP-2.</li>
  <li>ChatGPT follows the indirection in the function calls.</li>
</ul>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/images/posts/2022-12-lisp-repl/y-combinator.png" alt="Figure 5" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Figure 5: The Y Combinator.</td>
    </tr>
  </tbody>
</table>

<h2 id="closing-thoughts">Closing Thoughts</h2>
<p>I am very surprised how well ChatGPT handles the task of interpreting LISP code. I am very curious if ChatGPT actually <em>understands</em> the source code, or if it has seen enough examples that it can blindly regurgitate results it has memorized. Since LISP has very simple semantics, it’s a great tool for studying the extent of a large language model’s ability to understand and interpret source code.</p>

<p>At this past year’s ASE, there was a really interesting paper called <a href="https://arxiv.org/pdf/2206.11719.pdf"><em>AST-Probe: Recovering abstract syntax trees from hidden representations of pre-trained language models</em></a>. One conclusion of this paper is large language models deeply understand syntax trees. I wonder if we can somehow decide if large language models understand a language’s operational semantics?</p>]]></content><author><name>Max Taylor</name></author><category term="lisp" /><category term="gpt" /><category term="ai" /><summary type="html"><![CDATA[TL;DR: ChatGPT is a LISP REPL.]]></summary></entry><entry><title type="html">Programming Puzzle: Optimal Pothole Repair</title><link href="https://maxtaylor.dev/posts/2022/08/repairing-potholes" rel="alternate" type="text/html" title="Programming Puzzle: Optimal Pothole Repair" /><published>2022-08-15T00:00:00-04:00</published><updated>2022-08-15T00:00:00-04:00</updated><id>https://maxtaylor.dev/posts/2022/08/repairing-potholes</id><content type="html" xml:base="https://maxtaylor.dev/posts/2022/08/repairing-potholes"><![CDATA[<p>This post discusses how to efficiently schedule optimal pothole repair!</p>

<h2 id="contents">Contents</h2>
<ol>
  <li><a href="#introduction">Introduction</a></li>
  <li><a href="#solution">Solution Idea</a>
    <ul>
      <li><a href="#analysis">Asymptotic Analysis</a></li>
    </ul>
  </li>
  <li><a href="#implementation">Python Implementation</a></li>
  <li><a href="#follow-ups">Follow-ups</a></li>
</ol>

<h2 id="introduction-">Introduction <a name="introduction"></a></h2>
<p>I encountered a fun programming puzzle recently:</p>

<blockquote>
  <p>You are given a description of a two-lane road in which two strings, <em>L1</em> and <em>L2</em>, represent the first and the second lane. Each lane consists of <em>N</em> segments of equal length.</p>

  <p>The K-th segment of the first lane is represented by L1[K], and the K-th segment of the second lane is represented by L2[K], where “.” denotes a smooth segment of road, and “x” denotes a segment that contains potholes.</p>

  <p>Cars can drive over segments with potholes, but it is uncomfortable for passengers. Therefore, a project to repair as many potholes as possible was submitted. At most one contiguous region of each lane may be repaired at a time. The region is closed to traffic while it is under repair.</p>

  <p>How many road segments with potholes can be repaired given that the road must be kept open?</p>

  <p>For example, if L1 = “..xx.x.” and L2 = “x.x.x..”, the maximum number of potholes we can repair is 4. See Figure 1 for an explanation.</p>
</blockquote>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/images/posts/2022-08-repairing-potholes/pothole-example.png" alt="Figure 1" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Figure 1: Visualization of the example. Segments without potholes are shown as empty boxes. Segments with potholes are shown as gray boxes. Contiguous regions under repair are highlighted orange. The arrows indicate a path through the road.</td>
    </tr>
  </tbody>
</table>

<h2 id="solution-idea-">Solution Idea <a name="solution"></a></h2>
<p>This problem has two key requirements:</p>
<ol>
  <li>Repairs affect a contiguous region. That means that a solution like the one shown in Figure 2 is not allowed.</li>
  <li>Vehicles must be able to reach the end of the road.</li>
</ol>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/images/posts/2022-08-repairing-potholes/figure-2.png" alt="Figure 2" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Figure 2: L1 = “..xx…” and L2 = “x….x.”. The solution shown here is <strong>not allowed</strong>, since L2’s repair regions are not contiguous.</td>
    </tr>
  </tbody>
</table>

<p>There are two important observations about the problem. First, a vehicle must be able to travel the road by changing lanes at most once. I give an argument for this point in the next paragraph. Second, no repair can occur at the segment where the vehicle changes lanes. This is because both lanes must be open for the vehicle to change lanes.</p>

<p>A proof by contradiction shows the vehicle can change lanes at most once in an allowed solution. First, assume without loss in generality that a vehicle starts in L1, and changes lanes twice at segments <em>i</em> and <em>j</em>. A repair must occur in the region [0, <em>i</em>-1] in L2, otherwise the vehicle could have started in L2. A repair must start at segment <em>j</em> in L2, otherwise the vehicle need not change lanes. But the segments [0, <em>i</em>-1] and [<em>j</em>, …] are not contiguous. So, the solution is not allowed. We conclude that a vehicle can change lanes at most once.</p>

<p>Since the vehicle can only change lanes once, we only need to find  (1) the segment to change lanes, and (2) the starting lane. Let’s start by characterizing the segment where the vehicle changes lanes. Suppose the vehicle starts in L1. Call the ideal segment to change lanes <em>C</em>. The sum of potholes in L1 in region [C+1, …] and L2 in region […, C-1] is maximal. This is because, since the vehicle doesn’t start in L2, we can repair all segments in L2 until <em>C</em>. The same argument applies to L1 after <em>C</em>.</p>

<p>We can compute <em>C</em> in $O(n)$ time, where <em>n</em> is the number of segments. Maintain two arrays of length n, $avoided_{L1}$ and $avoided_{L2}$. Let $avoided_{L1}[i]$ denote the number of potholes avoided in L1[i+1, …] if the vehicle changes lanes from L1 to L2 at segment <em>i</em>. Similarly, $avoided_{L2}[i]$ denotes the number of potholes avoided in L2[0, i-1] if the vehicles changes lanes from L1 to L2 at segment <em>i</em>. So, $avoided_{L1}$ stores the partial sums of the number of potholes in L1 counting from the end. Meanwhile, $avoided_{L2}$ stores the partial sums of the number of potholes in L2 counting from the start. Computing <em>C</em> is simple: $C = \underset{0 \leq c &lt; n}{\text{argmax}}(avoided_{L1}[c] + avoided_{L2}[c]).$</p>

<p>Finding the starting lane $L$ is also easy. Let $F(A)$ denote the value of $C$ for a vehicle that starts in lane $A$. Then, $L = \underset{l \in \left\{ L1,~ L2 \right\} }{\text{argmax}}(F(l))$.</p>

<h3 id="asymptotic-analysis-">Asymptotic Analysis <a name="analysis"></a></h3>
<p>This solution has a runtime of $O(n)$, since computing $C$ takes $O(n)$ time. Memory usage is $O(n)$, since we create the extra arrays $avoided_{L1}$ and $avoided_{L2}$ to store partial sums.</p>

<h2 id="python-implementation-">Python Implementation <a name="implementation"></a></h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>


<span class="k">class</span> <span class="nc">PotholeState</span><span class="p">(</span><span class="n">enum</span><span class="p">.</span><span class="n">Enum</span><span class="p">):</span>
    <span class="n">POTHOLE</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="n">CLEAN</span> <span class="o">=</span> <span class="mi">2</span>


<span class="n">_STR_TO_STATE</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'.'</span><span class="p">:</span> <span class="n">PotholeState</span><span class="p">.</span><span class="n">CLEAN</span><span class="p">,</span>
    <span class="s">'x'</span><span class="p">:</span> <span class="n">PotholeState</span><span class="p">.</span><span class="n">POTHOLE</span><span class="p">,</span>
<span class="p">}</span>


<span class="k">def</span> <span class="nf">read_lanes</span><span class="p">(</span><span class="n">l1</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">l2</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">PotholeState</span><span class="p">]]:</span>
    <span class="k">return</span> <span class="p">[[</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">]</span> <span class="k">for</span> <span class="n">s1</span><span class="p">,</span> <span class="n">s2</span>
            <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span>
              <span class="p">[</span><span class="n">_STR_TO_STATE</span><span class="p">[</span><span class="nb">chr</span><span class="p">]</span> <span class="k">for</span> <span class="nb">chr</span> <span class="ow">in</span> <span class="n">l1</span><span class="p">],</span>
              <span class="p">[</span><span class="n">_STR_TO_STATE</span><span class="p">[</span><span class="nb">chr</span><span class="p">]</span> <span class="k">for</span> <span class="nb">chr</span> <span class="ow">in</span> <span class="n">l2</span><span class="p">],</span>
            <span class="p">)]</span>


<span class="k">def</span> <span class="nf">_max_repairable_helper</span><span class="p">(</span><span class="n">l1</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">PotholeState</span><span class="p">],</span> <span class="n">l2</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">PotholeState</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="n">l1_avoided_potholes</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">l1</span><span class="p">)</span>
    <span class="n">l2_avoided_potholes</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">l2</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">l1</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
        <span class="n">l1_avoided_potholes</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">l1_avoided_potholes</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">l1</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">PotholeState</span><span class="p">.</span><span class="n">POTHOLE</span><span class="p">:</span>
            <span class="n">l1_avoided_potholes</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">l2</span><span class="p">)):</span>
        <span class="n">l2_avoided_potholes</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">l2_avoided_potholes</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">l2</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">PotholeState</span><span class="p">.</span><span class="n">POTHOLE</span><span class="p">:</span>
            <span class="n">l2_avoided_potholes</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="nb">max</span><span class="p">([</span><span class="n">l1_avoided_potholes</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">l2_avoided_potholes</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">l1</span><span class="p">))])</span>


<span class="k">def</span> <span class="nf">max_repairable_segments</span><span class="p">(</span><span class="n">road</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">PotholeState</span><span class="p">]])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="n">lane1</span> <span class="o">=</span> <span class="p">[</span><span class="n">road</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">road</span><span class="p">))]</span>
    <span class="n">lane2</span> <span class="o">=</span> <span class="p">[</span><span class="n">road</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">road</span><span class="p">))]</span>
    <span class="k">return</span> <span class="nb">max</span><span class="p">(</span>
        <span class="n">_max_repairable_helper</span><span class="p">(</span><span class="n">lane1</span><span class="p">,</span> <span class="n">lane2</span><span class="p">),</span>
        <span class="n">_max_repairable_helper</span><span class="p">(</span><span class="n">lane2</span><span class="p">,</span> <span class="n">lane1</span><span class="p">),</span>
    <span class="p">)</span>
</code></pre></div></div>

<h2 id="follow-ups-">Follow-ups <a name="follow-ups"></a></h2>
<ul>
  <li>Remove the requirement that only one contiguous region per lane can be under repair.</li>
  <li>Find the regions under repair in both lanes in an optimal solution.</li>
  <li>There are two key properties in a solution. Check the implementation with property tests.</li>
</ul>]]></content><author><name>Max Taylor</name></author><category term="programming puzzles" /><category term="python" /><summary type="html"><![CDATA[This post discusses how to efficiently schedule optimal pothole repair!]]></summary></entry></feed>