Jekyll2019-10-17T06:32:18+00:00https://ct075.github.io/feed.xmlThe Uncommon TrajectoryI do some code things.Cameron Wongcam@camdar.ioWhirlwind Introduction to Subtyping2019-10-17T00:00:00+00:002019-10-17T00:00:00+00:00https://ct075.github.io/2019-10-17/subtyping<p>(These notes are adapted from a lecture I gave for a student-run class on type theory. See also the <a href="/assets/dist/subtyping.pdf">pdf version</a>)</p> <p>The purpose of this text is to provide a high-level overview of the motivations behind and basic theory of a subsumptive subtyping semantics over a simply-typed lambda calculus similar to ML. In the interest of brevity, we will elide a formal discussion of syntax forms, dynamics, etc, and simply discuss what properties this system should have if embedded into ML. As such, you should have some basic knowledge of ML and the formalities of ML-like typesystems.</p> <p>Disclaimer: The few “proofs” given <em>are not</em> completely rigorous. The biggest elephant in the room is that we have not formally defined the behavior of our system, and instead nebulously pretend that we’ve grafted things onto OCaml (or SML). The sections marked as “proof” are intended as sketches that may be superimposed onto a formal system with similar rules. <em>Please</em> contact me if you spot an error; a major reason I set out to write this was to confirm to myself that <em>I</em> properly understand these concepts.</p> <h1 id="motivation">Motivation</h1> <h2 id="typesafe-lists">Typesafe Lists</h2> <p>Consider the function <code class="highlighter-rouge">hd : 'a list -&gt; 'a</code> that returns the first element of a list. Typically, this function is only defined on non-empty lists, raising some form of error when given an empty input. One way around this is to instead use a type such as <code class="highlighter-rouge">hd_opt : 'a list -&gt; 'a option</code>, which forces the programmer to handle the empty case explicitly. However, this is no better than pattern matching on the list originally, and indeed only serves to delay the boilerplate in cases in which the list is provably non-empty. For example, consider the following (contrived) example:</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">v</span> <span class="o">:</span> <span class="kt">int</span> <span class="kt">list</span> <span class="o">=</span> <span class="n">map</span> <span class="n">foo</span> <span class="p">[</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="p">]</span> <span class="k">in</span> <span class="k">match</span> <span class="n">hd_option</span> <span class="n">v</span> <span class="k">with</span> <span class="o">|</span> <span class="nc">Some</span> <span class="n">x</span> <span class="o">-&gt;</span> <span class="n">do_something</span> <span class="n">x</span> <span class="o">|</span> <span class="nc">None</span> <span class="o">-&gt;</span> <span class="k">assert</span> <span class="bp">false</span> </code></pre></div></div> <p>It is a feature of ML-inspired languages that, in cases like this, we generally have no choice <em>but</em> to pattern match and think about what to do when <code class="highlighter-rouge">hd_option v</code> is <code class="highlighter-rouge">None</code>. In this case, however, we <em>know</em> that the value <code class="highlighter-rouge">v</code> is non-empty – <code class="highlighter-rouge">map f</code> is a function returning a list of the same length as its input, so using this function on <code class="highlighter-rouge">[1,2,3,4]</code> gives a list of length exactly 4. However, this fact isn’t actually recorded in the type of <code class="highlighter-rouge">map</code> anywhere – it is <em>opaque</em> to the typesystem. Because of this, the typesystem cannot prove that <code class="highlighter-rouge">v</code> is non-empty, and so the “safe” thing to do is pattern match and explicitly mark the extraneous case as unreachable. As you might expect, this boilerplate becomes annoying very quickly, and so nearly every standard library for these languages will provide unsafe function wrappers for this pattern (<code class="highlighter-rouge">Option.valOf</code> in OCaml, for example). But in using these functions, we remove the safety net provided by the compiler and typesystem, and invite runtime exceptions that such constructs are designed to avoid (see: option types vs pervasive use of <code class="highlighter-rouge">null</code> in other languages).</p> <p>Instead, it would be ideal to <em>restrict</em> the type of <code class="highlighter-rouge">hd</code> to only act on lists that are inhabited, so the compiler will throw an error if we attempt to blindly take the first element of a potentially empty list. By <em>lifting</em> the information about whether a list is empty into the typesystem, we can then allow the typechecker to construct proofs that a given invocation of <code class="highlighter-rouge">hd</code> is definitely safe (if we adjust other functions to match, of course).</p> <h2 id="attempt-1-generalized-algebraic-datatypes">Attempt 1: Generalized Algebraic Datatypes</h2> <p>A somewhat commonly-seen solution’’ to this issue that you might see uses <a href="https://en.wikibooks.org/wiki/Haskell/GADT">GADTs</a> to attach type information to the particular constructor. For example:</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">empty</span> <span class="k">type</span> <span class="n">nonempty</span> <span class="k">type</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="n">_</span><span class="p">)</span> <span class="kt">list</span> <span class="o">=</span> <span class="o">|</span> <span class="nc">Nil</span> <span class="o">:</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="n">empty</span><span class="p">)</span> <span class="kt">list</span> <span class="o">|</span> <span class="p">(</span><span class="o">::</span><span class="p">)</span> <span class="o">:</span> <span class="k">'</span><span class="n">a</span> <span class="o">*</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="k">'</span><span class="n">b</span><span class="p">)</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="n">nonempty</span><span class="p">)</span> <span class="kt">list</span> </code></pre></div></div> <p>Then, we can note that <code class="highlighter-rouge">map</code> does not change the emptiness of its argument by type-annotating it like so:</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">val</span> <span class="n">map</span> <span class="o">:</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">b</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="k">'</span><span class="n">e</span><span class="p">)</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="k">'</span><span class="n">b</span><span class="o">,</span> <span class="k">'</span><span class="n">e</span><span class="p">)</span> <span class="kt">list</span> </code></pre></div></div> <p>where, because the output type <code class="highlighter-rouge">'e</code> must be the same as the input type <code class="highlighter-rouge">'e</code>, we know that if the input list is <code class="highlighter-rouge">nonempty</code>, the output must also be <code class="highlighter-rouge">nonempty</code>.</p> <p>This works great if we know that the output emptiness is fixed, or is the same as the input list. However, we begin to run into issues where the relationship between the input and output emptiness is not constant or identity. For example, how would we encode the type of <code class="highlighter-rouge">append</code>?</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">val</span> <span class="n">append</span><span class="o">:</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="k">'</span><span class="n">e1</span><span class="p">)</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="k">'</span><span class="n">e2</span><span class="p">)</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="o">???</span><span class="p">)</span> <span class="kt">list</span> </code></pre></div></div> <p>In fact, we <em>do</em> know something about the output type in this case – the output of <code class="highlighter-rouge">append</code> is <code class="highlighter-rouge">empty</code> iff both inputs are. However, ML-style typesystems lack support for <em>type-level lambdas</em>, and so we have no way to express this type without employing more trickery.</p> <p>Even worse, what about functions where the output has <em>no</em> relation to the input?</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">val</span> <span class="n">filter</span><span class="o">:</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="k">'</span><span class="n">e</span><span class="p">)</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="o">???</span><span class="p">)</span> <span class="kt">list</span> </code></pre></div></div> <p>Here, we’re <em>completely</em> up the creek – there’s <em>no</em> way for us to know whether the filtered list is empty.</p> <p>One way around this is to use <a href="https://wiki.haskell.org/Rank-N_types">higher-ranked polymorphism</a> and continuation passing style to capture’’ the output. By making the type of <code class="highlighter-rouge">filter</code> look like</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">val</span> <span class="n">filter</span><span class="o">:</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="k">'</span><span class="n">e</span><span class="p">)</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="n">forall</span> <span class="n">b</span> <span class="o">.</span> <span class="p">(</span><span class="k">'</span><span class="n">a</span><span class="o">,</span> <span class="n">b</span><span class="p">)</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">c</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="k">'</span><span class="n">c</span> </code></pre></div></div> <p>we can force consumers of this function to provide a handler that is able to handle both cases. The extra function argument at the end is the “continuation” that consumes the list output by filter.</p> <p>This is stylistically jarring, however, and ML-style languages generally don’t support or require jumping through some hoops to encode rank N types.</p> <h2 id="attempt-2-sum-types">Attempt 2: Sum Types</h2> <p>The fundamental problem with <code class="highlighter-rouge">append</code> and <code class="highlighter-rouge">filter</code> in the previous example is that, in different cases, these functions must return <em>different types</em>. This is generally forbidden<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup>, and so we’re stuck.</p> <p>On the other hand, we <em>have</em> a mechanism by which we can join two disparate types – sum types! A generic list is <em>either</em> an empty list, or an inhabited list. So we come up with</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="k">'</span><span class="n">a</span> <span class="n">emptylist</span> <span class="o">=</span> <span class="n">nil</span> <span class="ow">and</span> <span class="k">'</span><span class="n">a</span> <span class="n">inhablist</span> <span class="o">=</span> <span class="p">(</span><span class="o">::</span><span class="p">)</span> <span class="k">of</span> <span class="k">'</span><span class="n">a</span> <span class="o">*</span> <span class="k">'</span><span class="n">a</span> <span class="kt">list</span> <span class="ow">and</span> <span class="k">'</span><span class="n">a</span> <span class="kt">list</span> <span class="o">=</span> <span class="nc">E</span> <span class="k">of</span> <span class="k">'</span><span class="n">a</span> <span class="n">emptylist</span> <span class="o">|</span> <span class="nc">N</span> <span class="k">of</span> <span class="k">'</span><span class="n">a</span> <span class="n">inhablist</span> </code></pre></div></div> <p>and so we can express functions like <code class="highlighter-rouge">filter</code> by returning an <code class="highlighter-rouge">'a list</code>, whereas functions like <code class="highlighter-rouge">hd</code> must take in <code class="highlighter-rouge">'a inhablist</code> as input.</p> <p>This is a lot of code for a relatively simple idea, though! In addition to the mutually recursive types, here’s an example of <em>using</em> this type:</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="k">rec</span> <span class="n">map</span> <span class="n">f</span> <span class="nc">L</span> <span class="o">=</span> <span class="k">match</span> <span class="nc">L</span> <span class="k">with</span> <span class="o">|</span> <span class="nc">E</span> <span class="n">nil</span> <span class="o">-&gt;</span> <span class="nc">E</span> <span class="n">nil</span> <span class="o">|</span> <span class="nc">N</span> <span class="p">(</span><span class="n">x</span> <span class="o">::</span> <span class="n">xs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nc">N</span> <span class="p">(</span><span class="n">f</span> <span class="n">x</span> <span class="o">::</span> <span class="n">map</span> <span class="n">f</span> <span class="n">xs</span><span class="p">)</span> </code></pre></div></div> <p>Notice the extra uses of <code class="highlighter-rouge">E</code> and <code class="highlighter-rouge">N</code>. This adds clutter without really adding meaning, and gets increasingly cumbersome as we write more complex functions. In addition, you may have noticed that this <em>doesn’t even solve the original problem!</em> When we call <code class="highlighter-rouge">map f [1,2,3,4]</code>, we get back a value of type <code class="highlighter-rouge">list</code>, which <em>doesn’t tell us that the list is non-empty!</em></p> <p>Really, what we <em>want</em> is a way to express that a non-empty list is the same as a regular list, but with some <em>extra information</em> (particularly, that the list is non-empty).</p> <h1 id="the-subtyping-relation">The Subtyping Relation</h1> <p>Let us introduce a new judgment into our typesystem. Let <script type="math/tex">% <![CDATA[ \tau_1 <: \tau_2 %]]></script> (pronounced “extends”, as in “<script type="math/tex">\tau_1</script> extends <script type="math/tex">\tau_2</script>”) be a judgment claiming pretty much exactly what we said above, that a value of type <script type="math/tex">\tau_1</script> is equivalent to a value of type <script type="math/tex">\tau_2</script>, with some more specific information attached. This leads to the following typing rule:</p> <script type="math/tex; mode=display">% <![CDATA[ \frac{\Gamma \vdash e : \tau_1\ \ \ \tau_1 <: \tau_2}{\Gamma \vdash e : \tau_2} %]]></script> <p>This is known as the <em>subsumption</em> rule, where we allow terms to automatically “upcast” from a subtype to a supertype. Using our intuition about what <code class="highlighter-rouge">&lt;:</code> means, this should make sense – if a <script type="math/tex">\tau_1</script> is a <script type="math/tex">\tau_2</script> with some extra information, then we should be able to recover the <script type="math/tex">\tau_2</script> by discarding that extra specificity.</p> <p>Another important property to maintain with this relation is its transitivity. In fact, if we are only concerned with typing specific expressions, then this property is irrelevant, as we can already show that <script type="math/tex">e:\tau_1</script>, <script type="math/tex">% <![CDATA[ \tau_1 <: \tau_2 %]]></script> and <script type="math/tex">% <![CDATA[ \tau_2 <: \tau_3 %]]></script> implies <script type="math/tex">e:\tau_3</script>.</p> <p><strong>Exercise:</strong> Give a derivation showing this.</p> <p>When discussing the types <em>themselves</em>, however, it is often convenient to add a rule stating this directly:</p> <script type="math/tex; mode=display">% <![CDATA[ \frac{\tau_1 <: \tau_2\ \ \ \tau_2 <: \tau_3}{\tau_1 <: \tau_3} %]]></script> <p>To solve our list problem, then, we want <code class="highlighter-rouge">'a nlist</code> to be a <em>subtype</em> of <code class="highlighter-rouge">'a list</code>, and re-structure all our functions accordingly. Before we address this directly, however, we need to explore how, exactly, this relation should behave.</p> <h1 id="structural-subtyping">Structural Subtyping</h1> <p>In any particular subtyped calculus, we might consider baking in forms of <em>semantic</em> subtyping, where the relationship between two given types is based on the fundamental properties of our primitive types. For example, convention claims that <script type="math/tex">\mathbb{N}</script>, the set of natural numbers, is a subset of <script type="math/tex">\mathbb{R}</script>, the set of reals. Then, assuming we have analogous types <code class="highlighter-rouge">nat</code> and <code class="highlighter-rouge">float</code> in our language (using floating point numbers to approximate the reals), we might decide that <code class="highlighter-rouge">nat &lt;: float</code><sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup>.</p> <p>However, in the absence of such specificity about our basic types, let us instead examine what properties might be derived <em>structurally</em>, examining only the form of the types themselves.</p> <h2 id="products">Products</h2> <p>Consider the following two types:</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">t1</span> <span class="o">=</span> <span class="p">{</span> <span class="n">l1</span> <span class="o">:</span> <span class="n">a</span> <span class="k">type</span> <span class="n">t2</span> <span class="o">=</span> <span class="p">{</span> <span class="n">l1</span> <span class="o">:</span> <span class="n">a</span> <span class="p">;</span> <span class="n">l2</span> <span class="o">:</span> <span class="n">b</span> <span class="p">;</span> <span class="n">l2</span> <span class="o">:</span> <span class="n">b</span> <span class="p">}</span> <span class="p">;</span> <span class="n">l3</span> <span class="o">:</span> <span class="n">c</span> <span class="p">}</span> </code></pre></div></div> <p>(note that we’re using <em>labeled</em> products, otherwise known as “records” instead of standard tuples, because it makes the semantics much clearer)</p> <p>In keeping with our intuition that “the subtype is the same as the supertype but with more information”, we notice that any value of type <code class="highlighter-rouge">t2</code> has all the fields of <code class="highlighter-rouge">t1</code> with the same types, but also has some data <code class="highlighter-rouge">l3</code>. We thus add a “width-subtyping” rule for records:</p> <script type="math/tex; mode=display">% <![CDATA[ \frac{}{\{ \ell_1 : \tau_1, \dots, \ell_n : \tau_n, \dots \} <: \{ \ell_1 : \tau_1, \dots, \ell_n : \tau_n\}} %]]></script> <p>saying that a record <script type="math/tex">\rho_1</script> is a subtype of another record <script type="math/tex">\rho_2</script> when <script type="math/tex">\rho_1</script> contains all the same fields+types as <script type="math/tex">\rho_2</script>.</p> <h2 id="sums">Sums</h2> <p>Similarly, suppose we have the following two labeled variants:</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">t1</span> <span class="o">=</span> <span class="nc">L1</span> <span class="k">of</span> <span class="n">a</span> <span class="k">type</span> <span class="n">t2</span> <span class="o">=</span> <span class="nc">L1</span> <span class="k">of</span> <span class="n">a</span> <span class="o">|</span> <span class="nc">L2</span> <span class="k">of</span> <span class="n">b</span> <span class="o">|</span> <span class="nc">L2</span> <span class="k">of</span> <span class="n">b</span> <span class="o">|</span> <span class="nc">L3</span> <span class="k">of</span> <span class="n">c</span> </code></pre></div></div> <p>Which direction should the subtyping go? We might claim that a value of type <code class="highlighter-rouge">t2</code> contains “extra” information compared to <code class="highlighter-rouge">t1</code> by noting that <code class="highlighter-rouge">t2</code> has an extra variant, so a <code class="highlighter-rouge">t2</code> is a <code class="highlighter-rouge">t1</code> but <em>may</em> be different. However, this is wrong, as demonstrated by the following snippet:</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">match</span> <span class="n">v</span> <span class="k">with</span> <span class="o">|</span> <span class="nc">L1</span> <span class="n">x</span> <span class="o">-&gt;</span> <span class="n">do_something</span> <span class="n">x</span> <span class="o">|</span> <span class="nc">L2</span> <span class="n">y</span> <span class="o">-&gt;</span> <span class="n">do_something_else</span> <span class="n">y</span> </code></pre></div></div> <p>If <code class="highlighter-rouge">t2 &lt;: t1</code> truly, then it would be valid for <code class="highlighter-rouge">v</code> to be of the form <code class="highlighter-rouge">L3 z</code>, as this is certainly of type <code class="highlighter-rouge">t2</code> and therefore type <code class="highlighter-rouge">t1</code>. But this would be disastrous, as the match statement does not specify what to do in the <code class="highlighter-rouge">L3</code> case!</p> <p>We conclude by noting that the <em>other</em> direction is fine –</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">match</span> <span class="n">v</span> <span class="k">with</span> <span class="o">|</span> <span class="nc">L1</span> <span class="n">x</span> <span class="o">-&gt;</span> <span class="n">do_something_1</span> <span class="n">x</span> <span class="o">|</span> <span class="nc">L2</span> <span class="n">y</span> <span class="o">-&gt;</span> <span class="n">do_something_2</span> <span class="n">y</span> <span class="o">|</span> <span class="nc">L3</span> <span class="n">z</span> <span class="o">-&gt;</span> <span class="n">do_something_3</span> <span class="n">z</span> </code></pre></div></div> <p>If <code class="highlighter-rouge">v : t1</code>, then we know that it is restricted to be an <code class="highlighter-rouge">L1</code> or <code class="highlighter-rouge">L2</code> variant, so this match statement can never get stuck. The “extra information” gained from having a <code class="highlighter-rouge">t1</code> over a <code class="highlighter-rouge">t2</code>, then, can be viewed as the information that a <code class="highlighter-rouge">t1</code> is <em>not</em> an <code class="highlighter-rouge">L3</code> variant! So <code class="highlighter-rouge">t1 &lt;: t2</code>.</p> <p>Another way to view this is that, in many ways, sum types are “flipped around” compared to product types. Categorically speaking, we claim that sums and products are <em>dual</em> to each other (in fact, you’ll often find sums referred to as “coproducts”, to make this relationship explicit). It makes sense, then, that a “bigger” sum is related to a “smaller” sum in the <em>opposite</em> of the way that bigger and smaller <em>products</em> relate.</p> <h1 id="variance">Variance</h1> <p>The next topic to consider relates to our type constructors. The exact question we will be addressing is</p> <blockquote> <p>When is <script type="math/tex">% <![CDATA[ \tau_1\ t <: \tau_2\ t %]]></script>, for a particular type constructor <script type="math/tex">t</script>?</p> </blockquote> <p>To answer this question, we typically must examine not only the internals of the constructor <script type="math/tex">t</script>, but also the relationship between <script type="math/tex">\tau_1</script> and <script type="math/tex">\tau_2</script>. The way that the relationship between <script type="math/tex">\tau_1</script> and <script type="math/tex">\tau_2</script> affects the relationship between <script type="math/tex">\tau_1\ t</script> and <script type="math/tex">\tau_2\ t</script> is known as <em>variance</em>.</p> <h2 id="lists">Lists</h2> <p>As we began this discussion by talking about lists, it seems only right that we consider first the variance properties of the (idealized) type <code class="highlighter-rouge">'a list</code>.</p> <p>The naive answer might be that <script type="math/tex">% <![CDATA[ \tau_1\ list <: \tau_2\ list %]]></script> when <script type="math/tex">% <![CDATA[ \tau_1 <: \tau_2 %]]></script>. This is actually the correct thing to do – if <script type="math/tex">% <![CDATA[ \tau_1 <: \tau_2 %]]></script>, then we can view all elements of a <script type="math/tex">\tau_1\ list</script> as a <script type="math/tex">\tau_2</script>, thus giving us a <script type="math/tex">\tau_2 \ list</script>, so we might gain the rule</p> <script type="math/tex; mode=display">% <![CDATA[ \frac{\tau_1 <: \tau_2}{\tau_1\ list <: \tau_2\ list} %]]></script> <p>In fact, we can generalize this to “containers” in general.</p> <p><strong>Theorem (Covariance of sources)</strong> <em>If there exists a function of most general type <script type="math/tex">\forall a.(a\ t \rightarrow a)</script> that returns a value for at least one input, then <script type="math/tex">% <![CDATA[ \tau_1\ t <: \tau_2\ t %]]></script> implies <script type="math/tex">% <![CDATA[ \tau_1 <: \tau_2 %]]></script></em>.</p> <p><em>Proof</em>.</p> <p>Let <script type="math/tex">f, v, t, \tau_1, \tau_2</script> be such that <script type="math/tex">f: \forall a.(a\ t \rightarrow a)</script>, <script type="math/tex">v: \tau_1\ t</script> and <script type="math/tex">% <![CDATA[ \tau_1\ t <: \tau_2\ t %]]></script>.</p> <p>By subsumption, <script type="math/tex">v: \tau_2\ t</script>, so <script type="math/tex">f\ v : \tau_2</script>.</p> <p>Alternatively, <script type="math/tex">f\ v : \tau_1</script>.</p> <p>Because <script type="math/tex">f</script> is universally quantified, it cannot rely on any properties of <script type="math/tex">\tau_1</script> or <script type="math/tex">\tau_2</script>. But then, <script type="math/tex">f\ v</script> is arbitrary. In particular, any object of type <script type="math/tex">\tau_1</script> can be written as <script type="math/tex">f\ v</script> for an appropriate <script type="math/tex">v</script>.</p> <p>But <script type="math/tex">f\ v</script> is the same object regardless of whether <script type="math/tex">v</script> is viewed as a <script type="math/tex">\tau_1\ t</script> or a <script type="math/tex">\tau_2\ t</script>, so we must be able to view arbitrary objects of type <script type="math/tex">\tau_1</script> as objects of type <script type="math/tex">\tau_2</script>.</p> <script type="math/tex; mode=display">\square</script> <h2 id="functions">Functions</h2> <p>Functions are a strange case, because a function arrow really has <em>two</em> type variables, the input and the output. We’ll examine these one at a time.</p> <p>Consider types <script type="math/tex">\tau \rightarrow \tau_1</script> and <script type="math/tex">\tau \rightarrow \tau_2</script>, where <script type="math/tex">\tau</script> is fixed. If <script type="math/tex">\tau</script> is a type inhabited with an object <script type="math/tex">\sigma</script>, then certainly <script type="math/tex">\lambda f : f \sigma</script> is a function of type <script type="math/tex">\forall a . ((\tau \rightarrow a) \rightarrow a)</script>, so by the above we know that function arrows should be covariant in their second argument. In fact, by a slightly modified argument, we can show this even when <script type="math/tex">\tau</script> is the empty type <script type="math/tex">\bot</script> (hint: suppose that there existed some function of type <script type="math/tex">(\bot \rightarrow \tau_1) \rightarrow \rho</script> and consider what behavior this function could possibly have).</p> <p>The first argument, on the other hand, actually <em>flips</em> this behavior. If we have a function of type <script type="math/tex">\tau_1 \rightarrow \tau</script>, we’re eventually going to want to <em>call</em> it on a value of type <script type="math/tex">\tau_1</script> (formally we might say that we are allowed to <em>eliminate</em> this function by passing it an input). For this to be safe, we must know that any subtype of the type <script type="math/tex">\tau_1 \rightarrow \tau</script> must be able to safely handle a <script type="math/tex">\tau_1</script>. But this means that subtypes of <script type="math/tex">\tau_1 \rightarrow \tau</script> must have <em>supertypes</em> of <script type="math/tex">\tau_1</script> on the left (as otherwise they might perform an operation that is only implemented by the subtype). This gives us the rule</p> <script type="math/tex; mode=display">% <![CDATA[ \frac{\tau_2 <: \tau_1}{\tau_1 \rightarrow \tau <: \tau_2 \rightarrow \tau} %]]></script> <p>This phenomenon, in which we <em>reverse</em> the relationship between <script type="math/tex">\tau_1</script> and <script type="math/tex">\tau_2</script> to <script type="math/tex">\tau_1\ t</script> and <script type="math/tex">\tau_2\ t</script> is known as <em>contravariance</em>.</p> <p>By duality, we might expect to be able to produce a similar theorem to the one given above, perhaps by the existence of a function of type <script type="math/tex">\forall a. (a \rightarrow a\ t)</script>. Unfortunately, this doesn’t quite work – the function <script type="math/tex">\lambda x.[x]</script> has type <script type="math/tex">\forall a.(a \rightarrow a\ list)</script>, but lists are definitely not contravariant.</p> <p><strong>Proposition (Contravariance of sinks)</strong> <em>If there exists a type <script type="math/tex">c</script> such that there exists a function of type <script type="math/tex">f</script> such that <script type="math/tex">f : \forall a . (a \rightarrow a\ t \rightarrow c)</script>, then <script type="math/tex">% <![CDATA[ \tau_1\ t <: \tau_2\ t %]]></script> implies <script type="math/tex">% <![CDATA[ \tau_2 <: \tau_1 %]]></script></em>.</p> <p>This is marked as a proposition, rather than a theorem, because it is false as stated. For example, the function <script type="math/tex">\lambda \__1.\lambda \__2.()</script> has type <script type="math/tex">\forall a . \forall b . (a \rightarrow b \rightarrow unit)</script>, which of course specializes to <script type="math/tex">\forall a . (a \rightarrow a\ t \rightarrow c)</script> for <em>any</em> <script type="math/tex">t</script>. It may be enough to assert that <script type="math/tex">f</script> “meaningfully uses” its arguments, but this is difficult to formalize. That said, I’m not actually sure that <em>this</em> is true either – if you can prove it (even semi-informally in our ML-like world), <em>please</em> let me know!</p> <h2 id="mutable-references">Mutable References</h2> <p>It turns out that mutable state (important to imperative programming) interacts in an interesting way with subtyping variance. Consider the types <script type="math/tex">\tau_1\ ref</script> and <script type="math/tex">\tau_2\ ref</script>, referring to the types of <em>mutable reference cells</em>.</p> <p>Notice that the dereference function <code class="highlighter-rouge">!</code> has type <script type="math/tex">\forall a . (a\ ref \rightarrow a)</script>, so by covariance of sources (I promise I will explain the terms “source” and “sink” shortly), we have that <script type="math/tex">% <![CDATA[ \tau_1\ ref <: \tau_2 \ ref %]]></script> implies <script type="math/tex">% <![CDATA[ \tau_1 <: \tau_2 %]]></script>.</p> <p>However, <script type="math/tex">% <![CDATA[ \tau_1 <: \tau_2 %]]></script> is <em>not</em> sufficient to conclude that <script type="math/tex">% <![CDATA[ \tau_1\ ref <: \tau_2\ ref %]]></script>. Let <code class="highlighter-rouge">A &lt;: B</code> be types, and consider the following code:</p> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">magic</span> <span class="p">(</span><span class="n">r</span><span class="o">:</span> <span class="nc">A</span> <span class="n">ref</span><span class="p">)</span> <span class="p">(</span><span class="n">v</span><span class="o">:</span> <span class="nc">B</span><span class="p">)</span> <span class="o">:</span> <span class="nc">A</span> <span class="o">=</span> <span class="p">(</span><span class="n">r</span> <span class="o">:</span> <span class="nc">B</span> <span class="n">ref</span><span class="p">)</span> <span class="o">&lt;-</span> <span class="n">v</span><span class="p">;</span> <span class="o">!</span><span class="n">r</span> </code></pre></div></div> <p>Remember that the rule of subsumption means that annotating <code class="highlighter-rouge">r</code> as type <code class="highlighter-rouge">B ref</code> is a no-op (just a hint to the compiler). But <code class="highlighter-rouge">!r</code> (= <code class="highlighter-rouge">v</code>) is definitely of type <code class="highlighter-rouge">A</code>, as <code class="highlighter-rouge">r : A ref</code>, even though <code class="highlighter-rouge">v</code> is already of type <code class="highlighter-rouge">B</code>! The only way this is safe is if <code class="highlighter-rouge">B &lt;: A</code>.</p> <p>In fact, we need <em>both directions</em> to assert that <script type="math/tex">% <![CDATA[ \tau_1\ ref <: \tau_2\ ref %]]></script>. This can only happen if <script type="math/tex">\tau_1</script> and <script type="math/tex">\tau_2</script> are the same type up to some definition of “same” (reordering of labels, etc)<sup id="fnref:3"><a href="#fn:3" class="footnote">3</a></sup>. This property is known as <em>invariance</em>.</p> <p>One way to think of covariance and contravariance is to discuss what you can <em>do</em> with an <code class="highlighter-rouge">'a t</code>. If having an <code class="highlighter-rouge">'a t</code> possibly <em>gives</em> you an <code class="highlighter-rouge">'a</code>, it is called a <em>source</em>, and is therefore covariant by the proof above. Otherwise, if an <code class="highlighter-rouge">'a t</code> <em>consumes</em> an <code class="highlighter-rouge">'a</code>, then it is called a <em>sink</em>, which is contravariant using the same reasoning as with function arguments. However, references are <em>both</em> a source (by dereferencing) <em>and</em> a sink (by writing a value to the cell). We don’t run into this issue with <em>immutable</em> containers because growing a container necessarily changes the type of the (resulting) container, but writing a value to a ref cell <em>doesn’t</em> retroactively change its type.</p> <h2 id="bivariance">Bivariance</h2> <p>Thus far, we’ve seen examples of both covariant and contravariant type constructors, along with a type constructor that is both (invariant). You may then ask whether there exists a type of variance that is <em>neither</em> – that is, if there exists some <script type="math/tex">t</script> such that <script type="math/tex">% <![CDATA[ \tau_1\ t <: \tau_2\ t %]]></script> regardless of the relation between <script type="math/tex">\tau_1</script> and <script type="math/tex">\tau_2</script>.</p> <p>In fact, there is! This is the case when an object of type <script type="math/tex">\tau\ t</script> is entirely unrelated to objects of type <script type="math/tex">\tau</script>. This is most often seen with phantom type variables intended to aid typechecking. Such types are known as <em>bivariant</em> in the given input.</p> <p>We can show that this is the <em>only</em> possibility for a type to be bivariant by appealing to the source theorem and sink proposition above. Regarding the latter case, if <script type="math/tex">% <![CDATA[ \tau_1\ t <: \tau_2\ t %]]></script> always, then we can sink <em>any value</em> into a value of type <script type="math/tex">\tau\ t</script> (for any <script type="math/tex">\tau</script>!). This can only possibly be safe if sinking a value is a no-op.</p> <p><strong>Exercise:</strong> Provide a similar argument to the above without the source theorem to show that a bivariant type constructor cannot be a source. (Hint: let <script type="math/tex">\tau = \bot</script>)</p> <h1 id="bounded-quantification">Bounded Quantification</h1> <p>You may notice that, after all that, we <em>still</em> haven’t solved the problem we initially set out to! Even if we construct a type <code class="highlighter-rouge">'a nlist</code> such that <code class="highlighter-rouge">'a nlist &lt;: 'a list</code>, we <em>still</em> can’t tell the typechecker that <code class="highlighter-rouge">map</code> only returns an <code class="highlighter-rouge">nlist</code> if given an <code class="highlighter-rouge">nlist</code>!</p> <p>The solution is to adjust how we approach polymorphism. Previously, ML-style languages supported only <em>unbounded quantification</em> in their types. Polymorphism was all-or-nothing; either you had a specific type or you must be generic over <em>all</em> possible types. This is denoted via the implicit <script type="math/tex">\forall</script> seen in polymorphic ML types. However, now we have interesting things to say about types and how they relate to each other, which in turn enrichens our type language.</p> <p>In particular, we can now have types of the form <script type="math/tex">% <![CDATA[ \forall\{t : t <: s\}.\tau %]]></script> and <script type="math/tex">% <![CDATA[ \forall\{t : s <: t\}.\tau %]]></script>, where we can <em>upper- or lower-bound</em> the range of types we are quantifying over.</p> <p>This allows us to solve our list problem relatively elegantly via a combination of subtyping and GADTs by declaring types <script type="math/tex">unknown</script>, <script type="math/tex">% <![CDATA[ nonempty <: unknown %]]></script> and <script type="math/tex">% <![CDATA[ empty <: unknown %]]></script>. Then <code class="highlighter-rouge">map</code> can have type <script type="math/tex">% <![CDATA[ \forall\{e : e <: unknown\} . \forall a . ((a \rightarrow b) \rightarrow (a,e) list \rightarrow (b,e) list) %]]></script>.</p> <p><strong>Exercise:</strong> Show that you can express the type of <code class="highlighter-rouge">append</code> under this scheme. You may assume any reasonable extension of the syntax to, say, provide an upper and lower bound at the same time, or to allow multiple bounds to apply at once.</p> <p>Many modern-day subtyping schemes allow quantification in this way. For example, in Java, there exists a type <code class="highlighter-rouge">List&lt;? extends T&gt;</code> for <script type="math/tex">% <![CDATA[ \forall\{t:t <: T\}.t\ list %]]></script> and <code class="highlighter-rouge">List&lt;? super T&gt;</code> for <script type="math/tex">% <![CDATA[ \forall\{t:T <: t\}.t\ list %]]></script>. However, adding expressiveness to a type-level language often leads to trouble. In fact, typechecking a system with bounded quantification in both directions is <em>undecidable</em>.</p> <h1 id="references">References</h1> <p> Pierce, Benjamin C. Types and Programming Languages. The MIT Press, 2002.</p> <p> Reynolds, John C. “Design of the Programming Language Forsythe.” Algol-like Languages, 28 June 1996.</p> <p> Grigore, Radu. “Java Generics Are Turing Complete.” ACM SIGPLAN Notices, vol. 52, no. 1, 1 Jan. 2017, pp. 73–85.</p> <div class="footnotes"> <ol> <li id="fn:1"> <p>There are exceptions stemming from type shenanigans (oftentimes via GADTs!), but this isn’t one of them. <a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:2"> <p>This may seem alarming – <code class="highlighter-rouge">nat</code> is certainly not a <em>subset</em> of <code class="highlighter-rouge">float</code> on any real computer architecture! We will gloss over this issue with regards to subsumption for now, as it is largely inconsequential without discussion of syntax forms and dynamic semantics. Attempting to address this leads to “coercive” subtyping, where casts must be made explicit. <a href="#fnref:2" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:3"> <p>We should be careful that we don’t claim that <script type="math/tex">% <![CDATA[ \tau_1 <: \tau_2 %]]></script> and <script type="math/tex">% <![CDATA[ \tau_2 <: \tau_1 %]]></script> implies <script type="math/tex">\tau_1 \cong \tau_2</script> (isomorphism) unduly, as isomorphism often works strangely with subsumptive subtyping. For example, <script type="math/tex">(a,(b,c)) \cong ((a,b),c)</script>, but in these cases the term <script type="math/tex">v.0</script> is ambiguous. The difference is technical and difficult to reason about without formally defining semantics. <a href="#fnref:3" class="reversefootnote">&#8617;</a></p> </li> </ol> </div>Cameron Wongcam@camdar.io(These notes are adapted from a lecture I gave for a student-run class on type theory. See also the pdf version)The Great Theorem Prover Challenge II2018-07-04T00:00:00+00:002018-07-04T00:00:00+00:00https://ct075.github.io/2018-07-04/theorem-prover-2<p><em>This is the second of a series on Hillel Wayne’s <a href="https://www.hillelwayne.com/post/theorem-prover-showdown/">Great Theorem Prover Showdown</a>. Make sure to check out the <a href="https://blog.camdar.io/2018-06-21/theorem-prover-1/">first part</a>! You can also follow my progress in <a href="https://github.com/CT075/theorem-prover-showdown">this repository</a>.</em></p> <p>In the last installation of “Cam learns to use a theorem prover”, we got acquainted with Liquid Haskell, induction, and formal specifications while writing a verified version of the legendary LeftPad function.</p> <p>Today, I’ll be going over my solution to the next challenge, “Unique”. This one actually gave me more trouble than LeftPad, contrary to Twitter’s opinion of it.</p> <p>Our task is to, given a list <code class="highlighter-rouge">L</code> construct a list containing every unique element of <code class="highlighter-rouge">L</code> exactly once (in any order).</p> <p>Let’s start with the code itself:</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ reflect elem @-}</span> <span class="n">elem</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="n">elem</span> <span class="n">x</span> <span class="kt">[]</span> <span class="o">=</span> <span class="kt">False</span> <span class="n">elem</span> <span class="n">x</span> <span class="p">(</span><span class="n">y</span><span class="o">:</span><span class="n">ys</span><span class="p">)</span> <span class="o">=</span> <span class="n">x</span> <span class="o">==</span> <span class="n">y</span> <span class="o">||</span> <span class="n">elem</span> <span class="n">x</span> <span class="n">ys</span> <span class="cm">{-@ reflect unique @-}</span> <span class="n">unique</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="n">unique</span> <span class="kt">[]</span> <span class="o">=</span> <span class="kt">[]</span> <span class="n">unique</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="o">|</span> <span class="n">x</span> <span class="p"></span><span class="n">elem</span><span class="p"></span> <span class="n">xs</span> <span class="o">=</span> <span class="n">unique</span> <span class="n">xs</span> <span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">x</span><span class="o">:</span><span class="n">unique</span> <span class="n">xs</span> </code></pre></div></div> <p>This is fairly straightforward – an element <code class="highlighter-rouge">x</code> is unique in <code class="highlighter-rouge">x:xs</code> if <code class="highlighter-rouge">x</code> is not present in <code class="highlighter-rouge">xs</code>, so we keep it. Now for the specification:</p> <blockquote> <p>Takes a sequence of integers, returns the unique elements of that list. There is no requirement on the ordering of the returned values.</p> </blockquote> <p>As usual, I think that our code is “obviously” correct. In fact, our code is even more general – we can take any <code class="highlighter-rouge">Eq a</code>, but it specializes to integers easily enough.</p> <p>Let’s see if we can prove it.</p> <h2 id="the-obvious-part">The “Obvious” Part</h2> <p>The “main” property of <code class="highlighter-rouge">Unique</code> is that every element in <code class="highlighter-rouge">xs</code> appears exactly once in <code class="highlighter-rouge">unique xs</code>. And this is important, because it means that a) we didn’t drop any elements and b) we actually removed duplicate elements. We can do so like this:</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ reflect count @-}</span> <span class="n">count</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="n">count</span> <span class="kr">_</span> <span class="kt">[]</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">count</span> <span class="n">x</span> <span class="p">(</span><span class="n">y</span><span class="o">:</span><span class="n">ys</span><span class="p">)</span> <span class="o">|</span> <span class="n">x</span> <span class="o">==</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">count</span> <span class="n">x</span> <span class="n">ys</span> <span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">count</span> <span class="n">x</span> <span class="n">ys</span> <span class="cm">{-@ thmUniqueNoDup :: xs:[a] -&gt; {x:a | elem x xs} -&gt; {count x (unique xs) == 1} @-}</span> <span class="n">thmUniqueNoDup</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="nb">()</span> </code></pre></div></div> <p>Now, here is where I got stuck. No matter what I tried, I couldn’t quite come up with a way to make LH infer this proof automagically, the way we did before.</p> <p>It turns out that LH does come with some hand-guidance tools. We can, in fact, step through code execution symbolically to show that it exhibits some property (correctness or otherwise) for all relevant inputs. While automating the entire process can be computationally intractable, we (as humans) have a fair bit more intuition and can “know” where to begin – we can write the proof, and the computer can check if our proof is correct<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup>.</p> <p>Here’s the case where <code class="highlighter-rouge">y</code> is an element of <code class="highlighter-rouge">xs</code>.</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">thmUniqueNoDup</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Proof</span> <span class="n">thmUniqueNoDup</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="n">y</span> <span class="o">|</span> <span class="n">y</span> <span class="p"></span><span class="n">elem</span><span class="p"></span> <span class="n">xs</span> <span class="o">=</span> <span class="kr">if</span> <span class="n">x</span> <span class="o">==</span> <span class="n">y</span> <span class="kr">then</span> <span class="n">count</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">))</span> <span class="o">==.</span> <span class="n">count</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="mi">1</span> <span class="o">?</span> <span class="n">thmUniqueNoDup</span> <span class="n">xs</span> <span class="n">y</span> <span class="o">***</span> <span class="kt">QED</span> <span class="kr">else</span> <span class="n">count</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">))</span> <span class="o">==.</span> <span class="n">count</span> <span class="n">y</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="n">count</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="mi">1</span> <span class="o">?</span> <span class="n">thmUniqueNoDup</span> <span class="n">xs</span> <span class="n">y</span> <span class="o">***</span> <span class="kt">QED</span> </code></pre></div></div> <p>Let’s break this down a bit. <code class="highlighter-rouge">==.</code> is the Liquid Haskell combinator for “extensionally equivalent to”, which means that the expressions on both sides will evaluate to the same value (or lack of value). When we use this, we’re asking LH to check whether using that equivalence as a step is justified<sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup>.</p> <p>As before, though, sometimes LH can’t figure out that a given step is justified on its own. Instead, we can provide a justification for a step with the <code class="highlighter-rouge">?</code> combinator. For example, in this step</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">count</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="mi">1</span> <span class="o">?</span> <span class="n">thmUniqueNoDup</span> <span class="n">xs</span> <span class="n">y</span> </code></pre></div></div> <p>By our assumption on <code class="highlighter-rouge">y</code>, we know <code class="highlighter-rouge">y </code><code class="highlighter-rouge">elem</code><code class="highlighter-rouge"> xs</code>. Thus, by the types of <code class="highlighter-rouge">thmUniqueNoDup</code> (that is, by our inductive hypothesis), we know that <code class="highlighter-rouge">count y (unique xs)</code> evaluates to <code class="highlighter-rouge">1</code>.</p> <p>The final thing here is <code class="highlighter-rouge">*** QED</code>, which finalizes the “proof” in Haskell types. We need this to ensure that we can actually have a term of the Haskell type <code class="highlighter-rouge">Proof</code> provided by Liquid Haskell.</p> <p>Next case might start looking like this:</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">thmUniqueNoDup</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Proof</span> <span class="n">thmUniqueNoDup</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="n">y</span> <span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">count</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">))</span> <span class="o">==.</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">count</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="mi">1</span> <span class="o">?</span> <span class="n">someTheorem</span> <span class="o">***</span> <span class="kt">QED</span> </code></pre></div></div> <p>So what can we use for <code class="highlighter-rouge">someTheorem</code>? We’d need to somehow show that <code class="highlighter-rouge">count y (unique xs)</code> is <code class="highlighter-rouge">0</code>, but our inductive hypothesis can only tell us that <code class="highlighter-rouge">count x (unique xs)</code> is equal to <code class="highlighter-rouge">1</code>!</p> <p>It turns out that we don’t actually have enough to show that the other case is sound. To do that, we’ll need two auxiliary theorems:</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ reflect thmElemCount @-}</span> <span class="cm">{-@ thmElemCount :: Eq a =&gt; x:a -&gt; xs:{[a] | not elem x xs} -&gt; { count x xs == 0 } @-}</span> <span class="n">thmElemCount</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="nb">()</span> <span class="n">thmElemCount</span> <span class="n">x</span> <span class="kt">[]</span> <span class="o">=</span> <span class="nb">()</span> <span class="n">thmElemCount</span> <span class="n">x</span> <span class="p">(</span><span class="n">y</span><span class="o">:</span><span class="n">ys</span><span class="p">)</span> <span class="o">=</span> <span class="n">thmElemCount</span> <span class="n">x</span> <span class="n">ys</span> <span class="cm">{-@ thmUniqueExistInv :: xs:[a] -&gt; {x:a | not elem x xs} -&gt; { not elem x (unique xs) } @-}</span> <span class="n">thmUniqueExistInv</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Proof</span> <span class="n">thmUniqueExistInv</span> <span class="kt">[]</span> <span class="n">y</span> <span class="o">=</span> <span class="n">trivial</span> <span class="o">***</span> <span class="kt">QED</span> <span class="n">thmUniqueExistInv</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="n">y</span> <span class="o">=</span> <span class="n">elem</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">))</span> <span class="o">==.</span> <span class="n">elem</span> <span class="n">y</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="n">elem</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="kt">False</span> <span class="o">?</span> <span class="n">thmUniqueExistInv</span> <span class="n">xs</span> <span class="n">y</span> <span class="o">***</span> <span class="kt">QED</span> </code></pre></div></div> <p>The first simply creates and proves the “obvious” relation between <code class="highlighter-rouge">elem</code> and <code class="highlighter-rouge">count</code>. We’ll ultimately be invoking <em>this</em> theorem to get our <code class="highlighter-rouge">0</code>. The second is slightly more interesting. Note that the precondition for <code class="highlighter-rouge">thmElemCount</code> is that <code class="highlighter-rouge">x</code> is <em>not</em> an element of <code class="highlighter-rouge">xs</code>. However, we haven’t actually shown that <code class="highlighter-rouge">unique xs</code> won’t contain any elements not in <code class="highlighter-rouge">xs</code>! So we need to prove it.</p> <p>This gives us our final step:</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="o">==.</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">count</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="mi">1</span> <span class="o">?</span> <span class="n">thmUniqueExistInv</span> <span class="n">xs</span> <span class="n">y</span> <span class="p"></span><span class="n">seq</span><span class="p"></span> <span class="n">thmElemCount</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">***</span> <span class="kt">QED</span> </code></pre></div></div> <p>Notice the <code class="highlighter-rouge">seq</code> here. In order to cite <code class="highlighter-rouge">thmElemCount</code>, we need to show that <code class="highlighter-rouge">y</code> is not an element of <code class="highlighter-rouge">unique xs</code>, so we must first use <code class="highlighter-rouge">thmUniqueExistInv</code>.</p> <h2 id="the-less-obvious-part">The Less Obvious Part</h2> <p>In most worlds, this would be enough. We’ve shown that, for every element in <code class="highlighter-rouge">xs</code>, <code class="highlighter-rouge">unique xs</code> contains that element exactly once. Incidentally, we also showed that <code class="highlighter-rouge">unique xs</code> didn’t “spawn” elements – if an element isn’t in <code class="highlighter-rouge">xs</code>, it can’t be in <code class="highlighter-rouge">unique xs</code> either.</p> <p>However, we’re not done yet. The world of type theory (of which Liquid Haskell’s refinement types are a subworld) is one based on <em>constructive logic</em> (otherwise known as <em>intuitionistic logic</em>), which removes the law of the excluded middle. That is, <script type="math/tex">\neg \neg P \nRightarrow P</script>. In this particular case, that means that we’re not done! We’ve only proven that every element in <code class="highlighter-rouge">unique xs</code> is <em>not not in</em> <code class="highlighter-rouge">xs</code>, which is not the same as saying that it’s <em>in</em> <code class="highlighter-rouge">xs</code><sup id="fnref:3"><a href="#fn:3" class="footnote">3</a></sup>.</p> <p>For the sake of completeness, let’s show that <code class="highlighter-rouge">x</code> being in <code class="highlighter-rouge">unique xs</code> implies that <code class="highlighter-rouge">x</code> is in <code class="highlighter-rouge">xs</code> as well!</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ thmUniqueExist :: xs:[a] -&gt; {x:a | elem x (unique xs)} -&gt; { elem x xs } @-}</span> <span class="n">thmUniqueExist</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Proof</span> <span class="n">thmUniqueExist</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="n">y</span> <span class="o">|</span> <span class="n">y</span> <span class="o">==</span> <span class="n">x</span> <span class="o">=</span> <span class="n">trivial</span> <span class="o">***</span> <span class="kt">QED</span> <span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">elem</span> <span class="n">y</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="n">elem</span> <span class="n">y</span> <span class="n">xs</span> <span class="o">==.</span> <span class="kt">True</span> <span class="o">?</span> <span class="n">thmUniqueExist'</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="n">y</span> <span class="p"></span><span class="n">seq</span><span class="p"></span> <span class="n">thmUniqueExist</span> <span class="n">xs</span> <span class="n">y</span> <span class="o">***</span> <span class="kt">QED</span> <span class="cm">{-@ thmUniqueExist' :: xs:{[a] | len xs &gt; 0} -&gt; {x:a | elem x (unique xs) &amp;&amp; x != hd xs} -&gt; { elem x (unique (tl xs)) } @-}</span> <span class="n">thmUniqueExist'</span> <span class="o">::</span> <span class="kt">Eq</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Proof</span> <span class="n">thmUniqueExist'</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="n">y</span> <span class="o">=</span> <span class="n">elem</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="p">(</span><span class="n">tl</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)))</span> <span class="o">==.</span> <span class="n">elem</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="n">elem</span> <span class="n">y</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">unique</span> <span class="n">xs</span><span class="p">)</span> <span class="o">==.</span> <span class="n">elem</span> <span class="n">y</span> <span class="p">(</span><span class="n">unique</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">))</span> <span class="o">==.</span> <span class="kt">True</span> <span class="o">***</span> <span class="kt">QED</span> </code></pre></div></div> <p>I won’t go over every step, but an important step is in the derivation of <code class="highlighter-rouge">thmUniqueExist'</code>, in which we actually step “backwards” from <code class="highlighter-rouge">elem y (x:unique xs)</code> to <code class="highlighter-rouge">elem y (unique (x:xs))</code> (by the definition of <code class="highlighter-rouge">unique</code>). I’m actually not particularly satisfied with this proof; in particular I find the forced use of <code class="highlighter-rouge">hd</code> and <code class="highlighter-rouge">tl</code> to be extremely inelegant. If you know of a better way, please let me know!</p> <p>Onwards to Fulcrum! This one may take a bit longer – I’ve been sitting on this solution to Unique for about a week, but Fulcrum has been giving me <em>way</em> more trouble.</p> <div class="footnotes"> <ol> <li id="fn:1"> <p>If you think this sounds similar to P vs NP, you’d be correct! Proof generation is actually coNP-complete, which means that it’s easy to check that a proof is <em>wrong</em>, but difficult to create a proof from scratch or verify that it’s correct! <a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:2"> <p>Technically, LH doesn’t check <em>every</em> step. The official documentation states that LH will only check that the first and last steps are justified, but with some experimentation I’ve found that certain intermediate steps are mandatory. Who knows? <a href="#fnref:2" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:3"> <p>The difference may seem academic to you, and in this case it arguably is. For a better discussion of the difference between classical and logical systems, check out <a href="https://existentialtype.wordpress.com/2017/03/04/a-proof-by-contradiction-is-not-a-proof-that-derives-a-contradiction/">this</a> awesome blog post by Professor Harper of CMU. <a href="#fnref:3" class="reversefootnote">&#8617;</a></p> </li> </ol> </div>Cameron Wongcam@camdar.ioThis is the second of a series on Hillel Wayne’s Great Theorem Prover Showdown. Make sure to check out the first part! You can also follow my progress in this repository.The Great Theorem Prover Challenge I2018-06-21T00:00:00+00:002018-06-21T00:00:00+00:00https://ct075.github.io/2018-06-21/theorem-prover-1<p>I was having a discussion with a coworker over lunch the other day about code reasoning and informal correctness (things like “it feels correct” and “it passes a very extensive array of tests”). At some point, the conversation strayed to the relative merits of imperative vs functional paradigms, and which one “feels” easier, as it always does. As a fairly loud proponent of functional-styled programming, I, of course, made the standard arguments about the heap and global state, etc, and was rebuffed by “the overhead of juggling abstractions”.</p> <p>Now, I won’t get into why I think the response is bullshit (tl;dr if you have to “juggle” an abstraction, it’s not really a very good abstraction), but it did leave me somewhat at a loss as to how we were defining “feeling” easy. It’s a very fuzzy, subjective term, and not really one I’m prepared to go into at any depth.</p> <p>This brings us to the subject of formal verification. One argument for powerful and expressive static typesystems is to give strong compile-time guarantees, which can aid in correctness. For example, in Python, if we create a function that operates primarily on integers, it may not work correctly or even just blow up if given, say, a string. This introduces a vector for bugs if we don’t properly check the return type of some other function, and can cause crashes or (worse) subtly wrong behavior. However, with a static typesystem, we can <em>know</em> that the function is never called on anything but a number – because otherwise the function wouldn’t typecheck, and the code wouldn’t compile at all!</p> <p>Formal verification is taking this idea to its logical conclusion – what if we encode <em>all</em> correctness properties inherent to the code in some way that can be checked alongisde the code at compile time? Well, the first thing you’ll find is that you start running into Rice’s Theorem (or more generally, the halting problem), but you also find that there are a surprising number of things that can be done and ways to do them.</p> <p>A few months ago, I found Hillel Wayne’s <a href="https://www.hillelwayne.com/post/theorem-prover-showdown/">Great Theorem Prover Showdown</a> on reddit, and found the included discussion quite interesting. After that argument with my friend, I decided that I’d also bite the bullet and try it for myself.</p> <p>“After all,” I thought, “it’s <em>functional programming</em>. We do proofs like that for the entire semester in 15-150; how hard can it be to write an equivalent in a theorem proving language?”</p> <p>Now, before I go any further, let me outline my previous personal experience with formal verification:</p> <ul> <li>A brief section of a static analysis course at university discussing loop invariants and pre-/post-condition analysis (contracts)</li> <li>An even briefer single lesson about refinement types, given by some friends in the CMU PL community</li> <li>Hillel Wayne’s blog post</li> </ul> <p>Note that none of the above even involved <em>touching</em> a theorem prover (the closest I had gotten was brute-forcing some logic problems using the Z3 Haskell bindings). Not that I’m about to let that stop me. So let’s dive into <code class="highlighter-rouge">Leftpad</code>, the first challenge. I chose to approach this with <a href="https://ucsd-progsys.github.io/liquidhaskell-blog/">Liquid Haskell</a> because it seemed to be the simpler of the two (Idris being the other option) to just pick up and get running.</p> <h1 id="the-challenge">The Challenge</h1> <p><em>You can follow my progress in <a href="https://github.com/CT075/theorem-prover-showdown">this</a> repository.</em></p> <p>Our goal is to replicate the classic <code class="highlighter-rouge">leftPad</code> function that pads a string to a desired length.</p> <p>The first thing we need to do is write the code itself. Most people attempting this challenge did some variant on a <code class="highlighter-rouge">replicate</code> solution, but I opted for a more naturally-recursive version (this ended up being a good decision).</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">leftPad</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="n">leftPad</span> <span class="n">n</span> <span class="n">x</span> <span class="n">xs</span> <span class="o">=</span> <span class="n">leftPad'</span> <span class="n">k</span> <span class="n">x</span> <span class="n">xs</span> <span class="kr">where</span> <span class="n">k</span> <span class="o">=</span> <span class="n">max</span> <span class="mi">0</span> <span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="n">size</span> <span class="n">xs</span><span class="p">)</span> <span class="n">leftPad'</span> <span class="mi">0</span> <span class="kr">_</span> <span class="n">xs</span> <span class="o">=</span> <span class="n">xs</span> <span class="n">leftPad'</span> <span class="n">n</span> <span class="n">x</span> <span class="n">xs</span> <span class="o">=</span> <span class="n">x</span> <span class="o">:</span> <span class="n">leftPad'</span> <span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">x</span> <span class="n">xs</span> </code></pre></div></div> <p>Now, let’s get onto actually proving this “correct”. Everyone with a hat in this game (including me!) will talk your ears off about how “correctness” doesn’t really mean anything without a “specification” to guide it. In this case, we’ve been helpfully provided an informal spec, but not really anything by way of a formalized contract to follow. That’s fine, part of our job is to turn informal specs into formal ones.</p> <p>The specification for LeftPad, as given is:</p> <blockquote> <p>Takes a padding character, a string, and a total length, returns the string padded to that length with that character. If length is less than the length of the string, does nothing.</p> </blockquote> <p>Ooookay. That’s a little vague. Breaking it down, we start by looking at the inputs – “given a padding character, a string, and a total length”. This gives us our input types:</p> <h2 id="a-first-try">A First Try</h2> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ leftPad :: Int -&gt; a -&gt; [a] -&gt; [a] @-}</span> </code></pre></div></div> <p>It’s a start, but it’s not particularly useful. To really leverage the power of LH, we need to actually use our types to prove something! The point of leftPad is to ensure that a string is the correct length, so let’s try to prove that:</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ reflect leftPad @-}</span> <span class="cm">{-@ leftPad :: n:Int -&gt; x:a -&gt; xs:[a] -&gt; result:{ [a] | size result = max n (size xs) } @-}</span> </code></pre></div></div> <p>What this means is that the result of leftPad will be of length <code class="highlighter-rouge">n</code> or the same length of the original string, whichever is larger. We also <code class="highlighter-rouge">reflect</code> our function, so it becomes a proper term in the logic.</p> <p>According to some people, this is enough. The function is “obviously” correct. Let’s test that assumption – does Liquid Haskell think that we’re done?</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Error: Liquid Type Mismatch 33 | leftPad n x xs = leftPad' k x xs ^^^^^^^^^^^^^^^ Inferred type VV : {v : [a] | [SNIPPED: big mess] } not a subtype of Required type VV : {VV : [a] | Main.size VV == Main.max n (Main.size xs)} In Context xs : {v : [a] | Main.size v &gt;= 0 &amp;&amp; len v &gt;= 0} n : {n : Int | n &gt;= 0} x : a Error: Liquid Type Mismatch 44 | leftPad' n x xs = x : leftPad' (n-1) x xs ^^^ Inferred type VV : {v : Int | v == ?b - (1 : int)} not a subtype of Required type VV : {VV : Int | VV &lt; ?b &amp;&amp; VV &gt;= 0} In Context ?b : Int </code></pre></div></div> <p>(if you just tried this for yourself, you may need to also define <code class="highlighter-rouge">size</code> and <code class="highlighter-rouge">max</code> and reflect them into the logic)</p> <p>Okay, there are two errors here. The first is that big mess of types on the function we actuallly annotated. I’ll get there in a bit. The <em>second</em> error, however, seems to be saying that our types are wrong for <code class="highlighter-rouge">leftPad'</code> as well! What gives?</p> <p>It turns out that LH is actually smart enough to recognize that the function won’t terminate if given an input <code class="highlighter-rouge">n &lt; 0</code>. Because we like our programs to be decidable, this lets LH decide that the type of the first argument to <code class="highlighter-rouge">leftPad'</code> should be positive, as seen by the <code class="highlighter-rouge">VV &gt;= 0</code>. In fact, a similar argument can be used to show an <em>upper</em> bound as well! Notice that our only base case is <code class="highlighter-rouge">n = 0</code>. This means that, in order to make progress, we should have <code class="highlighter-rouge">n</code> decrease every time – thus <code class="highlighter-rouge">VV &lt; ?b</code>. This seems like a less hard rule than the previous, but I’m not really familiar enough to say what would happen if we had multiple axes (or even just a nonstandard termination condition). However, we have no conditions on the <em>original</em> input, and there’s nothing stopping us from just passing in a negative <code class="highlighter-rouge">n</code>. Thus the type mismatch.</p> <p>The crux of the issue is that LH can’t (or won’t try to) understand what <code class="highlighter-rouge">leftPad'</code> is doing, or what theorems it upholds. We can fix that by adding some manual annotations of our own:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{-@ reflect leftPad' @-} {-@ leftPad' :: n:Nat -&gt; x:a -&gt; xs:[a] -&gt; result:{[a] | size result = n + size xs } @-} </code></pre></div></div> <p>The only new thing is <code class="highlighter-rouge">Nat</code>, which means <code class="highlighter-rouge">n:{Int | n &gt;= 0}</code><sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup>.</p> <p>We run it through <code class="highlighter-rouge">liquid</code> and… it’s safe! Wait, what?</p> <p>This blew my mind the first time I saw this. The only effort really expended by me was to <em>tell Liquid Haskell what my functions should do</em>, and it was able to tell me with no<sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup> other information!</p> <p>The key here is induction, the idea that properties hold over certain operations. If that seems a little abstract, let’s walk it through.</p> <p>First, let’s make sure that the function is even well-formed. We have</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">leftPad</span> <span class="n">n</span> <span class="n">x</span> <span class="n">xs</span> <span class="o">=</span> <span class="n">leftPad'</span> <span class="n">k</span> <span class="n">x</span> <span class="n">xs</span> <span class="kr">where</span> <span class="n">k</span> <span class="o">=</span> <span class="n">max</span> <span class="mi">0</span> <span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="n">size</span> <span class="n">xs</span><span class="p">)</span> </code></pre></div></div> <p><code class="highlighter-rouge">max</code> probably has a type like <code class="highlighter-rouge">x:Int -&gt; y:Int -&gt; r:{Int | r &gt;= x &amp;&amp; r &gt;= y}</code> <sup id="fnref:3"><a href="#fn:3" class="footnote">3</a></sup>. This means that <code class="highlighter-rouge">max 0 i</code> is a subtype of <code class="highlighter-rouge">r:{Int | r &gt;= 0}</code> automatically… which means that <code class="highlighter-rouge">k</code> must have type <code class="highlighter-rouge">Nat</code>, and is thus a valid input to <code class="highlighter-rouge">leftPad'</code>! <code class="highlighter-rouge">x</code> and <code class="highlighter-rouge">xs</code> have no special conditions, so those just pass through.</p> <p>Onto <code class="highlighter-rouge">leftPad'</code>. We know that, given valid inputs, our result must satisfy the type <code class="highlighter-rouge">r:{[a] | size r = n + size xs}</code>. For our base case, we find that</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>leftPad' 0 x xs = xs </code></pre></div></div> <p>so <code class="highlighter-rouge">r = xs</code> in this case. We also have <code class="highlighter-rouge">n = 0</code>, so we want to show that</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>size xs = 0 + size xs </code></pre></div></div> <p>which is a no-brainer.</p> <p>Next, our inductive step.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>leftPad' n x xs = x : leftPad' (n-1) x xs </code></pre></div></div> <p>For this to be well-typed, we need to know a few things, namely that <code class="highlighter-rouge">n-1</code> is a <code class="highlighter-rouge">Nat</code>, or that <code class="highlighter-rouge">n-1 &gt;= 0</code>. Luckily, we already know that <code class="highlighter-rouge">n &gt;= 0</code> because we’re given <code class="highlighter-rouge">n:Nat</code> as a precondition. But wait, we also know that <code class="highlighter-rouge">n != 0</code>, because we pattern matched on it earlier! Thus, <code class="highlighter-rouge">n &gt; 0</code> for sure, so <code class="highlighter-rouge">n - 1 &gt;= 0</code> is fine.</p> <p>Next, we can use our “inductive hypothesis” – we can assume that an recursive uses of <code class="highlighter-rouge">leftPad'</code> are well-typed. That means, in this case, that we know that <code class="highlighter-rouge">leftPad' (n-1) x xs</code> returns something of type <code class="highlighter-rouge">r:{[a] | size r = n-1 + size xs}</code>. We want to exhibit a term of type <code class="highlighter-rouge">r:{[a] | size r = n + size xs}</code>. Well, by the definition of <code class="highlighter-rouge">size</code>, <code class="highlighter-rouge">x:l</code> has type <code class="highlighter-rouge">r:{[a] | size r = 1 + size l}</code>, so our returned expression <code class="highlighter-rouge">x : leftPad' (n-1) x xs</code> should have type <code class="highlighter-rouge">r:{[a] | size r = 1 + size (leftPad' (n-1) x xs) }</code>… which is <code class="highlighter-rouge">r:{[a] | size r = 1 + (n-1) + size xs}</code>, or <em>exactly what we wanted</em>.</p> <p>All of this reasoning, by the way, was decided by LH with no prompting from me. The only thing I did was provide the code and the types, the typechecking happened automagically. The phrase “obviously correct” has a bit more weight after that.</p> <h2 id="not-so-obvious">Not so Obvious</h2> <p>You may notice that we haven’t actually fulfilled the spec given by Hillel. For example, a function that took a random <code class="highlighter-rouge">n</code> elements from the input would also fulfill the spec we’ve given so far. We need to also encode that the original string is <em>padded</em> with the pad character. The way this is formalized is as follows:</p> <blockquote> <p>Given a list <code class="highlighter-rouge">xs</code>, fill element <code class="highlighter-rouge">x</code> and a length <code class="highlighter-rouge">n</code>, for any index <code class="highlighter-rouge">i</code>,</p> <ul> <li>If <code class="highlighter-rouge">i &lt; n - size xs</code>, the list element should be <code class="highlighter-rouge">x</code>.</li> <li>Otherwise, the list element should be the corresponding element of <code class="highlighter-rouge">xs</code></li> </ul> </blockquote> <p>We can represent this in Haskell by introducing a separate predicate as follows:</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ reflect leftPadElt @-}</span> <span class="cm">{-@ leftPadElt :: n:Int -&gt; x:a -&gt; xs:[a] -&gt; i:{Nat | i &lt; n} -&gt; a @-}</span> <span class="n">leftPadElt</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="n">leftPad</span> <span class="kt">Elt</span> <span class="n">n</span> <span class="n">x</span> <span class="n">xs</span> <span class="n">i</span> <span class="o">|</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">k</span> <span class="o">=</span> <span class="n">x</span> <span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">x</span> <span class="o">!!</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">k</span><span class="p">)</span> <span class="kr">where</span> <span class="n">k</span> <span class="o">=</span> <span class="n">max</span> <span class="mi">0</span> <span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="n">size</span> <span class="n">xs</span><span class="p">)</span> </code></pre></div></div> <p>Because LH doesn’t have “forall” quantification (this leads to a nasty thing called “the halting problem”), we can’t really encode something like</p> <p>“For all <code class="highlighter-rouge">i</code> from <code class="highlighter-rouge">0 to n</code>, <code class="highlighter-rouge">leftPad n x xs !! i == leftPadElt n x xs i</code>”</p> <p>into the type of <code class="highlighter-rouge">leftPad</code> itself. Instead, we need to show this by demonstrating a term of type</p> <p><code class="highlighter-rouge">n:Int -&gt; x:a -&gt; xs:[a] -&gt; i:{Nat | i &lt; n} -&gt; {leftPad n x xs !! i == leftPadElt n x xs i}</code></p> <p>So, how do we do that? Well, any proof with no actually associated “data” (like the list elements) can be seen as having a refinement on the type <code class="highlighter-rouge">()</code>, a term that fulfills all the conditions we need. If that’s a little hard to follow, don’t worry – it will (hopefully!) make more sense in a moment.</p> <p>There are two parts to the elements definition, one for the first <code class="highlighter-rouge">k</code> elements and one for the last <code class="highlighter-rouge">size xs</code> elements, so it makes sense to split our “proof” into two parts. Let’s take a look at the first part.</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ thmLeftPadA :: n:Nat -&gt; x:a -&gt; xs:[a] -&gt; i:{Nat | i &lt; n} -&gt; { leftPad' n x xs !! i == x } @-}</span> </code></pre></div></div> <p>Notice that I’m using <code class="highlighter-rouge">leftPad'</code> here instead of <code class="highlighter-rouge">leftPad</code>. That’s because we’ve already shown (defined) <code class="highlighter-rouge">leftPad</code> to be a special case of <code class="highlighter-rouge">leftPad'</code>, so it makes sense to prove something about the easier function to reason about.</p> <p>So what would a term like this look like? Well, we can start with something simple: by definition, <code class="highlighter-rouge">leftPad' n x xs = x:[something]</code><sup id="fnref:4"><a href="#fn:4" class="footnote">4</a></sup>. But wait, that means <code class="highlighter-rouge">leftPad' n x xs !! 0 == x</code> always… so the result of <code class="highlighter-rouge">thmLeftPadA n xs 0</code> <em>already</em> has the right type.</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">thmLeftPadA</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="nb">()</span> <span class="n">thmLeftPadA</span> <span class="n">n</span> <span class="n">x</span> <span class="n">xs</span> <span class="mi">0</span> <span class="o">=</span> <span class="nb">()</span> </code></pre></div></div> <p>What about the other case? Same as any other proof by induction!</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">thmLeftPadA</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="nb">()</span> <span class="n">thmLeftPadA</span> <span class="n">n</span> <span class="n">x</span> <span class="n">xs</span> <span class="mi">0</span> <span class="o">=</span> <span class="nb">()</span> <span class="n">thmLeftPadA</span> <span class="n">n</span> <span class="n">x</span> <span class="n">xs</span> <span class="n">i</span> <span class="o">=</span> <span class="n">thmLeftPadA</span> <span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">x</span> <span class="n">xs</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> </code></pre></div></div> <p>By the same reasoning as before, we know that <code class="highlighter-rouge">i &gt; 0</code>. We actually know <code class="highlighter-rouge">n &gt; 0</code> as well, because the type <code class="highlighter-rouge">i:{Nat | i &lt; 0}</code> is uninhabited. Thus the recursive call to <code class="highlighter-rouge">thmLeftPadA</code> is well-founded. This gives us a term of type <code class="highlighter-rouge">{leftPad' (n-1) x xs !! (i-1) == x}</code>. Again by definition, <code class="highlighter-rouge">leftPad' n x xs</code> is equal to <code class="highlighter-rouge">x:leftPad' (n-1) x xs</code> and <code class="highlighter-rouge">(x:xs) !! i</code> is <code class="highlighter-rouge">xs !! i-1</code>, which means that this is, in fact, equivalent to a term of the type we want!</p> <p>The second part looks like this (the derivation is left to the reader):</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ thmLeftPadB :: n:Nat -&gt; x:a -&gt; xs:[a] -&gt; i:{Nat | i &gt;= n &amp;&amp; i &lt; n + size xs} -&gt; { leftPad' n x xs !! i == xs !! (i-n) } @-}</span> <span class="n">thmLeftPadB</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="nb">()</span> <span class="n">thmLeftPadB</span> <span class="mi">0</span> <span class="n">x</span> <span class="n">xs</span> <span class="n">i</span> <span class="o">=</span> <span class="nb">()</span> <span class="n">thmLeftPadB</span> <span class="n">n</span> <span class="n">x</span> <span class="n">xs</span> <span class="n">i</span> <span class="o">=</span> <span class="n">thmLeftPadB</span> <span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">x</span> <span class="n">xs</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> </code></pre></div></div> <p>All that’s left is to string these two parts together into our final proof:</p> <div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">{-@ thmLeftPad :: n:Int -&gt; x:a -&gt; xs:[a] -&gt; i:{Nat | i &lt; n} -&gt; { leftPad n x xs !! i == leftPadElt n x xs i } @-}</span> <span class="n">thmLeftPad</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="nb">()</span> <span class="n">thmLeftPad</span> <span class="n">n</span> <span class="n">x</span> <span class="n">xs</span> <span class="n">i</span> <span class="o">|</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">k</span> <span class="o">=</span> <span class="n">thmLeftPadA</span> <span class="n">k</span> <span class="n">x</span> <span class="n">xs</span> <span class="n">i</span> <span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">thmLeftPadB</span> <span class="n">k</span> <span class="n">x</span> <span class="n">xs</span> <span class="n">i</span> <span class="kr">where</span> <span class="n">k</span> <span class="o">=</span> <span class="n">max</span> <span class="mi">0</span> <span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="n">size</span> <span class="n">xs</span><span class="p">)</span> </code></pre></div></div> <p>And there you have it! We have a <em>formally verified</em> leftPad.</p> <div class="footnotes"> <ol> <li id="fn:1"> <p>I’m a computer scientist. Of course 0 is a natural number. <a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:2"> <p>Okay, with a little extra. I did have to define <code class="highlighter-rouge">size</code> and <code class="highlighter-rouge">max</code>. <a href="#fnref:2" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:3"> <p>Because I just let LH infer it for me, I don’t actually know for sure. <a href="#fnref:3" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:4"> <p>Technically, <code class="highlighter-rouge">n = 0</code> is the exception, but that actually violates the precondition on <code class="highlighter-rouge">thmLeftPadA</code>. <a href="#fnref:4" class="reversefootnote">&#8617;</a></p> </li> </ol> </div>Cameron Wongcam@camdar.ioI was having a discussion with a coworker over lunch the other day about code reasoning and informal correctness (things like “it feels correct” and “it passes a very extensive array of tests”). At some point, the conversation strayed to the relative merits of imperative vs functional paradigms, and which one “feels” easier, as it always does. As a fairly loud proponent of functional-styled programming, I, of course, made the standard arguments about the heap and global state, etc, and was rebuffed by “the overhead of juggling abstractions”.Who needs an API, anyway?2016-06-30T00:00:00+00:002016-06-30T00:00:00+00:00https://ct075.github.io/2016-06-30/trolling<p>Alt Title: My (Failed) Quest to Achieve Automated Trolling</p> <p>So today is my good friend’s birthday. As is typical of a birthday, I had several photos of said friend to post on his wall, but wait! Instead of being boring and doing a collage/album, I wanted to return the favor he paid to me on my birthday by posting one every hour. Given that I had seven of them, I would need to stick around (or at least pay attention) every hour. Ugh. Add that I’m impatient, and we get a recipe for “not a good idea”!</p> <p>Enter scripting! My thought was to set a timer for every hour to post a photo to his wall (with the accompanying caption), but little did I know that this was quite a tall order (and probably one that FB doesn’t endorse anyway).</p> <p>My first thought (of course) is to google “Facebook API post photo to wall”. This gets me to <a href="http://stackoverflow.com/questions/14504397/facebook-api-how-to-publish-photo-on-the-users-wall">this page</a> and <a href="https://developers.facebook.com/docs/php/howto/uploadphoto">this page</a>. Both look like PHP. Pass. (A bit more preliminary research showed that FB only officially supports JS and PHP. Yeesh. And they say they want to <em>promote</em> new programmers?)</p> <p>Searching for the python SDK is a little more fruitful, although I’m faced with the same problem of “how do i post phot”. With enough digging and reflection (the <a href="https://docs.python.org/3/library/inspect.html">inspect module</a> is fantastic and should be used on mystery libraries frequently if you’re like me and need to know how <em>everything works</em>), I find <a href="the same page as before">https://developers.facebook.com/docs/graph-api/reference/user/photos</a>, but now with some clarity - particularly the “target_id” parameter.</p> <p>All good, it seems - I can now programmatically upload photos to a friend’s wall. Last thing I need to do is authenticate.</p> <p>Wait, what?</p> <p>You see, Facebook authenticates its apps with some kind of “user token”. I have no idea how to obtain one of these, nor is it important enough for me to find out. I did do some fiddling around with Facebook Apps and ended up with something called an “App Secret”, but that doesn’t allow me to post to walls. I’m sure that, with enough patience, I could finagle a way (perhaps by adapting the php code found earlier), but at this point I’ve literally done an hour of research on, essentially, a prank.</p> <p><img src="http://imgs.xkcd.com/comics/automation.png" alt="xkcd 1319" /></p> <p><a href="https://xkcd.com/1319/">source</a></p> <p>Sitting here a few hours later, though, I’ve started to appreciate FB’s obscurity. By making their SDK so obtuse (well, the actual documentation looks alright, but <em>PHP</em>?), they drive away a lot of amateur (read: non-profitable) programmers like myself who are just looking for a bit of fun with the system. Particularly, it prevents people (namely, me) from screwing around and <em>automating</em> an annoyance system. Maybe it’s a good thing that FB doesn’t let other people mess up a potentially curated, professional wall with a mere script.</p> <p>At least, maybe not without some \$ behind it.</p>Cameron Wongcam@camdar.ioAlt Title: My (Failed) Quest to Achieve Automated TrollingPACTF 2016 - Reflections2016-05-22T00:00:00+00:002016-05-22T00:00:00+00:00https://ct075.github.io/2016-05-22/pactf-reflections<p>By the time anybody is reading this, we should have finalized both DQs and prizes for PACTF 2016. If I said I thought everything went 100% as I wanted it to, I’d be lying, but I’m overall pretty happy with how things went.</p> <p>The first thing I have to mention is how amazing it is to open the admin panel and see 1600 registered users and 1000 registered teams. When we gave our speech to the gathered members of the Abbot Academy Association (AAA), we mentioned that similar competitions had numbers close to this, but only after a year or two of running with a reputation built up. “We’d be thrilled to get half that many,” we said, predicting about 500 teams and 1000 users. By our guess (after filtering out the various dummy accounts we found and extrapolating a few more), we still got about 50% more participation than I’d ever dreamed of.</p> <h2 id="ctfery">CTFery</h2> <p>Writing the problems for this competition was by far the most fun part of this process, at least for me. Cryptography especially - you’d be surprised how much people were overthinking some of the problems (the square problem especially). The binary round was a little weirder; as a procrastinating high school student I predictably had not finished writing the code for the majority of my problems until the Saturday before Round 2 launched. That and I still had no idea how nc worked on the server-side (I ultimately ended up writing a python script to serve it on the port for me) so that was a fun last 3/4 hour rush to launch. I was almost completely uninvolved with the web round (I wrote the SQL problems but they were pretty much copy-pasted from other CTFs and the internet), but Tony was a real workhorse and pulled it together.</p> <p>I’m with several of the competitors in that having themed rounds really doesn’t work that well. Regex fits with “binary” just as well as everyone thinks - it doesn’t. When we first came up with the round format, the idea was that every round would have a hodgepodge of problems, and that they would each be a smaller version of what a larger CTF “sprint” would look like. At the same time, though, themed rounds meant that I could push off all my work until immediately before the round itself went live, so that’s that I suppose.</p> <p>I’m honestly surprised the framework held together as well as it did. We had some issues with the scoreboard being recalculated every time someone loaded it (leading to the weird issue where we’d cache the entire page including the navbar with the “your team” etc stuff in the top right corner), but that fixed itself when we got the server to thread properly. Writing CTFlex was a super educational experience that I haven’t really had before so I’m grateful for that.</p> <h2 id="communication">Communication</h2> <p>If you ask me, our biggest mistake (specifically my downfall) was our lack of communication, especially during the last round. This should come as a surprise to almost nobody who was in IRC at any point during that last week when I would show up solely to mention “yeah there are no hints this round” and then again to mention “yeah well rip the flagdump”. The first round was, in my mind, actually went pretty well (all things considered) beyond the whole Got Bits fiasco, but things sort of snowballed on my end. Specifically, I had two tests and an essay during Round 2 keeping me away from IRC, which led to the short-lived reign of “Camdar_mobile” and my personal annoyance at having to deal with things during my trip to PAX East that weekend.</p> <p>In hindsight, volunteering myself to be the “public face” of PACTF wasn’t as good an idea as I thought. As much as I enjoy hanging out on IRC and meeting cool people (to all of you who stuck around to chat for more than the forty-five seconds it took to ask me a question, this one goes out to you), it was kind of shitty mentally to deal with the constant complaints and insults (particularly about Got Bits round 1). As time went on, I had less and less motivation to sit on the channel and deal with the (at times) thirty different people begging me for hints (which, in turn, led to more people angrily emailing us for clarifications). I like to think I have pretty thick skin, but I’m really not suited for playing the part of the “front man” who has to wade through the accusations of cheating and bullshit problems.</p> <h2 id="well">Well…</h2> <p>Overall, though, I don’t think our first try could have gone much better. There were definitely some people who would disagree (the zodiac killer even took some time out of their busy schedule to kill the last six hours of our competition!), but I’m honestly okay with it. Thanks to everyone for their participation and feedback and we hope to see you again next year!</p>Cameron Wongcam@camdar.ioBy the time anybody is reading this, we should have finalized both DQs and prizes for PACTF 2016. If I said I thought everything went 100% as I wanted it to, I’d be lying, but I’m overall pretty happy with how things went.