Jekyll2022-04-16T20:12:03+00:00https://1computer1.github.io/feed.xml1Computer1Computer's personal website. A blog on various topics in programming and a portfolio of cool projects.1ComputerVariance and Subtyping2021-07-15T00:00:00+00:002021-07-15T00:00:00+00:00https://1computer1.github.io/posts/variance-and-subtyping<p>Variance is a concept in type systems, especially those with subtyping. Keeping variance in mind when working with advanced type-level machinery in languages is quite helpful, but it applies a lot to simpler concepts too, such as when you are designing classes and interfaces. In this post, we will get an understanding of what variance is and how we can use it to our advantage to write correct code.</p>
<p>Note that this post requires an understanding of type parameters, also known as generics, as well as basic OOP concepts.</p>
<h1 id="variance-of-type-parameters">Variance of Type Parameters</h1>
<h2 id="covariance">Covariance</h2>
<p>Consider the following classes in a statically-typed OOP language. We will be using TypeScript syntax, although they won’t necessarily typecheck.</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Animal</span> <span class="p">{</span>
<span class="k">public</span> <span class="nx">isLiving</span><span class="p">():</span> <span class="nx">boolean</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Dog</span> <span class="kd">extends</span> <span class="nx">Animal</span> <span class="p">{</span>
<span class="k">public</span> <span class="nx">woof</span><span class="p">():</span> <span class="kr">string</span> <span class="p">{</span>
<span class="k">return</span> <span class="dl">'</span><span class="s1">woof</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Cat</span> <span class="kd">extends</span> <span class="nx">Animal</span> <span class="p">{</span>
<span class="k">public</span> <span class="nx">meow</span><span class="p">():</span> <span class="kr">string</span> <span class="p">{</span>
<span class="k">return</span> <span class="dl">'</span><span class="s1">meow</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">ReadBox</span><span class="o"><</span><span class="nx">T</span><span class="o">></span> <span class="p">{</span>
<span class="k">private</span> <span class="na">value</span><span class="p">:</span> <span class="nx">T</span><span class="p">;</span>
<span class="k">public</span> <span class="kd">constructor</span><span class="p">(</span><span class="na">value</span><span class="p">:</span> <span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="nx">getValue</span><span class="p">():</span> <span class="nx">T</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As a refresher, we write <code class="language-plaintext highlighter-rouge">Dog extends Animal</code> to mean <code class="language-plaintext highlighter-rouge">Dog</code> is a subtype of <code class="language-plaintext highlighter-rouge">Animal</code><sup id="fnref:extends" role="doc-noteref"><a href="#fn:extends" class="footnote" rel="footnote">1</a></sup>.</p>
<p>Then, the question is, does it follow that <code class="language-plaintext highlighter-rouge">ReadBox<Dog></code> is a subtype of <code class="language-plaintext highlighter-rouge">ReadBox<Animal></code>? Well, if we want this to be true, it should be that whenever we want to use a <code class="language-plaintext highlighter-rouge">ReadBox<Animal></code>, we can instead give it a <code class="language-plaintext highlighter-rouge">ReadBox<Dog></code> and everything should work.</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">isLivingInBox</span><span class="p">(</span><span class="nx">box</span><span class="p">:</span> <span class="nx">ReadBox</span><span class="o"><</span><span class="nx">Animal</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">box</span><span class="p">.</span><span class="nx">getValue</span><span class="p">().</span><span class="nx">isLiving</span><span class="p">();</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">dogBox</span><span class="p">:</span> <span class="nx">ReadBox</span><span class="o"><</span><span class="nx">Dog</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ReadBox</span><span class="p">(</span><span class="k">new</span> <span class="nx">Dog</span><span class="p">());</span>
<span class="nx">isLivingInBox</span><span class="p">(</span><span class="nx">dogBox</span><span class="p">);</span>
</code></pre></div></div>
<p>And we can see that there really is nothing that stops this from being correct. No matter what, as long as the class extends <code class="language-plaintext highlighter-rouge">Animal</code> and therefore has the <code class="language-plaintext highlighter-rouge">isLiving</code> method, it works. Because of the fact that for types <code class="language-plaintext highlighter-rouge">T, U</code> where <code class="language-plaintext highlighter-rouge">T extends U</code>, it follows that <code class="language-plaintext highlighter-rouge">ReadBox<T> extends ReadBox<U></code>, we say that <code class="language-plaintext highlighter-rouge">ReadBox</code> is <em>covariant</em> in its type parameter<sup id="fnref:one_param" role="doc-noteref"><a href="#fn:one_param" class="footnote" rel="footnote">2</a></sup>: it preserves the subtyping relationship.</p>
<h2 id="contravariance">Contravariance</h2>
<p>Consider the same <code class="language-plaintext highlighter-rouge">Animal</code> and <code class="language-plaintext highlighter-rouge">Dog</code> class, but this different <code class="language-plaintext highlighter-rouge">ListenBox</code> where we can only tell new values are coming in and be notified of when the box’s value changes:</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">ListenBox</span><span class="o"><</span><span class="nx">T</span><span class="o">></span> <span class="p">{</span>
<span class="k">private</span> <span class="na">listener</span><span class="p">:</span> <span class="p">(</span><span class="na">value</span><span class="p">:</span> <span class="nx">T</span><span class="p">)</span> <span class="o">=></span> <span class="k">void</span><span class="p">;</span>
<span class="k">public</span> <span class="kd">constructor</span><span class="p">(</span><span class="na">listener</span><span class="p">:</span> <span class="p">(</span><span class="na">value</span><span class="p">:</span> <span class="nx">T</span><span class="p">)</span> <span class="o">=></span> <span class="k">void</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">listener</span> <span class="o">=</span> <span class="nx">listener</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="nx">tell</span><span class="p">(</span><span class="na">value</span><span class="p">:</span> <span class="nx">T</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">listener</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Is <code class="language-plaintext highlighter-rouge">ListenBox</code> covariant in its type parameter?</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">dogBox</span><span class="p">:</span> <span class="nx">ListenBox</span><span class="o"><</span><span class="nx">Dog</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ListenBox</span><span class="p">((</span><span class="nx">newDog</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">newDog</span><span class="p">.</span><span class="nx">woof</span><span class="p">());</span>
<span class="p">});</span>
<span class="nx">dogBox</span><span class="p">.</span><span class="nx">tell</span><span class="p">(</span><span class="k">new</span> <span class="nx">Dog</span><span class="p">());</span> <span class="c1">// Works fine here...</span>
<span class="kd">const</span> <span class="nx">animalBox</span><span class="p">:</span> <span class="nx">ListenBox</span><span class="o"><</span><span class="nx">Animal</span><span class="o">></span> <span class="o">=</span> <span class="nx">dogBox</span><span class="p">;</span> <span class="c1">// If we assume ListenBox is covariant, this is valid.</span>
<span class="nx">animalBox</span><span class="p">.</span><span class="nx">tell</span><span class="p">(</span><span class="k">new</span> <span class="nx">Cat</span><span class="p">());</span> <span class="c1">// Oh no!</span>
</code></pre></div></div>
<p>As you can see, the answer is no. If <code class="language-plaintext highlighter-rouge">ListenBox<Dog> extends ListenBox<Animal></code> then we can assign <code class="language-plaintext highlighter-rouge">dogBox</code> to <code class="language-plaintext highlighter-rouge">animalBox</code>. But then because <code class="language-plaintext highlighter-rouge">animalBox.tell</code> accepts <code class="language-plaintext highlighter-rouge">value</code> of type <code class="language-plaintext highlighter-rouge">Animal</code>, we can pass in a <code class="language-plaintext highlighter-rouge">Cat</code>, making the <code class="language-plaintext highlighter-rouge">listener</code> be called with a <code class="language-plaintext highlighter-rouge">Cat</code>, which does <strong>not</strong> have the <code class="language-plaintext highlighter-rouge">woof</code> method.</p>
<p>So, <code class="language-plaintext highlighter-rouge">ListenBox</code> is not covariant in its type parameter. But we can salvage something different from this, where it turns out that whenever we want to use a <code class="language-plaintext highlighter-rouge">ListenBox<Dog></code>, we can use a <code class="language-plaintext highlighter-rouge">ListenBox<Animal></code> and everything works fine:</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">animalBox</span><span class="p">:</span> <span class="nx">ListenBox</span><span class="o"><</span><span class="nx">Animal</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ListenBox</span><span class="p">((</span><span class="nx">newAnimal</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">newAnimal</span><span class="p">.</span><span class="nx">isLiving</span><span class="p">());</span>
<span class="p">});</span>
<span class="kd">const</span> <span class="nx">dogBox</span><span class="p">:</span> <span class="nx">ListenBox</span><span class="o"><</span><span class="nx">Dog</span><span class="o">></span> <span class="o">=</span> <span class="nx">animalBox</span><span class="p">;</span>
<span class="nx">dogBox</span><span class="p">.</span><span class="nx">tell</span><span class="p">(</span><span class="k">new</span> <span class="nx">Dog</span><span class="p">());</span> <span class="c1">// Dog has isLiving.</span>
<span class="kd">const</span> <span class="nx">catBox</span><span class="p">:</span> <span class="nx">ListenBox</span><span class="o"><</span><span class="nx">Cat</span><span class="o">></span> <span class="o">=</span> <span class="nx">animalBox</span><span class="p">;</span>
<span class="nx">catBox</span><span class="p">.</span><span class="nx">tell</span><span class="p">(</span><span class="k">new</span> <span class="nx">Cat</span><span class="p">());</span> <span class="c1">// Cat has isLiving.</span>
</code></pre></div></div>
<p>The code above works just fine when instead of saying that <code class="language-plaintext highlighter-rouge">ListenBox</code> is covariant, we say that it is <em>contravariant</em> in its type parameter. This means that for types <code class="language-plaintext highlighter-rouge">T, U</code> where <code class="language-plaintext highlighter-rouge">T extends U</code>, we have that <code class="language-plaintext highlighter-rouge">ListenBox<U> extends ListenBox<T></code>. <code class="language-plaintext highlighter-rouge">U</code> is a supertype of <code class="language-plaintext highlighter-rouge">T</code>, the subtyping relationship is reversed, hence the “contra”.</p>
<h2 id="invariance">Invariance</h2>
<p>Now, for the kind of box that you might see the most often, one where you can read from and write to:</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Box</span><span class="o"><</span><span class="nx">T</span><span class="o">></span> <span class="p">{</span>
<span class="k">private</span> <span class="na">value</span><span class="p">:</span> <span class="nx">T</span><span class="p">;</span>
<span class="k">public</span> <span class="kd">constructor</span><span class="p">(</span><span class="na">value</span><span class="p">:</span> <span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="nx">getValue</span><span class="p">():</span> <span class="nx">T</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="nx">setValue</span><span class="p">(</span><span class="na">value</span><span class="p">:</span> <span class="nx">T</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Is this covariant? Well, this example says no:</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">dogBox</span><span class="p">:</span> <span class="nx">Box</span><span class="o"><</span><span class="nx">Dog</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Box</span><span class="p">(</span><span class="k">new</span> <span class="nx">Dog</span><span class="p">());</span>
<span class="kd">const</span> <span class="nx">animalBox</span><span class="p">:</span> <span class="nx">Box</span><span class="o"><</span><span class="nx">Animal</span><span class="o">></span> <span class="o">=</span> <span class="nx">dogBox</span><span class="p">;</span> <span class="c1">// If we assume Box is covariant, this is valid.</span>
<span class="nx">animalBox</span><span class="p">.</span><span class="nx">setValue</span><span class="p">(</span><span class="k">new</span> <span class="nx">Cat</span><span class="p">());</span>
<span class="nx">dogBox</span><span class="p">.</span><span class="nx">getValue</span><span class="p">().</span><span class="nx">woof</span><span class="p">();</span> <span class="c1">// Oh no, not again!</span>
</code></pre></div></div>
<p>Since <code class="language-plaintext highlighter-rouge">dogBox</code> and <code class="language-plaintext highlighter-rouge">animalBox</code> are aliases of the same <code class="language-plaintext highlighter-rouge">Box</code>, by assigning <code class="language-plaintext highlighter-rouge">dogBox</code> to <code class="language-plaintext highlighter-rouge">animalBox</code>, we are able to put a <code class="language-plaintext highlighter-rouge">Cat</code> in it via <code class="language-plaintext highlighter-rouge">animalBox</code>, which once we retrieve it later using <code class="language-plaintext highlighter-rouge">dogBox</code>, we think it’s a <code class="language-plaintext highlighter-rouge">Dog</code> due to the types, but it is not<sup id="fnref:cov_arrays" role="doc-noteref"><a href="#fn:cov_arrays" class="footnote" rel="footnote">3</a></sup>.</p>
<p>So what about contravariance? It turns out all you need to do is rearrange the it a bit and you run into the exact same problem:</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">animalBox</span><span class="p">:</span> <span class="nx">Box</span><span class="o"><</span><span class="nx">Animal</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Box</span><span class="p">(</span><span class="k">new</span> <span class="nx">Animal</span><span class="p">());</span>
<span class="nx">animalBox</span><span class="p">.</span><span class="nx">setValue</span><span class="p">(</span><span class="k">new</span> <span class="nx">Cat</span><span class="p">());</span>
<span class="kd">const</span> <span class="nx">dogBox</span><span class="p">:</span> <span class="nx">Box</span><span class="o"><</span><span class="nx">Dog</span><span class="o">></span> <span class="o">=</span> <span class="nx">animalBox</span><span class="p">;</span> <span class="c1">// If we assume Box is contravariant, this is valid.</span>
<span class="nx">dogBox</span><span class="p">.</span><span class="nx">getValue</span><span class="p">().</span><span class="nx">woof</span><span class="p">();</span> <span class="c1">// Oh no, not a third time!</span>
</code></pre></div></div>
<p>So our <code class="language-plaintext highlighter-rouge">Box</code> is neither covariant nor contravariant. They are invariant: no matter the relationship between two different types <code class="language-plaintext highlighter-rouge">T, U</code>, there will never be a subtyping relationship between <code class="language-plaintext highlighter-rouge">Box<T></code> and <code class="language-plaintext highlighter-rouge">Box<U></code>.</p>
<h2 id="bivariance">Bivariance</h2>
<p>So we looked at covariance, which preserves the subtyping relationship. We looked at contravariance, which reverses it. Then, we looked at invariance, which completely removes it. You might be wondering whether there is something that allows the type parameter to go both ways.</p>
<p>This is known as <em>bivariance</em> and it is very spooky: it only works if there’s never any values of that type parameter!</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">PhantomBox</span><span class="o"><</span><span class="nx">T</span><span class="o">></span> <span class="p">{</span>
<span class="c1">// That's it.</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">animalBox</span><span class="p">:</span> <span class="nx">PhantomBox</span><span class="o"><</span><span class="nx">Animal</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PhantomBox</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">dogBox</span><span class="p">:</span> <span class="nx">PhantomBox</span><span class="o"><</span><span class="nx">Dog</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PhantomBox</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">catBox</span><span class="p">:</span> <span class="nx">PhantomBox</span><span class="o"><</span><span class="nx">Cat</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PhantomBox</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">animalBox2</span><span class="p">:</span> <span class="nx">PhantomBox</span><span class="o"><</span><span class="nx">Animal</span><span class="o">></span> <span class="o">=</span> <span class="nx">dogBox</span><span class="p">;</span> <span class="c1">// Works in preserving the relationship!</span>
<span class="kd">const</span> <span class="nx">dogBox2</span><span class="p">:</span> <span class="nx">PhantomBox</span><span class="o"><</span><span class="nx">Dog</span><span class="o">></span> <span class="o">=</span> <span class="nx">animalBox</span><span class="p">;</span> <span class="c1">// Works in reversing the relationship!</span>
<span class="kd">const</span> <span class="nx">catBox2</span><span class="p">:</span> <span class="nx">PhantomBox</span><span class="o"><</span><span class="nx">Cat</span><span class="o">></span> <span class="o">=</span> <span class="nx">dogBox</span><span class="p">;</span> <span class="c1">// Works in doing absolutely crazy stuff!</span>
</code></pre></div></div>
<p>In practice, this is rarely seen in most languages<sup id="fnref:phantom" role="doc-noteref"><a href="#fn:phantom" class="footnote" rel="footnote">4</a></sup>.</p>
<h2 id="function-types">Function Types</h2>
<p>To facilitate later understanding, we also want to talk about variance in function types.</p>
<p>Let’s suppose that we have these function types<sup id="fnref:ts_fn" role="doc-noteref"><a href="#fn:ts_fn" class="footnote" rel="footnote">5</a></sup>, which corresponds to the type of the function take parameters of type <code class="language-plaintext highlighter-rouge">A1</code> to <code class="language-plaintext highlighter-rouge">An</code> and returns type <code class="language-plaintext highlighter-rouge">Ret</code>:</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">type</span> <span class="nx">Function0</span><span class="o"><</span><span class="nx">Ret</span><span class="o">></span>
<span class="kd">type</span> <span class="nx">Function1</span><span class="o"><</span><span class="nx">A1</span><span class="p">,</span> <span class="nx">Ret</span><span class="o">></span>
<span class="kd">type</span> <span class="nx">Function2</span><span class="o"><</span><span class="nx">A1</span><span class="p">,</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">Ret</span><span class="o">></span>
<span class="kd">type</span> <span class="nx">Function3</span><span class="o"><</span><span class="nx">A1</span><span class="p">,</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">A3</span><span class="p">,</span> <span class="nx">Ret</span><span class="o">></span>
<span class="c1">// And so on...</span>
</code></pre></div></div>
<p>What is the variance on these type parameters? Let’s consider that we are writing the function with type <code class="language-plaintext highlighter-rouge">Function1<Dog, Animal></code> for perhaps a callback. This means that someone else will pass us the <code class="language-plaintext highlighter-rouge">Dog</code> that we can use. But, we don’t have to use all of the <code class="language-plaintext highlighter-rouge">Dog</code>, for example, we only use <code class="language-plaintext highlighter-rouge">isLiving</code>. This means that <code class="language-plaintext highlighter-rouge">Function1<Animal, Animal></code> will also work in place of the previous type for the callback. Therefore, <code class="language-plaintext highlighter-rouge">Function1</code> is contravariant in the type of its first type parameter.</p>
<p>Now if we consider from the perspective of the person calling our callback, they are expecting to receive an <code class="language-plaintext highlighter-rouge">Animal</code>. This means that it is perfectly fine to use a subtype of <code class="language-plaintext highlighter-rouge">Animal</code>, such as <code class="language-plaintext highlighter-rouge">Dog</code>. Therefore, <code class="language-plaintext highlighter-rouge">Function1</code> is covariant in the type of its return type parameter.</p>
<p>In general, for functions, the types of the parameters are contravariant, while the type of the return is covariant. This leads to a guideline for the variance of type parameters in general: if a type has a value that is to be “produced”, it is covariant. If it is to be “consumed”, it is contravariant. If both, it is invariant; if neither, it is bivariant<sup id="fnref:position" role="doc-noteref"><a href="#fn:position" class="footnote" rel="footnote">6</a></sup>. You can see with our boxes that <code class="language-plaintext highlighter-rouge">ReadBox</code> only produced values via <code class="language-plaintext highlighter-rouge">getValue</code>, <code class="language-plaintext highlighter-rouge">ListenBox</code> only consumed values via <code class="language-plaintext highlighter-rouge">tell</code> and <code class="language-plaintext highlighter-rouge">listener</code>, and <code class="language-plaintext highlighter-rouge">Box</code> both produced and consumed values with <code class="language-plaintext highlighter-rouge">getValue</code> and <code class="language-plaintext highlighter-rouge">setValue</code>.</p>
<h2 id="variance-annotations">Variance Annotations</h2>
<p>In practice, the variance of type parameters in a class or interface that you declare is very language-dependent. Some languages always have invariant type parameters. Others allow you to specify the variance of type parameters.</p>
<p>Here’s a little summary:</p>
<ul>
<li>
<p><strong>Java</strong>: Invariant type parameters, but allows specifying variance at use-sites. This means you can actually change the variance of a type parameter depending on how you want to use it at a certain place and time. You can look up Java’s wildcards for more information.</p>
</li>
<li>
<p><strong>Kotlin</strong>, <strong>Scala</strong>, <strong>C#</strong>, <strong>OCaml</strong>: Allows specifying invariant, covariant, or contravariant type parameters. These languages will also check that these type parameters are used in such a way that they do not contradict their variance annotation.</p>
</li>
<li>
<p><strong>TypeScript</strong>: An unsound mess. Variance is not checked at all, but instead properties are checked if they are assignable to each other. This sometimes ensures variance is respected, sometimes it doesn’t.</p>
</li>
</ul>
<h1 id="variance-in-software-design">Variance in Software Design</h1>
<h2 id="overriding-methods">Overriding Methods</h2>
<p>Inheritance is what allows us to make classes (or interfaces) that extend other classes (or interfaces). When a class <code class="language-plaintext highlighter-rouge">B</code> inherits from a class <code class="language-plaintext highlighter-rouge">A</code>, languages will now consider <code class="language-plaintext highlighter-rouge">B</code> to be a subtype of <code class="language-plaintext highlighter-rouge">A</code>. Of course, once we have subtyping we have to get to variance. Specifically, how much can we override the type signatures of the methods of a class?</p>
<p>If we look at the bigger picture, we realize that a class declaration isn’t too different from assigning values to variables. The parent class tells you that these methods have certain types, and we can “re-assign” or override these methods in a subclass.</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">AnimalBox</span> <span class="p">{</span>
<span class="c1">// It's like declaring get: Function0<Animal> = ...</span>
<span class="k">public</span> <span class="kd">get</span><span class="p">():</span> <span class="nx">Animal</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span>
<span class="k">public</span> <span class="kd">set</span><span class="p">(</span><span class="nx">value</span><span class="p">:</span> <span class="nx">Animal</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">DogBox</span> <span class="p">{</span>
<span class="c1">// It's like re-assigning get = ...</span>
<span class="k">public</span> <span class="kd">get</span><span class="p">():</span> <span class="nx">Dog</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span>
<span class="k">public</span> <span class="kd">set</span><span class="p">(</span><span class="nx">value</span><span class="p">:</span> <span class="nb">Object</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And we can see indeed that our <code class="language-plaintext highlighter-rouge">DogBox</code> is using the concept of variance. In the case of <code class="language-plaintext highlighter-rouge">get</code>, it is almost as if we are assigning a <code class="language-plaintext highlighter-rouge">Function0<Dog></code> to a <code class="language-plaintext highlighter-rouge">Function0<Animal></code>, which is valid because functions are covariant in the return type. Similarly, for <code class="language-plaintext highlighter-rouge">set</code>, it is like we are assigning <code class="language-plaintext highlighter-rouge">Function1<Object, void></code> to <code class="language-plaintext highlighter-rouge">Function1<Animal, void></code>, which is valid because <code class="language-plaintext highlighter-rouge">Animal extends Object</code> and functions are contravariant in the parameter types.</p>
<p>Note that not all languages support this. For example, Java and C# both have covariant return types but invariant parameter types. TypeScript has contravariant parameter types (in strict mode) and covariant return types.</p>
<h2 id="liskovs-substitution-principle">Liskov’s Substitution Principle</h2>
<p>Liskov’s Substitution Principle, the L in SOLID: the principle says that if a property about a type <code class="language-plaintext highlighter-rouge">T</code> is true, then it must also be true for a subtype of <code class="language-plaintext highlighter-rouge">T</code><sup id="fnref:liskov" role="doc-noteref"><a href="#fn:liskov" class="footnote" rel="footnote">7</a></sup>. It is from this principle that covariance and contravariance of methods were adopted in newer OOP languages.</p>
<p>But the principle does not just apply to the types of objects that you type into the source code and is checked by the compilers. We have to consider things that are more than just the language, we have to consider things that you might instead document in a comment: preconditions, postconditions, and invariants.</p>
<h3 id="preconditions">Preconditions</h3>
<p>Preconditions are requirements that a method wants to be true about the state of the program before it runs. For example, you might ask that before calling <code class="language-plaintext highlighter-rouge">advance</code> on a parser that you are not at the end of the string. Preconditions are contravariant. They can only be weakened when overridden, not strengthened. You might for example have a parser implementation that allows for streaming parsing, in which case calls to <code class="language-plaintext highlighter-rouge">advance</code> may be called past the string in buffer currently. Strengthening a precondition would mean that someone might not satisfy all the preconditions of a method when they are programming to the superclass but receives the subclass.</p>
<p>For another example, perhaps your method takes a tuple <code class="language-plaintext highlighter-rouge">(Int, Int)</code> which must have <code class="language-plaintext highlighter-rouge">x^2 + y^2 = 1</code> (point on the circumference of the circle). A subclass can override that method and allow a weaker constraint if it wanted to, such as <code class="language-plaintext highlighter-rouge">x^2 + y^2 <= 1</code> (point on the surface of the circle). It cannot, however, allow a stronger constraint, because this would break the contravariance of method parameter types.</p>
<p>Notice that preconditions are analogous to the input of functions: contravariant.</p>
<h3 id="postconditions">Postconditions</h3>
<p>Likewise, postconditions are things about the state of the program that must be true after the method runs. They are covariant, so they can only be strengthened when overridden, not weakened. You can imagine some code that relies on the state of the program being a certain way after running a function, and so weakening a postcondition would break the program.</p>
<p>As an example, consider a method that returns a <code class="language-plaintext highlighter-rouge">String</code> value that must be a valid email address. This means that, by return types being covariant, methods which override that method must only return <code class="language-plaintext highlighter-rouge">String</code> values that are a valid email address or stronger (perhaps, a valid Gmail address).</p>
<p>Notice that postconditions are analogous to the output of functions: covariant.</p>
<h3 id="invariants">Invariants</h3>
<p>Invariants are always true statements about your program that must never be broken. You can imagine that this is, well, invariant: subclasses must never change the invariants. For example, a datetime class may have an invariant that the month is always between 1 and 12. You cannot weaken this to allow months out-of-range, and you cannot strengthen this to disallow users of the class from using certain months.</p>
<p>A special type of invariant that always apply is the idea of encapsulation. It says that if a class allows its state to be modified through a set of methods, then those are the only methods that can modify that state. This means that a subclass that introduces new behavior cannot modify itself in ways that are not allowed by the superclass i.e. it must use the methods provided by the superclass to do so. This rule is called the “history rule”. Of course, the subclass itself can define new mutable state and corresponding methods separately from the superclass.</p>
<p>Notice that invariants usually apply to things like mutable state: you can both read from and write to mutable state, so it must be invariant.</p>
<h1 id="conclusion">Conclusion</h1>
<p>Hopefully this post gives you a good understanding of what variance is in terms of OOP languages and how it applies to OOP design.</p>
<p>For those interested in theory, the concept of variance comes from category theory, where one would study covariant and contravariant functors. Those who know a bit of Haskell would also encounter them there! The concept of preconditions, postconditions, and invariants come from design-by-contract, but are also related to the more formal theory of refinement types: types which allow us to make them more specific for our domain.</p>
<hr />
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:extends" role="doc-endnote">
<p>I’m not a huge fan of that syntax, since you don’t extend interfaces, but you can still be a subtype of an interface. The better notation for this is the <code class="language-plaintext highlighter-rouge"><:</code> symbol which means “is subtype of” but that is a bit obtuse for some audiences. <a href="#fnref:extends" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:one_param" role="doc-endnote">
<p>We can say “its type parameter” because it only has one. You can imagine some type where only some parameters are covariant. <a href="#fnref:one_param" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:cov_arrays" role="doc-endnote">
<p>It turns out that quite a few languages (Java, C#, TypeScript, etc.) have covariant arrays. You can think of our <code class="language-plaintext highlighter-rouge">Box</code> as an array with just one item at all times. As you can see, covariant arrays are a very bad idea! They should be invariant. <a href="#fnref:cov_arrays" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:phantom" role="doc-endnote">
<p>It does have its uses! These phantom types (which TypeScript does not have) allow you do things such as tag a value at the type level or pass type information without passing the value of that type itself. Languages like Haskell and Rust make use of this, though they are not languages that have subtyping. <a href="#fnref:phantom" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:ts_fn" role="doc-endnote">
<p>In TypeScript, the actual syntax for function types is <code class="language-plaintext highlighter-rouge">(arg: A1) => Ret</code>, but this syntax is not in line with the syntax with angle brackets, so we won’t use it in this post. <a href="#fnref:ts_fn" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:position" role="doc-endnote">
<p>This is just a guideline. It starts to become unwieldy when you have functions that are higher-order than just callbacks. For a more rigid approach, consider function parameters to be “negative” and function returns to be “positive”, and that each time you introduce a type in a negative position, you invert all of the types inside. Then, types that are in only positive positions are covariant, in only negative positions are contravariant, in both are invariant, and in neither are bivariant. As an example, the type (in Haskell-like syntax) <code class="language-plaintext highlighter-rouge">a -> (b -> a) -> ((c -> a) -> b) -> c</code> becomes <code class="language-plaintext highlighter-rouge">-a -> (+b -> -a) -> ((-a -> +c) -> -b) -> +c</code> which means <code class="language-plaintext highlighter-rouge">a</code> is contravariant, <code class="language-plaintext highlighter-rouge">b</code> is invariant, and <code class="language-plaintext highlighter-rouge">c</code> is covariant. <a href="#fnref:position" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:liskov" role="doc-endnote">
<p>This is indeed a pretty vague definition. The <a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle">Wikipedia page</a> goes much more in depth about the formalities. <a href="#fnref:liskov" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>1ComputerVariance is a concept in type systems, especially those with subtyping. Keeping variance in mind when working with advanced type-level machinery in languages is quite helpful, but it applies a lot to simpler concepts too, such as when you are designing classes and interfaces. In this post, we will get an understanding of what variance is and how we can use it to our advantage to write correct code.Installing Haskell in Current Year2020-07-28T00:00:00+00:002020-07-28T00:00:00+00:00https://1computer1.github.io/posts/installing-haskell-in-current-year<p>After seeing first-timers fail to install GHC in various ways, I decided to write a quick guide on how to install Haskell. The point of this post won’t be on how to install the best setup or anything like that, but rather for people to choose a method that sounds easy to them so that they can get on with trying out Haskell for the first time.</p>
<p>For clarification:</p>
<ul>
<li>GHC is the Glasgow Haskell Compiler. It is <em>the</em> compiler for working with Haskell.</li>
<li><a href="https://cabal.readthedocs.io/">cabal</a> (or cabal-install) is the build tool for building your projects and managing dependencies, among other things. Not to be confused with Cabal, which is the backend library used by cabal.</li>
<li><a href="https://docs.haskellstack.org/en/stable/README/">Stack</a> is an alternative to cabal, created back when cabal used to be much worse at doing its job.</li>
</ul>
<p>As for whether you should use cabal or Stack, this is a point of contention in the Haskell community. For beginners, it does not matter which you choose, since they do the same thing in the end: building your projects. If you are interested in the differences, take a look <a href="https://gist.github.com/merijn/8152d561fb8b011f9313c48d876ceb07">here</a>.</p>
<p>I’ll also touch on the current IDE integration situation in the Haskell ecosystem, as well as linters and formatters.</p>
<h1 id="on-linuxmacos">On Linux/macOS</h1>
<p>There are multiple options here, but much of Haskell development is done on Linux, so you should have much less trouble installing GHC here.</p>
<h2 id="ghcup">ghcup</h2>
<p><a href="https://gitlab.haskell.org/haskell/ghcup-hs">ghcup</a> manages GHC and cabal installations and lets you switch between different versions. You can get it and install GHC and cabal soon after <a href="https://www.haskell.org/ghcup/#">by following the instructions here</a> (if you are browsing on Windows, click ‘display all supported installers’).</p>
<h2 id="stack">Stack</h2>
<p><a href="https://docs.haskellstack.org/en/stable/README/">Stack</a> is tool for developing Haskell projects, meant to be an alternative to cabal. It is very simple to start up, simply click on that link and get the installer.</p>
<h1 id="on-windows">On Windows</h1>
<p>As we all know, Windows is a pain for development. So, you probably should use <a href="https://docs.microsoft.com/en-us/windows/wsl/">Windows Subsystem for Linux</a> if you haven’t been using that already. Then, just go to the Linux section and maybe configure your favorite editor like <a href="https://code.visualstudio.com/docs/remote/wsl">VSCode to work in WSL</a>.</p>
<p>If you’re set on sticking to Windows though, here are some ways to get started with Haskell.</p>
<h2 id="chocolatey">Chocolatey</h2>
<p>The recommended method to install GHC and cabal on Windows at the moment is through <a href="https://chocolatey.org/">Chocolatey</a>. You can simply follow the <a href="https://www.haskell.org/platform/windows.html">instructions on the Haskell website</a>.</p>
<h2 id="ghcups">ghcups</h2>
<p><a href="https://github.com/kakkun61/ghcups">ghcups</a> manages GHC and cabal installations and lets you switch between different versions. It is similar to <a href="https://gitlab.haskell.org/haskell/ghcup-hs">ghcup</a>.</p>
<h2 id="stack-1">Stack</h2>
<p><a href="https://docs.haskellstack.org/en/stable/README/">Stack</a> is cross-platform, so you can install it on Windows too.</p>
<h1 id="memory-usage">Memory Usage</h1>
<p>A known problem with GHC is that it takes up a lot of memory when compiling. If you are working in a low-memory environment (a VPS, a Raspberry PI, an old laptop), then you’ll most likely run out of memory compiling large libraries or executables.</p>
<p>Using cabal, append <code class="language-plaintext highlighter-rouge">--ghc-options="+RTS -M<memory> -RTS"</code> to your commands. For example, <code class="language-plaintext highlighter-rouge">cabal build --ghc-options="+RTS -M600M -RTS"</code> to build a project without using more than 600 megabytes.</p>
<p>Unfortunately, I do not know of a Stack-specific way to limit its memory usage while installing. You can set the maximum number of parallel jobs with <code class="language-plaintext highlighter-rouge">-j<number></code> e.g. <code class="language-plaintext highlighter-rouge">stack build -j1</code>, but this does not limit memory usage.</p>
<p>Another thing you can do is set the <code class="language-plaintext highlighter-rouge">GHCRTS</code> environment variable:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">GHCRTS</span><span class="o">=</span><span class="s1">'-M<memory>'</span>
<span class="nb">export </span>GHCRTS
</code></pre></div></div>
<p>This will apply it to all GHC compiled programs. However, if a program was not compiled to be compatible with RTS options, you will get an error and should remove the environment variable.</p>
<h1 id="the-ide-situation">The IDE Situation</h1>
<h2 id="integration">Integration</h2>
<p>I’ll list some options you have in order of how quickly you can probably get it working:</p>
<ul>
<li>
<p><a href="https://github.com/haskell/haskell-language-server">haskell-language-server</a> is a high power tool that uses the language server protocol, so it can integrate with any editor that supports LSP. HLS uses a plugin system, which means it includes many different tools within it, such as code diagnostics, linters, and formatters. There are prebuilt binaries available for Windows, Linux, and macOS. The <a href="https://marketplace.visualstudio.com/items?itemName=haskell.haskell">VSCode extension</a> will also download the binaries automatically for you.</p>
</li>
<li>
<p><a href="https://marketplace.visualstudio.com/items?itemName=dramforever.vscode-ghc-simple">Simple GHC (Haskell) Integration</a> is a plugin for VSCode. It uses GHCi to do its thing, which means there’s no extra tools to install other than the extension, and it should work on all platforms.</p>
</li>
<li>
<p><a href="https://github.com/haskell/haskell-mode">haskell-mode</a> is an Emacs mode for Haskell. You can take a look at the link for installation instructions.</p>
</li>
<li>
<p><a href="https://github.com/ndmitchell/ghcid">ghcid</a> is a simple application that uses GHCi to show errors and warnings in your code. There are plugins for various editors.</p>
</li>
</ul>
<h2 id="linting-and-formatting">Linting and Formatting</h2>
<p>Linters:</p>
<ul>
<li><a href="https://github.com/ndmitchell/hlint">hlint</a>, which works fairly well and has plugins for various editors, such as <a href="https://marketplace.visualstudio.com/items?itemName=hoovercj.haskell-linter">VSCode</a>.</li>
<li><a href="https://github.com/kowainik/stan">Stan</a>, a more in-depth static code analyzer. It can generate pretty reports for your project.</li>
</ul>
<p>Formatters:</p>
<ul>
<li><a href="https://github.com/tweag/ormolu">ormolu</a>, a formatter using a one “true” formatting style with no configuration.</li>
<li><a href="https://github.com/jaspervdj/stylish-haskell">stylish-haskell</a>, a formatter that tries to not get in the way and focuses on tedius things like imports, whitespace, alignment, etc.</li>
</ul>
<h1 id="conclusion">Conclusion</h1>
<p>That’s pretty much it, enjoy playing around with Haskell! If you spot mistakes or misleading information, please let me know.</p>1ComputerAfter seeing first-timers fail to install GHC in various ways, I decided to write a quick guide on how to install Haskell. The point of this post won’t be on how to install the best setup or anything like that, but rather for people to choose a method that sounds easy to them so that they can get on with trying out Haskell for the first time.