<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Infrequent, Pragmatic, Lambda Blog</title>
    <link href="https://rpeszek.github.io//atom.xml" rel="self" />
    <link href="https://rpeszek.github.io/" />
    <id>https://rpeszek.github.io//atom.xml</id>
    <author>
        <name>Robert Peszek</name>
        <email>rpeszek.io@gmail.com</email>
    </author>
    <updated>2023-05-15T00:00:00Z</updated>
    <entry>
    <title>Refactoring error messages in Haskell programs. Who cares about K9 composers?</title>
    <link href="https://rpeszek.github.io//posts/2023-05-15-FP-refactor-errors-p0.html" />
    <id>https://rpeszek.github.io//posts/2023-05-15-FP-refactor-errors-p0.html</id>
    <published>2023-05-15T00:00:00Z</published>
    <updated>2023-05-15T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on May 15, 2023
        
        
        
        
        <div class="info">Tags: <a title="All pages tagged 'patterns-of-erroneous-code'." href="../tags/patterns-of-erroneous-code.html">patterns-of-erroneous-code</a>, <a title="All pages tagged 'Haskell'." href="../tags/Haskell.html">Haskell</a>, <a title="All pages tagged 'error-messages'." href="../tags/error-messages.html">error-messages</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <div class="toc"><div class="header">Table of Contents</div>
<ul>
<li><a href="#historical-notes">Historical notes</a></li>
<li><a href="#criticism-outlined">Criticism outlined</a></li>
<li><a href="#monadplus-error-laws">MonadPlus error laws</a></li>
<li><a href="#alternative-dog-music.-a-use-of-is-considered-harmful">Alternative dog music. A use of <code>&lt;|&gt;</code> is considered harmful</a></li>
<li><a href="#overloaded-errors">Overloaded errors</a></li>
<li><a href="#topics-to-discuss">Topics to discuss</a></li>
</ul>
</div>
<blockquote>
<p><em>Great programming is mathematics. … Except, all falsehoods are the same and error messages are not. Otherwise, great programming is mathematics.</em><br />
    A quote that I wish someone whom I could quote had said</p>
</blockquote>
<p>In this post I will talk about improving error messages. We will also discuss dogs which compose music.</p>
<p>I wrote about error messages in Haskell before, I decided to give it one more go. I am working on a slowly progressing task: rewriting code to improve the quality of error messages across projects I contribute to (… and log messages too, but I will focus on the error outputs here). I try to dedicate a few hours every sprint to it. This work often includes rethinking parts of <em>aeson</em> or <em>Parsec</em> code that use <code>MonadPlus</code> / <code>Alternative</code> when the resulting error message is likely to throw anyone for a loop, or re-implementing code that uses <code>Maybe</code> where something like <code>Either</code> would be a better choice, or where errors were never caught… This work also involves adding a decent amount of context to the messages. I have been trying to fix up the errors for several years now and I am starting to believe that this work may never end. You roll this rock uphill and it rolls back down. Can Functional Programming create quality error outputs? Of course it can! But, for this to happen on the level of projects … I think that the community needs to talk about it more.</p>
<p>The (low) quality of error messages I witness in functional code is something that has puzzled me for a very long time. I wrote about <a href="2021-01-17-maybe-overuse.html" target="_blank">Maybe Overuse</a> and <a href="2021-02-13-alternative.html" target="_blank">Alternative Overuse</a> in the past. The first received a very mixed response (including very positive and very negative), the response to the second was flat negative. I decided that the reasons for what I am observing are probably mostly not technical. This (at least partially) motivated me to look into cognitive psychology (<a href="2022-08-30-code-cognitiveload.html" target="_blank">Cognitive Loads in Programming</a>), and I came up with “a theory” about <a href="2022-11-07-empirical-programming.html" target="_blank">Theorists vs Pragmatists</a>. I cannot claim that I understand what is happening, I can only claim spending literally years thinking about it.</p>
<p>I want to try one more time to talk about my experience with errors and troubleshooting with some examples and thoughts. My current plan is that with this series (or with this post) I will end my blogging.</p>
<p>This post is also about conveniences. It shows a few examples where established parts of the Haskell ecosystem make it easy to be careless about errors or where providing decent error messages is simply hard.</p>
<p>I will mostly focus on <em>aeson</em> (the premier Haskell package for working with JSON) with short mentions outside of this library. This is because code that uses <em>aeson</em> has been my more recent refactoring effort and is fresh on my mind.</p>
<h2 id="historical-notes">Historical notes</h2>
<p>These are my (Haskell user) observations about the history of error messages in the Haskell ecosystem. If you have been using Haskell for a long time, you probably remember that <em>aeson</em> did not have <code>eitherDecode</code> at the beginning. <code>eitherDecode</code> was added in <code>0.6.1.0</code> (about two years after the initial release). What it did have (and unfortunately still does) is a more nicely named</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="ot">decode ::</span> <span class="dt">FromJSON</span> a <span class="ot">=&gt;</span> <span class="dt">ByteString</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> a</span></code></pre></div>
<p>If I did my hackage archaeology correctly, an ability to output error messages was added in <code>0.2.0.0</code> with the introduction of <code>parse :: (a -&gt; Parser b) -&gt; a -&gt; Result b</code> which has been hiding in <a href="https://hackage.haskell.org/package/aeson-0.2.0.0/docs/Data-Aeson-Types.html" target="_blank"><code>Data.Aeson.Types</code></a>. The commonly imported <code>Data.Aeson</code> module did not have an error message producing combinator until <code>0.6.1.0</code>.</p>
<p>If you look over the documentation of the older versions of <em>aeson</em> you will see the following code as the suggested implementation for <a href="https://hackage.haskell.org/package/aeson-0.6.0.0/docs/Data-Aeson.html#t:FromJSON" target="_blank"><code>FromJSON</code></a>:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="co">-- A non-Object value is of the wrong type, so use mzero to fail.</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>   parseJSON _          <span class="ot">=</span> mzero</span></code></pre></div>
<p>I am still finding (and fixing) similar code despite a past effort to eradicate these. It is not easy to troubleshoot a bug if the message handed to you says only “mzero”.</p>
<p>With respect to error messages, <em>aeson</em> clearly went a long way since the old days. If you look at aeson’s Haddock <a href="https://hackage.haskell.org/package/aeson-2.1.2.1/docs/Data-Aeson.html#t:FromJSON" target="_blank">today</a> you will find the use of <code>mzero</code> discouraged!:</p>
<blockquote>
<p>"The basic ways to signal a failed conversion are as follows:</p>
<ul>
<li><code>fail</code> yields a custom error message: it is the recommended way of reporting a failure;</li>
<li><code>empty</code> (or <code>mzero</code>) is uninformative: use it when the error is meant to be caught by some <code>(&lt;|&gt;)</code>;</li>
<li><code>typeMismatch</code> can be used to report a failure when the encountered value is not of the expected JSON type; unexpected is an appropriate alternative when more than one type may be expected, or to keep the expected type implicit.</li>
</ul>
<p><code>prependFailure</code> (or <code>modifyFailure</code>) add more information to a parser’s error messages."</p>
</blockquote>
<p>However, I still find the recommended use of <code>&lt;|&gt;</code> for working with errors an odd design choice. I will explain shortly why.</p>
<p>There are other libraries where an ability to get or provide crucial error information has been added only recently (e.g <em>servant-multipart</em>). At the same time, there are many examples where <code>Maybe</code> has been overused in the past and still is. My <a href="2021-01-17-maybe-overuse.html" target="_blank">Maybe overuse</a> post has a few examples like these<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</p>
<h2 id="criticism-outlined">Criticism outlined</h2>
<p><strong><code>Maybe</code> criticism:</strong> Legacy <code>Maybe</code> combinators should be causing some concern. In programming, legacy is inertia. <code>Maybe</code> is not the correct type to represent something like a parsing failure, it can be useful to describe missing data but not for situations where <em>we care</em> about what went wrong (like parsing errors). <em>A decoding function that returns <code>Maybe</code> should be marked deprecated and eventually removed.</em> Functions like these are found in many libraries, not just <em>aeson</em>, and this is not just about parsing. One can even see it as a pattern across the whole Haskell ecosystem.</p>
<p>Anyone in a desperate need of dropping the error information can do that with an easy to create natural transformation like:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="ot">errInfoDon'tCare ::</span> <span class="dt">Either</span> e a <span class="ot">-&gt;</span> <span class="dt">Maybe</span> a</span></code></pre></div>
<p>I am not trying to be sarcastic, IMO <em>“who cares?”</em> is a fair question to ask. It would be loud enough and useful in PR reviews.</p>
<p><strong>Hyrum’s Law and friends:</strong> If you believe there is some truth to the <a href="https://www.hyrumslaw.com/" target="_blank">Hyrum’s Law</a> (the law which states that all, even the unintended ways to use a library will be exploited by its users) you will probably agree with my stance on this. I like to think about Hyrum’s Law using words that end with “use”: use, overuse, misuse, and abuse. Programming concepts often end up being misused and abused, it is enough for a library like <em>aeson</em> to provide an opening.</p>
<p>I do believe in (or rather, have been observing) something similar to Hyrum’s Law, namely:</p>
<blockquote>
<p>  <em>Developers are likely to choose convenience over correctness</em></p>
</blockquote>
<p>I call it <em>The Law of Convenience</em> and note that <code>Maybe</code> is much more convenient to use then <code>Either</code>.</p>
<p>I also believe that writing code has a significant habitual factor. Ignoring error messages is a concerning habit to have.</p>
<p>And, finally, I believe that major libraries lead the ecosystem by example.</p>
<p>Haskell is converting from a research language to a language that is used commercially and topics like efficient ability to troubleshoot production issues are becoming important. The changes I am observing are good, I only hope that the community will get more aggressive on this front.<br />
In particular, it would be nice to see more error types that are semantically richer than <code>String</code>. We do not want <code>String</code> to become the type of choice for errors and I am happy to see when this is not the case (Megaparsec, yaml, amqp, …). I would also love to see more of standard type level consideration for errors (e.g. standard typeclasses for working with them that go beyond the <code>Exception</code> typeclass)<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>.</p>
<h2 id="monadplus-error-laws">MonadPlus error laws</h2>
<p>I am fixing a lot of code that uses <code>Alternative</code> / <code>MonadPlus</code> abstractions. The next section will show a code that produces wrong error messages by misusing these abstractions. In this section I will discuss <code>MonadPlus</code> in more general terms.</p>
<p><code>MonadPlus</code> is a very convenient and easy to use <code>Monoid</code> like abstraction. It comes with <code>mzero</code> which is often used to represent a failure without any error information. It is supposed to be a principled abstraction that needs to follow certain monoid-like laws (see <a href="https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Monad.html#t:MonadPlus" target="_blank"><code>MonadPlus</code></a>, <a href="https://wiki.haskell.org/Typeclassopedia#Failure_and_choice:_Alternative.2C_MonadPlus.2C_ArrowPlus" target="_blank">Laws</a>). Does this abstraction play well with computations that also can emit nontrivial errors?</p>
<p>To dig this rabbit hole a little deeper, let’s try to test the second law for <em>mzero</em> (<code>v &gt;&gt; mzero = mzero</code>) polymorphically by adding <code>MonadFail</code> constraint:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="ot">tst ::</span> (<span class="dt">MonadFail</span> m, <span class="dt">MonadPlus</span> m) <span class="ot">=&gt;</span> m b</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a>tst <span class="ot">=</span> <span class="fu">fail</span> <span class="st">&quot;not mzero&quot;</span> <span class="op">&gt;&gt;</span> mzero</span></code></pre></div>
<p>Now I can try it with different monads to see if its error output is the same as <code>mzero</code>’s. E.g.:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="ot">{-# LANGUAGE TypeApplications #-}</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Text.Megaparsec</span> <span class="kw">as</span> <span class="dt">MP</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Void</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a><span class="co">-- |</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a><span class="co">-- &gt;&gt;&gt; verifyMP</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a><span class="co">-- False</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a><span class="ot">verifyMP ::</span> <span class="dt">Bool</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a>verifyMP <span class="ot">=</span> runTest tst <span class="op">==</span> runTest mzero </span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a>  <span class="kw">where</span> </span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true"></a>    runTest p <span class="ot">=</span> MP.parse <span class="op">@</span><span class="dt">Void</span> <span class="op">@</span><span class="dt">String</span> <span class="op">@</span><span class="dt">Int</span> p <span class="st">&quot;test&quot;</span> <span class="st">&quot;&quot;</span></span></code></pre></div>
<p> <div class="side-note">Side note: <a href="https://hackage.haskell.org/package/megaparsec-9.3.0/docs/Text-Megaparsec.html#t:ParsecT" target="_blank">Megaparsec haddock</a> on the <code>MonadPlus</code> instance of <code>ParsecT</code> states:</p>
<blockquote>
<p><em>“strictly speaking, this instance is unlawful. The right identity law does not hold, e.g. in general this is not true: <code>v &gt;&gt; mzero = mero</code>. However the following holds: <code>try v &gt;&gt; mzero = mzero</code>”</em></p>
</blockquote>
<p>Obviously, there is no magic here, backtracking or not the error message from <code>try v &gt;&gt; mzero</code> may be different than <code>mzero</code>, making a simple change to the above test verifies this as well.  </div></p>
<p>Examples that fail <em>“<code>tst</code> output is the same as <code>mzero</code> output”</em> tests:</p>
<ul>
<li><code>IO</code></li>
<li><code>Parser</code> from <code>Data.Aeson.Types</code></li>
<li><code>Parec</code> from say <code>Text.Megaparsec</code></li>
<li><code>Parser</code> from <em>attoparsec</em></li>
</ul>
<p>Examples that pass such test:</p>
<ul>
<li><code>Maybe</code></li>
<li><code>ReadP</code> and <code>ReadPrec</code> from <code>Text.ParserCombinators</code></li>
</ul>
<p><code>Maybe</code> has no error information, <code>Text.ParserCombinators</code> implement <code>mzero</code> as a <a href="https://hackage.haskell.org/package/base-4.18.0.0/docs/src/Text.ParserCombinators.ReadP.html#P" target="_blank">no-message failure</a>.</p>
<p><strong>Question:</strong> Can we find an example where a monadic computation allows for nontrivial error messages and passes this test?</p>
<p><strong>Answer:</strong> For a failing computation <code>v</code>, we would expect<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> <code>v &gt;&gt; anything = v</code>. This, combined with the second mzero law (<code>v &gt;&gt; mzero = mzero</code>) implies that any failing computation is equivalent to <code>mzero</code>. So, we either need to think about the second mzero law “modulo errors” or we have to accept that any lawful <code>MonadPlus</code> computation will suppress error information.</p>
<p>I believe developers are divided into these 2 camps: those that think about and implement laws, and those who do not, but are nevertheless surprised when computations behave in unlawful ways. We consciously or subconsciously assume various computational properties when we reason about the computations. Partially lawful is concerning. If you care about error output, “lawful modulo errors” should be concerning, having such limitation undocumented is concerning too.</p>
<p>Principled computations give us abstractions to work with, like theorems are tools to a mathematician. We do not need to think about the details, just apply them to create new code. When we do that with <code>MonadPlus</code> error messages can fall through the cracks. I am dealing with quite a bit of code that has fallen into this trap. Next section will show one such example.</p>
<p><strong><em>Who cares</em></strong> about errors?: I hope we will come up with principled abstractions that are error message friendly. I am looking forward to a day where <em>aeson</em> will stop recommending the use of <code>&lt;|&gt;</code> as an error message signaling abstraction.</p>
<p> <div class="side-note">On a more positive note: A somewhat more principled (and interesting) approach would seem to be to require monoidal structure on the underlying error messages. We would require that <code>mzero</code> results in <code>mempty</code> error, and <code>f1 &lt;|&gt; f2</code> results in <code>e1 &lt;&gt; e2</code> if both fail. Alternative can be viewed as a “higher order monoid”, it only makes sense that its errors should be a Monoid as well. Note <code>(Monad m, Monoid e) =&gt; MonadPlus (ExceptT e m)</code>. However, _appending error messages tends to produce not very user friendly results.  </div></p>
<p> <div class="side-note">Side note: (Mostly for grins.) Polymorphic implementation of the proposed <code>errInfoDon'tCare</code> combinator.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="ot">errInfoDon'tCare ::</span> <span class="dt">Alternative</span> f <span class="ot">=&gt;</span> <span class="dt">Either</span> e a <span class="ot">-&gt;</span> f a</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>errInfoDon'tCare <span class="ot">=</span> <span class="fu">either</span> (<span class="fu">const</span> empty) <span class="fu">pure</span></span></code></pre></div>
<p>It dumps any error information you might have.  </div></p>
<h2 id="alternative-dog-music.-a-use-of-is-considered-harmful">Alternative dog music. A use of <code>&lt;|&gt;</code> is considered harmful</h2>
<p>Let’s sketch a contrived code to illustrate a use of <code>&lt;|&gt;</code>:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="kw">data</span> <span class="dt">Pet</span> <span class="ot">=</span> <span class="dt">MkPet</span> {</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="ot">  breed ::</span> <span class="dt">Breed</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a>  ,<span class="ot"> species ::</span> <span class="dt">Species</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a>  ,<span class="ot"> petname ::</span> <span class="dt">Text</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a>}</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a><span class="kw">data</span> <span class="dt">Composer</span> <span class="ot">=</span> <span class="dt">MkComposer</span> {</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a><span class="ot">    genre ::</span> <span class="dt">Genre</span> </span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a>    ,<span class="ot"> composername ::</span> <span class="dt">Text</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a>}</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a><span class="kw">data</span> <span class="dt">Favorite</span> <span class="ot">=</span> </span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a>  <span class="dt">MkFavoritePet</span> <span class="dt">Pet</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a>  <span class="op">...</span> <span class="co">-- there are other favorite things </span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true"></a>  <span class="op">|</span> <span class="dt">MkFavoriteComposer</span> <span class="dt">Composer</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true"></a></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true"></a><span class="co">-- Constituent types (Pet, Composer) intances are not shown</span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true"></a><span class="co">-- Assume these types have unique (different) JSON representations</span></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true"></a></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true"></a><span class="kw">instance</span> <span class="dt">FromJSON</span> <span class="dt">Favorite</span> <span class="kw">where</span> </span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true"></a>  parseJSON v <span class="ot">=</span> </span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true"></a>     <span class="dt">MkFavoritePet</span> <span class="op">&lt;$&gt;</span> (A.parseJSON v) </span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true"></a>     <span class="op">&lt;|&gt;</span> <span class="op">...</span> <span class="co">-- parse other things</span></span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true"></a>     <span class="op">&lt;|&gt;</span> <span class="dt">MkFavoriteComposer</span> <span class="op">&lt;$&gt;</span> (A.parseJSON v)   </span></code></pre></div>
<p>Note the above <em>Law of Convenience</em> applies here: this code reuses existing JSON parsers to create the parser for <code>Favorite</code>, and this parser is very easy to implement. This also looks elegant, and seems to principally fit the <code>Alternative</code> very well. There is no special JSON representation of <code>Favorite</code>, rather we use JSON representations of the constituent types <code>Pet</code>, <code>Composer</code>, etc. This approach does not fuss with data constructor tags eliminating some JSON size overhead and looks ideal for structurally typed callers (e.g. TypeScript). But, this approach has issues.</p>
<p>Assume this has some frontend UI. Assume that a user enters information about her favorite four legged friend and that does not parse for some reason (e.g. frontend JSON encoding of <code>Pet</code> is incorrect). The error message from the parser will say something like</p>
<pre><code>&quot;Composer needs a genre&quot;</code></pre>
<p>(or whatever error <code>FromJSON</code> for <code>Composer</code> returns if it is given an unexpected JSON object).</p>
<p>We see a couple of problems: the message is misleading and it lacks context (there is nothing in this message to indicate that it came from the JSON parser for <code>Favorite</code>). I will focus on it being misleading because, believe me, this coding pattern can produce very confusing errors in real life. Code like this is something I am slowly working to fix in projects I contribute to. Fixing such code is often not easy.</p>
<p> <div class="side-note">Common solution to the above parsing issue is to add a tag to JSON data to disambiguate the data constructor. In fact, this is what happens if you use default generic JSON instances. You no longer use <code>&lt;|&gt;</code> if you know which constructor is being parsed.<br />
However, fixing such code gets more tricky if you have to consider backward compatibility, or when parsing into an extensibly defined (e.g. using something like <em>vinyl</em>) coproduct type (basically when adding tags to JSON representation is harder). In worst cases returning error messages from all alternatives may need to be considered (not a user frienly option but better than lying).</p>
<p><strong>Exercise:</strong> Try to implement JSON boilerplate for <a href="https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Functor-Sum.html" target="_blank">Data.Functor.Sum</a> that would be friendly for non-Haskellers and provide clear error messages (“InL” and “InR” tags would not be very friendly). (I do not have a good solution.)</p>
<p>Adding tags to JSON representation of constituent types can also be considered<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>.  </div></p>
<p> <div class="side-note">The alternative game is somewhat different when using Parsec or Megaparsec (the differences are related to backtracking and input consumption). Interestingly, the <code>a &lt;|&gt; b</code> phenomenology we are discussing is very similar to Parsec’s <code>try a &lt;|&gt; b</code>.<br />
This post does a great job explaining the complexity: <a href="http://blog.ezyang.com/2014/05/parsec-try-a-or-b-considered-harmful/" target="_blank">Parsec: “try a &lt;|&gt; b” considered harmful</a>. Fixing <code>try a &lt;|&gt; b</code> anti-pattern can be not trivial.  </div></p>
<p> <div class="side-note">Side note: The scenario I described in this example is what I call: <em>Unexpected input test</em>. Such tests can pin-point problems with error message response to a programming bug. Some readers will argue that improving error messages caused by development time issues is an overkill. However, this should be a case by case decision, e.g. such tests can be very relevant when programmers are the users (implementing a new programming language, a low-code infrastructure, etc) or when bugs are observed frequently.  </div></p>
<p> <div class="side-note">Side note: Another danger of the described approach: consider constituent (<code>Pet</code>, <code>Composer</code>) type JSON specs that do not tag type information and have partially overlapping data definitions (e.g. think about not overlapping fields being nullable).  </div></p>
<p>Are developers aware of this <code>&lt;|&gt;</code> issue? Probably some are and some are not. Code like this is probably written because JSON parser errors are unlikely to be viewed by the end user, <em>aeson</em> makes code like this easy to implement, the code looks elegant, and error messages are the last thing on people’s minds.</p>
<p>Which leads to another question:<br />
Q: How would we guard against issues like this? A common practice for avoiding program issues is writing tests. How do I write a non-brittle test that checks the quality of <em>aeson</em> error messages? Do I write message parsers?</p>
<p>Let’s forget about <code>&lt;|&gt;</code> for a moment and try to formalize what a parser error message is: Consider the input document specification as a collection of sets of detailed specs <em>S<sub>T</sub></em>, one for each parsed type <em>T</em> (e.g. “Composer has a not-nullable ‘genre’ field of type Genere” is an element of <em>S<sub>Composer</sub></em>) . An error message pin-points an<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a> element in one of these sets marking it as failed (e.g. “Composer needs a genre”).</p>
<p>To return a user-friendly error message, the parser needs to choose <em>S<sub>T</sub></em> wisely by matching the data the user is working on. Parser needs to have access to enough information about this context to compute which <em>S<sub>T</sub></em> to use (data constructor tags is an example of how such context is provided to the parser). Thus,</p>
<blockquote>
<p>  <em>thinking about user friendly error messages needs to be a part of software design and input specification</em>.</p>
</blockquote>
<p>The point I have been trying to make is that using <code>Alternative</code> / <code>MonadPlus</code> in computations where error information is important (like parsing) can be very tricky. It requires thinking about and testing error outputs, not something developers typically do.</p>
<blockquote>
<p>  <em>Hmm, I think Snuffy’s genre would be hard rock. But what if the dog’s name is Beethoven?</em></p>
</blockquote>
<h2 id="overloaded-errors">Overloaded errors</h2>
<p>This section will be more subtle. <em>Programs sometimes need to be selective about which error condition is handled</em>.</p>
<p>We will try to write a program that checks if the local config file <em>“.my.yaml”</em> exists and if not, uses <em>“~/.my.yaml”</em>, and returns an error if there is an issue with any of the files.</p>
<p> <div class="side-note">Side note: Making issues “loud” helps in troubleshooting. Returning an error if there is a problem with the local file instead of alternating to a backup file is one way to be loud about configuration problems.  </div></p>
<p>We will use <code>MonadPlus</code> instance of <code>IO</code>. Here is standard library <a href="https://hackage.haskell.org/package/base-4.18.0.0/docs/src/GHC.IO.html#mplusIO" target="_blank">implementation</a> of <code>mplus</code> or <code>&lt;|&gt;</code> for the <code>IO</code> Monad:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="ot">mplusIO ::</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a>mplusIO m n <span class="ot">=</span> m <span class="ot">`catchException`</span> \ (<span class="ot">_ ::</span> <span class="dt">IOError</span>) <span class="ot">-&gt;</span> n</span></code></pre></div>
<p> <div class="side-note">Side note: this code silences the first error which could be not ideal if you care about what went wrong. It becomes the responsibility of the caller do deal with this (e.g. at least log the error in the computation passed to <code>mplus</code>).<br />
Also, this <code>MonadPlus</code> instance is unlawful: <code>launchMissiles &gt;&gt; mzero</code> is not <code>mzero</code>.  </div></p>
<p>Let’s take a journey trying to do implement this and see some nuances and how complex using <code>IO</code> with <code>&lt;|&gt;</code> can be:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Yaml</span> <span class="kw">as</span> <span class="dt">Y</span> <span class="co">-- yaml package dep</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Control.Applicative</span> ((&lt;|&gt;))</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.FilePath</span> ((&lt;/&gt;))  <span class="co">-- filepath package dep</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.ByteString</span> <span class="kw">as</span> <span class="dt">BS</span>    <span class="co">-- bytestring package dep</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Control.Exception</span> ( throwIO )</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a><span class="co">-- MyConfig and its instances not shown, home directory is passed as argument for simplicity</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a><span class="co">-- will not alternate to home directory file no matter what the issue with the local file is</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a><span class="co">-- because Y.decodeFileThrow is not throwing IOError, it throws Y.ParseException  </span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a><span class="ot">won'tWork ::</span> <span class="dt">FilePath</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">MyConfig</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a>won'tWork homedir <span class="ot">=</span> </span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a>    Y.decodeFileThrow <span class="st">&quot;.my.yaml&quot;</span> </span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a>    <span class="op">&lt;|&gt;</span> Y.decodeFileThrow (homedir <span class="op">&lt;/&gt;</span> <span class="st">&quot;.my.yaml&quot;</span>)</span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a><span class="co">-- Y.decodeFileEither :: FromJSON a =&gt; FilePath -&gt; IO (Either ParseException a) </span></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a><span class="co">-- uses ParseException to also signal readFile issues like missing file</span></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true"></a><span class="co">-- this puts all problems in one bucket and alternates to home directory on any issue with the local file</span></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true"></a><span class="ot">conflateAllIssues ::</span> <span class="dt">FilePath</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">MyConfig</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true"></a>conflateAllIssues homedir <span class="ot">=</span> decode <span class="st">&quot;.my.yaml&quot;</span></span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true"></a>    <span class="op">&lt;|&gt;</span> decode (homedir <span class="op">&lt;/&gt;</span> <span class="st">&quot;.my.yaml&quot;</span>)</span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true"></a>  <span class="kw">where</span> </span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true"></a><span class="ot">    decode ::</span> <span class="dt">FilePath</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">MyConfig</span></span>
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true"></a>    decode file <span class="ot">=</span> Y.decodeFileEither file <span class="op">&gt;&gt;=</span> <span class="fu">either</span> (<span class="fu">ioError</span> <span class="op">.</span> parseErrToIOError) <span class="fu">pure</span>  </span>
<span id="cb10-25"><a href="#cb10-25" aria-hidden="true"></a><span class="ot">    parseErrToIOError ::</span> <span class="dt">Y.ParseException</span> <span class="ot">-&gt;</span> <span class="dt">IOError</span></span>
<span id="cb10-26"><a href="#cb10-26" aria-hidden="true"></a>    parseErrToIOError <span class="ot">=</span> <span class="fu">userError</span> <span class="op">.</span> <span class="fu">show</span> <span class="co">-- for illustration only</span></span>
<span id="cb10-27"><a href="#cb10-27" aria-hidden="true"></a></span>
<span id="cb10-28"><a href="#cb10-28" aria-hidden="true"></a></span>
<span id="cb10-29"><a href="#cb10-29" aria-hidden="true"></a><span class="co">-- still not ideal, it conflates any IOError issued from BS.readFile and alternates on any of them</span></span>
<span id="cb10-30"><a href="#cb10-30" aria-hidden="true"></a><span class="co">-- however invalid syntax in local file will now cause an error</span></span>
<span id="cb10-31"><a href="#cb10-31" aria-hidden="true"></a><span class="ot">isolateIOErrors ::</span> <span class="dt">FilePath</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">MyConfig</span></span>
<span id="cb10-32"><a href="#cb10-32" aria-hidden="true"></a>isolateIOErrors homedir <span class="ot">=</span> </span>
<span id="cb10-33"><a href="#cb10-33" aria-hidden="true"></a>    decodeFileIsolateIOErrors <span class="st">&quot;.my.yaml&quot;</span> </span>
<span id="cb10-34"><a href="#cb10-34" aria-hidden="true"></a>    <span class="op">&lt;|&gt;</span> decodeFileIsolateIOErrors (homedir <span class="op">&lt;/&gt;</span> <span class="st">&quot;.my.yaml&quot;</span>)</span>
<span id="cb10-35"><a href="#cb10-35" aria-hidden="true"></a></span>
<span id="cb10-36"><a href="#cb10-36" aria-hidden="true"></a><span class="co">-- override what yaml package provides</span></span>
<span id="cb10-37"><a href="#cb10-37" aria-hidden="true"></a><span class="ot">decodeFileIsolateIOErrors ::</span> <span class="dt">FilePath</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">MyConfig</span></span>
<span id="cb10-38"><a href="#cb10-38" aria-hidden="true"></a>decodeFileIsolateIOErrors file <span class="ot">=</span> <span class="kw">do</span> </span>
<span id="cb10-39"><a href="#cb10-39" aria-hidden="true"></a>    bytes <span class="ot">&lt;-</span> BS.readFile file      <span class="co">-- possible IOError</span></span>
<span id="cb10-40"><a href="#cb10-40" aria-hidden="true"></a>    <span class="fu">either</span> throwIO <span class="fu">pure</span> <span class="op">$</span> Y.decodeEither' bytes <span class="co">-- not IOError</span></span></code></pre></div>
<p>If you dislike this code, then I am with you. This example’s goal is to illustrate a thought process that goes into handling errors, so let’s focus on that process only.</p>
<p><code>conflateAllIssues</code> example conflates (and silences) all of these things:</p>
<ul>
<li>local file is missing</li>
<li>invalid yaml syntax in the local file</li>
<li>local file yaml has valid syntactically but does not represent <code>MyConfig</code></li>
<li>other IO issues related to the local file, e.g. file access problems, file corruption …</li>
</ul>
<p>The requirement is to alternate to the home directory file only when the local file is missing and output an error message otherwise. <code>isolateIOErrors</code> moves in this direction, but is still not right (it will alternate if there is anything wrong with <code>readFile</code>). Obviously there are ways to move forward, e.g. explore <code>Y.ParseException</code> constructors (there is more than one!) and make decisions whether to convert to <code>IOError</code> to alternate or not, or explore the content of the <code>IOError</code> returned from <code>readFile</code> and flip some of it outside of <code>IOError</code>.</p>
<p>I hope this shows that things can get complex.</p>
<p>My hidden goal behind this exercise was to have us notice something that applies to a wider range of <code>MonadPlus</code> / <code>Alternative</code> instances. In particular, it is related to the previous example. The impression I probably left on you in the previous section was: a naive use of <em>Alternative</em> results in bad error messages.</p>
<p>I look at the “Who cares about K9 composers” as a deeper issue of 2 conflated errors. The code in the previous section conflates errors from parsing JSON data representing one of the possible constituent types (parsing wrong branch), with errors from parsing JSON data that does not represent any of the constituent types (parsing unexpected data). This code cannot distinguish between these errors and alternates on both. Ideally we would only alternate on the first but there is no obvious way to do that (<em>aeson</em> errors are <code>String</code>s).</p>
<p>Overloaded errors is a concern when programming parsers using <code>MonadPlus</code> instances. This is subtle and, probably, I have not explained it clearly enough. Please give it some thought before dismissing it.</p>
<h2 id="topics-to-discuss">Topics to discuss</h2>
<p>In this post I wrote about things that irk me at the present moment. I think that the overall situation with error messages is getting better and better, but IMO we are far from being where we should be. Haskell does not have expressive stack traces or convenient debuggers. One would assume the community will try to compensate with clear error messages and great log outputs to make up for these limitations. I believe this topic needs more attention.</p>
<p>Here is a broader list of engineering topics that are IMO worth discussing:</p>
<ul>
<li>Overuse of <code>String</code> / <code>Text</code> as the error type.<br />
</li>
<li>Programming approach where <code>Either</code> Monad / <code>MonadError</code>-like computations augment error outputs with additional context at every opportunity. Strategies for compounding error information.</li>
<li>More about code that incorrectly uses wide ranging instead of specific errors and how abstractions fit into this.</li>
<li>I dislike the non-termination <code>throw</code> <code>catch</code> games. Throwing errors effectively bypasses the type checker. If you think of types as propositions and programs as proofs, you can prove any nonsense by throwing an error. IMO, explicit <code>Either</code> type (or its close friends <code>ExceptT</code>/<code>MonadError</code>) are a better way to write code. To me, throwing errors is not FP (think about Idris or even Rust for alternative ideas). IMO, the same goes for effect systems: I prefer no <code>throw</code> <code>catch</code> games. I would like to see the use of <code>error :: String -&gt; a</code>, or even things like <code>IOError</code> eradicated from the ecosystem, (e.g.  <code>readFile :: FilePath -&gt; IO (Either IOFileErr ByteString)</code>). (I unloaded a lot from my chest here 🙂)</li>
<li>Type level consideration for errors.</li>
<li>Strategies for dealing with non termination caused by use of <code>error :: String -&gt; a</code> (a pure function, I call it 😉 “pure evil”).</li>
<li>More about <code>Maybe</code>, <code>MonadPlus</code>, <code>Alternative</code> when they are, in addition to being very convenient, completely OK to use.</li>
<li>More about <code>MonadPlus</code>, <code>Alternative</code> when their use is concerning (e.g. are you using <code>guard :: Alternative f =&gt; Bool -&gt; f ()</code> in parsers? If so, how?).</li>
<li>Strategies for refactoring code overusing <code>Alternative</code> in parsers. Writing parsers without using <code>&lt;|&gt;</code>.</li>
<li>Monadic vs Applicative parsers comparison from the error messages standpoint.</li>
<li>Strategies for input spec designs (e.g. for JSON representations, tagging constructors vs tagging types).</li>
</ul>
<p>It would be nice to know if I am alone in my views and if these topics are of interest for anyone out there. If not, I will probably make this my last blog post, blogging is costing a little bit too much energy. If yes, I will select one of these topics and try to write more over the summer.</p>
<p>Was this post negative? IMO, there is a difference between negativity and frustration. Frustration can result in something positive, negativity cannot. Frustration seeks understanding, negativity does not. Frustration can unite, negativity can only divide.</p>
<p>If you agree with some of the things I wrote here, please try to focus on these and let me know! Thank you for reading and for your feedback.</p>
<p>I am a concerned Haskeller who loves and adores this language.</p>
<p>To all my readers: thank you for reading my posts and for your constructive comments and for your encouragement.</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>Of course, <em>aeson</em> historical record cannot be generalized to all libraries (e.g. Parsec was clearly concerned about error outputs from day one.).<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>I am sure some readers are going to point out the sophisticated open union approach that went into the design of Haskell exceptions. I agree.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3" role="doc-endnote"><p>e.g. <a href="https://wiki.haskell.org/Typeclassopedia#MonadFail" target="_blank">Monad Fail Law</a><a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4" role="doc-endnote"><p>E.g. in structurally typed environments there are no data constructors. Adding a type disambiguating property to all objects in the union types is a programming pattern in TypeScript.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5" role="doc-endnote"><p>this assumes, for simplicity, that we are listing only one (e.g. first encountered) violation of the spec.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

    </section>
</article>
]]></summary>
</entry>
<entry>
    <title>Let's agree to be different. On empirical and deductive nature of coding.</title>
    <link href="https://rpeszek.github.io//posts/2022-11-07-empirical-programming.html" />
    <id>https://rpeszek.github.io//posts/2022-11-07-empirical-programming.html</id>
    <published>2022-11-07T00:00:00Z</published>
    <updated>2022-11-07T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on November  7, 2022
        
        
        
        <div class="changelog"> <div>Revision History: <ul> <li> (2022.11.12-20) Feedback from readers including <a href="#fn16">footnote [16]</a> and <a href="#fn37">footnote [37]</a>. Clarified parts as "IMO". </li> <li> (2022.12.31) Added link to xena project and new footnote <a href="#fn5">footnote [5]</a> (this breaks previous external links to footnotes above 4). </li> <li> (2024.06.08) Added section <a href="#side-note-formal-deduction-mathematics-and-immutability-of-knowledge">formal deduction, mathematics, and immutability of knowledge</a>. </li> </ul> </div></div>
        
        
        <div class="info">Tags: <a title="All pages tagged 'patterns-of-erroneous-code'." href="../tags/patterns-of-erroneous-code.html">patterns-of-erroneous-code</a>, <a title="All pages tagged 'communication'." href="../tags/communication.html">communication</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <div class="toc"><div class="header">Table of Contents</div>
<ul>
<li><a href="#empirical-vs-deductive">Empirical vs Deductive</a>
<ul>
<li><a href="#side-note-formal-deduction-mathematics-and-immutability-of-knowledge">Side note: formal deduction, mathematics, and immutability of knowledge</a></li>
</ul></li>
<li><a href="#coding-by-experimenting-and-observing">Coding by experimenting and observing</a></li>
<li><a href="#why-is-programming-empirical">Why is programming empirical?</a>
<ul>
<li><a href="#experimental-process-and-high-extraneous-loads">Experimental process and high extraneous loads</a></li>
<li><a href="#empirical-fp">Empirical FP</a></li>
</ul></li>
<li><a href="#pragmatists-and-theorists">Pragmatists and theorists</a></li>
<li><a href="#conversations">Conversations</a></li>
<li><a href="#negativity">Negativity</a></li>
<li><a href="#final-thoughts">Final thoughts</a></li>
<li><a href="#unexplored">Unexplored</a></li>
</ul>
</div>
<blockquote>
<p><em>“When the going gets tough, the tough get empirical.” Jon Carroll</em></p>
</blockquote>
<blockquote>
<p><em>“If in physics there’s something you don’t understand, you can always hide behind the uncharted depths of nature. You can always blame God. You didn’t make it so complex yourself. But if your program doesn’t work, there is no one to hide behind. You cannot hide behind an obstinate nature. If it doesn’t work, you’ve messed up.” Edsger W. Dijkstra</em></p>
</blockquote>
<p>I will discuss (on a very high level) empirical, experimental, and deductive aspects of programming. This distinction is fun to analyze (and a somewhat unusual way to look at programming) but it seems mostly a useless curiosity in itself. I have convinced myself that programmers tend to favor empirical or favor deductive. IMO, we are placing ourselves into 2 camps. I call these camps <em>pragmatists</em> and <em>theorists</em>. This division impacts how we program and communicate. Discussion of both mindsets is the main goal of this post.</p>
<p>The topic for this post came from a realization I had when thinking about cognitive loads (<a href="2022-08-30-code-cognitiveload.html" target="_blank">Cognitive Loads in Programming</a>). It may be obvious to some of you, but it was not obvious to me: programming is largely an empirical process. I will argue that the pragmatic empirical mindset is also dominant.</p>
<p>What the others mean, how they reason, or what is important to them are the contexts in human communication. Communication without the basis of common interest is hard. Good communication requires an effort of understanding these contexts (we call it finding a common language). IMO, <em>empirical, deductive, pragmatist,</em> and <em>theorist</em> are a good terminology choice to analyze some of the current discourse (especially about FP). In this post I will present my observations about the empirical and deductive mindsets. You may disagree with me, please let me know if you do. The point is to get these contexts right, or at least to have all of us think about them a little.</p>
<p>Fairness and lack of bias are rare but beautiful if encountered in human interactions. In my current work I mostly use Haskell and I have been interested in functional programming for a long time, I am a mathematician who became a software developer (27 years ago, but these things stay with you). Thus, this post is likely to have some unintended bias. Also, these are my opinions, not an attempt at a scientifically sound reasoning.</p>
<p>I have not found much discussion about the empirical nature of programming, I am not following academic research in any related area. The topic of <a href="https://en.wikipedia.org/wiki/Empirical_software_engineering" target="_blank">empirical software engineering</a> is relevant to programming and the empirical method, but is not really what I will talk about. Retrospecting on my software programmer career, I recall good and bad things. The good had good communication, the bad had bad communication of some sort. Pragmatists vs theorists is just a part of a bigger puzzle, I am going to explore that part here.</p>
<p>So what is the point I am trying to make? My only real point is that both mindsets are important, my goal is to discuss empirical and deductive (programming), theorists and pragmatists (programmers) in as much debt as I can muster.</p>
<p>When reading it, keep in mind that my goal is to present different points of view developers are observed to have, I am not trying to make you agree with these viewpoints (that includes my side notes on formalism, mathematics, programming, and everything else in this post). The fact that we may disagree on these, is a corollary of the diversity I am trying to explore. <em>This post is not about establishing consensus which viewpoint is better, it is about gaining some understanding of these viewpoints</em>.</p>
<h2 id="empirical-vs-deductive">Empirical vs Deductive</h2>
<p>In my experience, developers do not use this terminology. Hey, in my experience, I do not use this terminology so a recap is in order.</p>
<p>Scientists use these 6 (or more) terms: inductive, empirical, a posteriori, deductive, rational, and a priori. This sounds like the beginning of a catchy song. There is also informal and formal but it breaks the rhythm, so I skip these for now. <em>Practical</em> and <em>theoretical</em> are great terms to describe a mindset, while <em>empirical</em> and <em>deductive</em> describe the thought process. In this post I will mostly try to use <em>empirical</em> and <em>deductive</em> when referring to the thought process, I will use <em>pragmatist</em> and <em>theorist</em> when referring to people.</p>
<p>Empirical (to avoid using inductive<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>) reasoning draws general conclusions from observations. E.g. certain software functionality will work because we tested it (notice how well software testing fits into this definition). Thinking about software testing as an empirical process allows us to consider things like bias<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> (e.g. the assumptions we make in unit tests), observation sample size (did we test enough?), correlation vs causality (e2e tests are unstable, is there something wrong with e2e testing itself?), if observations are balanced (adequate coverage of test scenarios across functional areas), establish observation baseline (<a href="https://en.wikipedia.org/wiki/Characterization_test" target="_blank">golden testing</a> for complex deterministic code)… Here is an exaggerated example of a correlation vs causation problem: “Each time I test the app it works just fine, yet users keep reporting issues. Something’s wrong with these people!”. But it is even more fun to think about both experiments and observations.</p>
<p>Deductive reasoning goes from general knowledge to specific conclusions. E.g. certain software functionality will work because of type safety or because it is a straightforward application of something else we believe works. In this post, deductive represents a wide range of thought processes: from tedious “observation-less” mental verification of values and types (oh, this value was supposed to be a positive number, why is it negative?, is the new refactored code equivalent to the previous?…) all the way to formal reasoning (e.g. equational proof that refactored code is equivalent).</p>
<p>In this essay, I use the term “formal” somewhat loosely. In particular, formalization of mathematics can mean something even much stricter than mathematical proofs, e.g. proofs that type check using a proof assistant. In this post mathematics is viewed as an example of formal thought already, my goal is to contrast it with, say, biology.</p>
<p>Empirical and deductive work in tandem and both are essential. IMO, we (as individuals) prefer to use one more than the other. Understanding more about these preferences will be the main topic of this post.</p>
<p> <div class="side-note"><em>I got quite a bit of pushback on this sidenote, I will expand it in next section.</em> Formal reasoning<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> is the only approach humans have figured out to solve complex problems correctly<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a> on the first go (without trial and error associated with empirical reasoning). Mathematicians are not infallible, however mathematics is more formalized than any other science and, thus, is more “correct” than other sciences. Being a programmer, I like to think about formal reasoning as immutable and empirical reasoning as an in-place mutation (e.g. mathematics effectively keeps adding to itself while empirical sciences like medicine keep changing)<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a>.<br />
In-place mutation in programming is the refactoring process. Can you think about math-like immutable designs in programming? E.g. a design or code that aged very well over, say, over the last 20 years?<br />
In this post I am not separating the formal out, it is bundled into deductive. I have struggled with this decision, at the end I decided to simplify things and keep formal reasoning bundled into the more broad deductive process.</p>
<p><em>Some people disagree strongly with the claim that formalism or mathematics are much less error prone than the empirical method. I admit that I was surprised by this. My views about mathematical correctness are something I have developed from my experiences as a mathematician and are a part of who I am. However, both positions need to be noted.</em>  </div></p>
<p>You may think that I am spending too much time explaining the obvious: any engineering will have a strong empirical aspect, engineers like to tinker with things. I assume that some readers are like me and have not thought about it before. The next section goes deeper into the empirical nature of coding.</p>
<h3 id="side-note-formal-deduction-mathematics-and-immutability-of-knowledge">Side note: formal deduction, mathematics, and immutability of knowledge</h3>
<p>A logical thing to do here would be to discuss deductive systems in programming (e.g. operational semantics, equivalences between mathematical and programming concepts, etc). I decided against it, doing this would create a bias towards <em>theorists</em> and would probably alienate some <em>pragmatists</em>. I hope I am avoiding such bias by using mathematics as the example of formal thought.</p>
<p>My comments about mathematics being mostly immutable and additive and very different from empirical sciences that keep mutating to get things right got a lot of pushback from the readers. This section expands on this topic and make some points, previously buried in footnotes, more visible. Here is an example of feedback from a reader:</p>
<blockquote>
<p><em>“Lol what? Mathematicians are fallible and often find faults in decades old proofs.”</em></p>
</blockquote>
<p>I agree with the quoted text in general, but I do not agree with the <em>often</em> quantifier. We need to keep things in context. We are comparing mathematics to other sciences. I am not suggesting that published mathematical work is never wrong (e.g. the premises behind Homotopy Type Theory, Univalent Foundations <a href="#fn6" class="footnote-ref" id="fnref6" role="doc-noteref"><sup>6</sup></a>, attempts to formalize maths with proof assistants <a href="#fn7" class="footnote-ref" id="fnref7" role="doc-noteref"><sup>7</sup></a> are all thumbs up). My argument is comparative, errors in mathematical proofs exist but are rare and exceptional. Comparatively speaking mathematics is a beacon of truth and an outlier.</p>
<p>Example: Why do we have infinitely many prime numbers? We are discussing formal deduction so let’s do it!</p>
<p><em>Theorem</em>: <em>Set of prime numbers is infinite.</em></p>
<p><em>Proof</em>: Let <code>S</code> denote the set of all primes. Assume it is finite. Let <code>P</code> be the number obtained by multiplying all numbers in <code>S</code> PLUS <code>1</code>.<br />
By construction, <code>forall p ∈ S</code>, <code>P mod p = 1</code> (<code>P</code> divided by any prime <code>p</code> has reminder of <code>1</code>).<br />
Thus, the prime factors of <code>P</code> are not in <code>S</code>. Thus, we have a contradiction. <code>S</code> has to be infinite. <em>QED</em></p>
<p>This is the proof I remember being taught in school. How old is the proof itself? I believe that nobody knows. We can find it in Euclid’s Elements. So, it was known around 300 BC! I hope this example gives you some evidence of the immutability of “formal thought”. Yes, some areas of mathematics have changed around 1900, but a surprising amount of it has not changed for centuries.</p>
<p>In contrast, Empirical sciences are in the midst of <a href="https://www.nature.com/articles/533452a" target="_blank">replication crisis</a> with reproducibility rates for published results below 50%<a href="#fn8" class="footnote-ref" id="fnref8" role="doc-noteref"><sup>8</sup></a>.</p>
<p>It is good to note that the above proof still does some hand waving. E.g. “Thus, the prime factors of <code>P</code> are not in <code>S</code>” implicitly assumes knowledge about prime factors and I am not referencing the “fundamental theorem of arithmetic”: <em>every natural number greater than 1 is either a prime itself or can be factorized as a product of primes that is unique up to their order</em>. I left it ambiguous on purpose as such ambiguities are often present in math proofs. If <code>P</code> itself is prime the proof implicitly defines its prime factors as a one element set <code>{P}</code> which is somewhat not standard. This all can be fully formalized but it would be tedious to do that and avoiding such tediosity is common in math. However, this looseness sometimes leads to overlooked errors. Again, such errors are rare. There is also the question of constructive proofs, this proof is not constructive. There is a concept of “proof relevant” logic, traditional mathematics is not proof relevant (all proofs are treated as equivalent and are not themselves an object of study). I am not delving into these areas at all.</p>
<p>Some mathematicians are trying to move towards formalizing such proofs. The new way is to write “informal” proofs for human consumption (like the one above) and accompany them with proof assistant type checked versions. This change, again, is additive as opposed to mutating. IMO, the above proof is likely to remain for another 2000 years.</p>
<p>I believe proof assistants and type checking the proofs will be used much more in the future. This formula is a quote (I do not remember the source):</p>
<p><em>lim<sub>t -&gt; ∞</sub> Math(t) = CS</em></p>
<p>I hope for this asymptotics:</p>
<p><em>lim<sub>t -&gt; ∞</sub> CS(t) = Math</em></p>
<p>and have a lot of thoughts about how this relates to LLMs and the future of human programmers. These would take too much space and do not belong in this post.</p>
<p>If you look at how most computer scientists approach programming and how most mathematicians approach writing proofs these areas remain very much divorced at the current moment. Even people who know a lot of math and do a lot of programming (e.g. data scientists) are using programming to do math but not use math to program.</p>
<blockquote>
<p><em>“Once upon a time, there was a university with a peculiar tenure policy. All faculty were tenured, and could only be dismissed for moral turpitude. What was peculiar was the definition of moral turpitude: making a false statement in class. Needless to say, the university did not teach computer science. However, it had a renowned department of mathematics.”</em></p>
</blockquote>
<p><em>John C.Reynolds</em> <a href="https://people.mpi-sws.org/~dreyer/tor/papers/reynolds.pdf" target="_blank">Types, Abstraction and Parametric Polymorphism (John C. Reynolds)</a> 1983</p>
<h2 id="coding-by-experimenting-and-observing">Coding by experimenting and observing</h2>
<p>Consider these tools and processes: debuggers (<em>observe</em> execution of statements), loggers (record <em>observed</em> behavior), testing (<em>observe</em> app behavior and <em>draw general conclusions</em> about app correctness), TDD (pre-define <em>observation</em> outcomes for the code), design patterns<a href="#fn9" class="footnote-ref" id="fnref9" role="doc-noteref"><sup>9</sup></a> (generalize code <em>observed</em> to work well, create coding protocols). I encourage you to think about tools and processes that are targeting the deductive, there are some!</p>
<p>Trial and error is how a lot of programming is done. We write some code (<em>experiment</em>) and then <em>observe</em> the result using tests, a debugger, looking at the logs, or simply observe how the app behaves ignoring other diligence. Personally, I am a little afraid of experimenting with certain types of code and I try to think through all scenarios, I could miss something but my mental process includes a lot of deductive effort. I have worked with many programmers who operate in a similar way. However, I still need to test the code to <em>observe</em> it working, make changes if needed, and rinse and repeat. Despite some deductive elements, this approach is still very experimental in its nature.</p>
<p>How about working in code that is a complete mess? We make experimental changes and then test / debug / trace the heck out of it<a href="#fn10" class="footnote-ref" id="fnref10" role="doc-noteref"><sup>10</sup></a>, right? We may try to reason about what solution is likely to work, but that is not much different from experimental alchemists trying not to blow themselves up. I can recall several projects where all my understanding was derived from debugging or tracing.</p>
<p>Let’s talk about process bureaucracy. Working in an empirical world means procedural protocols. Scrum is a procedural protocol: consider the continuous improvement process with retrospectives or team velocity calculation, these are all very empirical. Test plans, test cases, coding and formatting standards, git hygiene, even design patterns are also procedural protocols. In contrast, the deductive needs a cushy couch. Deductive and bureaucracy, IMO, do not mix well. Waterfall was a failed idea of applying an informal deductive approach to project management. There is a lot of real world complexity in project management and empirical is needed (see also <a href="https://premieragile.com/defined-vs-empirical-process/" target="_blank">Defined Process Vs Empirical Process</a>). Waterfall reminds me of mocking in unit testing. Both do not work well for similar reasons.</p>
<p>Let’s talk about bias in empirical reasoning. Figuring out contributing factors and causality is often the hardest and the most important part in empirical reasoning. If your wrist hurts when you type, is this a pinched nerve in the wrist? or, are you looking down on your laptop and the nerve is pinched around your neck? or there is a pressure point somewhere in your arm? or maybe it is not a nerve issue at all? Practitioners of empirical have been known to assume wrong cause<a href="#fn11" class="footnote-ref" id="fnref11" role="doc-noteref"><sup>11</sup></a>. Empirical is tricky.</p>
<p>Programmers are exposed to bias too. Performance issues or a bug could be caused by many factors, e.g. an application could misbehave only in certain scenarios or only in certain environment configurations, the underlying issue could be in the application code, library code, a configuration issue, an environment problem… It is not unlikely for a developer to go down a wrong path during the troubleshooting process. I view programming as an empirical process with accelerated feedback. You can go down a wrong path but you typically learn that fast.</p>
<p>I had a fun discussion with my wife, she is a data scientist working on pharmacological studies. We were discussing if a design of a clinical trial could be adjusted to do software benchmarks. There are some intriguing similarities. Obviously, humans are much more complex than programs and targeted approaches will work better in either domain. But it was a fun discussion and one that convinced me even more about the empirical nature of software development.</p>
<p>Why did we settle on using the empirical method in science? We do empirical not because we want to but because we have to. Empirical is the only way to study the unruly real world. Why did we settle on using the empirical method in programming?</p>
<h2 id="why-is-programming-empirical">Why is programming empirical?</h2>
<p>I will focus on these 2 reasons: nondeterminism and code complexity. Let’s start with the second. An interesting question is how much the empirical nature of programming has to do with arbitrary (artificial) complexity. IMO, a lot.</p>
<h3 id="experimental-process-and-high-extraneous-loads">Experimental process and high extraneous loads</h3>
<p>I am framing this in the context of my previous (<a href="2022-08-30-code-cognitiveload.html" target="_blank">cognitive loads in programming</a>) post. If you have not looked at that post, simply substitute <em>high extraneous load</em> with messy code or complex code. I think we are in a position to put a 1 and 1 together:</p>
<blockquote>
<p>  <em>The experimental nature of programming is a consequence of its high extraneous load and also one of its main causes</em></p>
</blockquote>
<p>The bigger extraneous load the more we experiment. The more we experiment the bigger the extraneous load gets.<br />
To work on a complex code we are effectively forced to experiment. Adding more experimental code only increases the complexity.</p>
<p>This is a feedback loop. To break this loop (and control the complexity) we need to rewrite or refactor parts of the code. These involve some deductive process. Some amount of deductive is essential, a deductive is what can break the feedback loop. Deductive and empirical thought processes are closely related to cognitive loads discussed in my previous post and to controlling arbitrary complexity.</p>
<p>There is one notable exception where piling up experiments does not result in extraneous complexity. It is called type safety.</p>
<p> <div class="side-note"><strong>Side Note: Type safe experiments</strong> are my favorite approach to programming. In the presence of a nice type system, coding can become solving jig-saw puzzles (writing code by using building blocks that can fit only in a correct way). A similar process can work even in TypeScript<a href="#fn12" class="footnote-ref" id="fnref12" role="doc-noteref"><sup>12</sup></a>. It is typically enough to provide just enough safety to reduce the implementation solution space to prevent accidental incorrect implementations (this more relaxed approach ignores <a href="https://www.hyrumslaw.com/">Hyrum’s Law</a> but IMO still works well).</p>
<p>This approach could include an interactive, type checker assisted deductive process (asking the compiler a series of type questions, something akin to type hole driven development<a href="#fn13" class="footnote-ref" id="fnref13" role="doc-noteref"><sup>13</sup></a>) or just making a guess and trying to see if the pieces fit. The second approach is a form of experimentation. Working with <em>hlint</em> (Haskell linter, other static tools that do similar things exist) is lots of experimental fun too. I often need to get <em>a solution</em> in and the linter will replace it with a much nicer fused code.</p>
<p><em>Combinator</em> is a 100 years old term and I could be wrong about its intended intuitive meaning<a href="#fn14" class="footnote-ref" id="fnref14" role="doc-noteref"><sup>14</sup></a>. To me it means a <em>building block</em>. Functional programming is about designing and using well fitting combinators.</p>
<p>Jig-saw puzzle approach results in very clean programs, is addictive, and a lot of fun, but is not commonly used at large. Importance of type safety is a position I hold but is also one that many programmers will disagree with.<br />
 </div></p>
<h3 id="empirical-fp">Empirical FP</h3>
<p>FP is considered theoretical for at least 2 reasons: <em>(1)</em> FP considers formalism to be important, <em>(2)</em> how it is being presented to the public<a href="#fn15" class="footnote-ref" id="fnref15" role="doc-noteref"><sup>15</sup></a>. But, is FP an empirical process as well?</p>
<p>Consider 2 cornerstones of scientific experiments: reproducibility and properly randomized sufficiently large observation samples, and restrict our attention to automated testing. FP-ers will immediately notice two related concepts: <em>referential transparency</em> and <em>property testing</em> (e.g. QuickCheck). Clearly FP has a lot going on for it if you look at it from the empirical angle.</p>
<p>Whatever your opinion about FP vs code complexity is, you have to agree that FP is about controlling nondeterminism. Functional programming tries to separate out the predictable deterministic part (pure functions) and limit (even stratify) the unruly effectful parts. Functional programming handling of nondeterminism is analogous to empirical study designs trying to control variance. I think that this is why FP seems so empirically-friendly.</p>
<p>Haskell is sometimes called <em>the best imperative PL</em><a href="#fn16" class="footnote-ref" id="fnref16" role="doc-noteref"><sup>16</sup></a>. I propose that <em>FP could become the best empirical programming method</em> (yeah, could I be a little biased). However, it is “could become” rather than “is”. Empirical process benefits from good observability, e.g. decent debuggers, stack traces, rich amount of error information… This is an area where FP could improve in general (Haskell in particular)<a href="#fn17" class="footnote-ref" id="fnref17" role="doc-noteref"><sup>17</sup></a>.</p>
<p> <div class="side-note"><strong>Side Note:</strong><a href="#fn18" class="footnote-ref" id="fnref18" role="doc-noteref"><sup>18</sup></a> (Haskell 101). What could better explain a theorist mindset than to actually experience some of the formal thinking? Here is an “elementary” (from ground up, using only basic language features) implementation of a popular Haskell combinator. Notice the implementation is just a bunch of equations that use constructors or pattern match and nothing else:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="ot">partitionEithers ::</span> [<span class="dt">Either</span> a b] <span class="ot">-&gt;</span> ([a], [b]) </span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a>partitionEithers [] <span class="ot">=</span> ([], [])</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a>partitionEithers (<span class="dt">Left</span> a<span class="op">:</span> es) <span class="ot">=</span> (a<span class="op">:</span> ra, rb)</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a>    <span class="kw">where</span> (ra, rb) <span class="ot">=</span> partitionEithers es</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a>partitionEithers (<span class="dt">Right</span> b<span class="op">:</span> es) <span class="ot">=</span> (ra, b<span class="op">:</span> rb)</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a>    <span class="kw">where</span> (ra, rb) <span class="ot">=</span> partitionEithers es</span></code></pre></div>
<p><strong>Fun exercise 1</strong>: Identify the obvious conservation law for list lengths. Use QuickCheck or other property testing library to verify it. It suffices to test just one type, say <code>a ~ b  ~ Bool</code> (unit <code>()</code> will do too), can you provide a reason why? Can you try to formalize it?<br />
<strong>Fun exercise 2</strong>: Use paper and pencil to prove that this implementation satisfies that law. This exercise shows, formal reasoning does not need to be complex or advanced.<br />
<strong>Fun exercise 3</strong>: Change the above code to violate the conservation law. How likely is for such implementation to be accidental? Add <a href="https://ucsd-progsys.github.io/liquidhaskell/" target="_blank">Liquid Haskell</a> annotations to prevent unlawful solutions.<br />
<strong>Fun exercise 4</strong>: In Haskell, any implementation of <code>partitionEithers</code> type is a <a href="https://bartoszmilewski.com/2015/04/07/natural-transformations/" target="_blank">natural transformation</a> in <code>a</code> and <code>b</code>. Can this be used in exercise 1?<br />
<strong>Fun exercise 5</strong>: (1) and (4) rely on Haskell language property called parametricity. This prevents Haskell programs from learning what the actual types behind <code>a</code> and <code>b</code> are or use values of these types in a concrete way (e.g. there are no globally available <code>==</code>, <code>toString</code> etc). A number of mainstream languages have now the ability to express a type similar to <code>partitionEithers</code>. Exploit lack of parametricity in your chosen language to create an implementation of <code>partitionEithers</code> that violates the conservation law only for some types and works as expected for others (this makes randomized testing harder, why?). Show that your implementation is not a natural transformation.</p>
<p>Hints and partial solutions<a href="#fn19" class="footnote-ref" id="fnref19" role="doc-noteref"><sup>19</sup></a>. It would be interesting to know how often thinking about properties and verification of properties is included in actual project work.  </div></p>
<p>FP is a hybrid containing both empirical and formal. … But I got sidetracked a bit towards areas of my interest. You can classify this under IMO if you disagree with what I wrote.<br />
Let’s finally get to my main topic: the human aspect.</p>
<h2 id="pragmatists-and-theorists">Pragmatists and theorists</h2>
<p>I am using terms <em>theorist</em> and <em>pragmatist</em> somewhat colloquially, but the meaning is close to how the terms are used elsewhere. In this section I will try my best to describe both mindsets. IMO, mindset and interest are very related terms. In this post I could almost use them interchangeably.</p>
<p>If you listen to a functional programmer talk, you are likely to hear these terms: “reasoning about code”, “principled computation”, “computation laws”, “correctness by design”, “type safety”. These people are likely to study things like lambda calculi, operational semantics, category theory, type theory… All these things come with formal proofs and could result in a very specific mental training. <em>To this group programming is more of a deductive process.</em></p>
<p>These things do not resonate with the vast majority of programmers who have a more pragmatic mindset. You are more likely to hear these terms: “testing”, “TDD”, “hacking” (meant as a compliment, “I made it work”), even “design patterns” (though rarely these days). <em>To this group programming is more an empirical process.</em><a href="#fn20" class="footnote-ref" id="fnref20" role="doc-noteref"><sup>20</sup></a></p>
<p>A theorist’s primary interest is in deductive reasoning. This implies an interest in the deductive itself. Deductive reasoning is often called top-down reasoning (general knowledge comes first). Formal reasoning (the pinnacle of deductive) has a very strong attraction for some individuals while being very much disliked by others. Some programmers dive deep into FP and learn formal reasoning. Sometimes a mathematician (this was the case with me) makes a career conversion and becomes a developer. Interest in the deductive and formal is rather rare, to many programmers formal methods are a foreign concept.</p>
<p>In contrast, a pragmatist’s primary interest is in accomplishing the tasks at hand. Pragmatists will experiment and go empirical to get there. “When the going gets tough, the tough get empirical.” Real world complexity is rarely fully amenable to formal reasoning. Unfortunately pragmatists take a stronger stance on this, a pragmatist may think that formal reasoning is never useful.</p>
<p>I need to emphasize that what I am presenting is an oversimplification. People are complex and cannot be easily labeled. In particular, data scientists, statisticians, programmers interested in probabilistic computing models have a lot of formal training but probably will develop a more empirical attitude towards programming. The attitude towards programming is shaped by many factors (e.g. work experiences, types of programming projects). Theoretical vs pragmatic mindset is IMO a good first approximation.</p>
<p>Pragmatists will say “it works because we tested it”. Theorists will say “it is correct by design” (or closer to Dijkstra: “we tested it and we know it does not work”<a href="#fn21" class="footnote-ref" id="fnref21" role="doc-noteref"><sup>21</sup></a>). Pragmatists will say “this abstraction is too complicated to use because it is too theoretical”. Theorists will say “this abstraction is too complicated because it lacks theoretical backing”. The 2 groups may find it difficult to communicate “this is nice” and “this is terrible” have a very different context when spoken by a pragmatist and a theorist.</p>
<p>Pragmatists want the tools to be mainstream and popular to pass their test of practical usefulness. Pragmatists will select tools that provide good ability to observe how the code runs. To a pragmatist reasoning about code often means studying its execution flow. Some pragmatists go a step further and will only select a PL that allows them the best control over execution (e.g.  <em>JS</em> is used by browsers so this is what they will want to write). This could be caused by a (somewhat justified<a href="#fn22" class="footnote-ref" id="fnref22" role="doc-noteref"><sup>22</sup></a>) distrust for abstractions.</p>
<p>In contrast, theorists want tools that support, rather than inhibit, deductive reasoning. They typically want to reason on a higher level than the execution flow. Many will view programs and execution as decoupled concerns<a href="#fn23" class="footnote-ref" id="fnref23" role="doc-noteref"><sup>23</sup></a>. This is where the <em>imperative vs denotative</em> discussion comes into play as well.</p>
<p>I consider programming to be a combination of both the engineering procedural processes and math-like science. Pragmatists are interested in the process, they want code standards, formatting standards, clean git history… Theorists will want functors, monads, higher rank types, higher kinded types, dependent types… Programming, obviously, benefits from both engineering and mathematics.</p>
<p>What makes us effective and confident when working with code? For a pragmatist the source of confidence is likely to be test coverage, for a theorist it will be type safety, abstractions, lawfulness. For both confidence implies some ability to understand the code (absorb the cognitive load). To put some of these thoughts in the context of the previous post:</p>
<blockquote>
<p>  <em>Theorist typically prefer germane, pragmatists are more at home with extraneous cognitive load</em><br />
  <em>Theorist typically prefer simple and hard, pragmatists prefer easy and will accept complex</em></p>
</blockquote>
<p>One bizarre difference I have noticed between heavy deductive thinkers and empirical mindsets is their favorite approach to learning. When learning, some theorists will want to finish a section or chapter before writing a single line of code. The top-down thinking sometimes extends to top-down learning (a theorist wants to internalize the theory before applying it). My wife and I are in this group. I had to force myself to write some code early when going through <a href="https://www.goodreads.com/book/show/112252.Types_and_Programming_Languages" target="_blank">Types and Programming Languages</a>, I decided to create a public github repo to give myself incentive to code as I learn when reading <a href="https://www.goodreads.com/book/show/33618151-category-theory-for-programmers" target="_blank">Category Theory for Programmers</a> and <a href="https://www.manning.com/books/type-driven-development-with-idris" target="_blank">T(ype)DD in Idris</a>. Some theorists treat learning as a murder mystery and want to learn ASAP who done it, pragmatists know it will be the butler.</p>
<p>On the flip side, pragmatists prefer hands-on learning from code examples (ideally, associated with project work) and typically expect immediate return on their learning investments. E.g. a pragmatist is unlikely to spend several months studying, say, a one line of code<a href="#fn24" class="footnote-ref" id="fnref24" role="doc-noteref"><sup>24</sup></a>. Pragmatists are less likely to search for deep understanding (this is kinda definitional, having theoretical interests makes you a theorist).<br />
Also, it seems logical to assume that learning from experience is more habituating. Pragmatists may have a harder time making mental shifts to how they work with code. <em>Unlearning</em> is hard on all of us, I believe it is harder on pragmatists.</p>
<p>The part of FP that has been the most disappointing for me is a typically low quality of error outputs. This may have to do with all falsehoods being equivalent in mathematics<a href="#fn25" class="footnote-ref" id="fnref25" role="doc-noteref"><sup>25</sup></a>. However, we should not criticize the theory, rather the theorists for selecting abstractions that suppress or confuse error information (IMO). I wrote about it in the previous post and also before<a href="#fn26" class="footnote-ref" id="fnref26" role="doc-noteref"><sup>26</sup></a>.</p>
<p>In contrast, to an experienced pragmatist error output is (typically) an important observation. It is a pragmatic thing to do to know <em>what</em> went wrong. However, the tendency towards the use of <code>null</code>, <code>Option</code>, <code>Maybe</code> suppressing available information is something I do not understand. IMO, goes beyond the topics we are discussing here<a href="#fn27" class="footnote-ref" id="fnref27" role="doc-noteref"><sup>27</sup></a>.</p>
<p>While theorists may have a problem engaging with things outside of their theoretical model, pragmatists often have a problem engaging with things that are even mildly theoretical like computational laws or even referential transparency. Programmers often don’t care about computational properties, but will be surprised by software behavior when they are missing. Many gotchas can be described as a “natural” computation property that is violated.</p>
<p>Some theorists may not see a big difference between a prototype and a product that is fully implemented and maintained. This could be related to <em>published equals done</em> in academia and can be quite annoying.</p>
<p><strong>Probably, the most interesting difference between both mindsets is:</strong></p>
<blockquote>
<p>  <em>A bug means an observed malfunction to empirical pragmatists</em><br />
  <em>A bug means a logical flaw to formal theorists</em></p>
</blockquote>
<p>In empirical science, rare is tricky. Rare can escape the empirical process. The term <em>outlier</em>, used in statistics, is relevant. The term “zebra” is used by medical doctors, “zebras” are hard to figure out, rare cases. Empirical reasoning sometimes equates rare with impossible. When dealing with real world we say:</p>
<blockquote>
<p>  <em>“the exception that proves the rule”</em><br />
  In formal reasoning <em>exceptions disprove the rule.</em></p>
</blockquote>
<p>In software engineering an exceptional, rare event often stops being rare (e.g. when the data or usage changes expose it). I fixed many bugs caused by a programmer’s decision that a certain scenario is very unlikely and, thus, it is OK to cut corners. In my experience, ignoring rare scenarios saves hours and ends up costing weeks or even months later.</p>
<p>However, I am not convinced that cutting corners is unique to either mindset. A pragmatist may think “this is so unlikely, I will not waste time on it”. The rare case could be not represented by the theoretical model that a theorist is considering (e.g. error information in a bunch of FP code). IMO, pragmatists and theorists cut corners in different ways, however, <em>cutting corners has no place in formal reasoning</em> and I expect some programmers (most likely a subset of theorists) carefully think through rare cases.</p>
<p>Theoretical and practical mindsets are antipodes of programming. Even for people who “own” both, being a pragmatist or being a theorist is like wearing a hat. You can’t wear both at the same time, you would look ridiculous. I started making a conscious effort to understand which hat I have on.</p>
<p> <div class="side-note"><strong>Side Notes.</strong> Many things about programming seem to be on their head (making hats a somewhat tricky accessory). Some ideas typically associated with FP are very pragmatic. E.g. descriptive types, clear inputs and outputs, getting the same result on each try, ADTs (how could the ability to get a lot of generic code for free in Haskell be considered anything but pragmatic?)… At the same time OOP is quite theoretical (taxonomic knowledge is fascinating academically, e.g. in biology or linguistics, but how practically important is it in programming?) and very complex (e.g. subtyping variance). I dislike OOP primarily because it makes type checking less effective and I rely on type safety.</p>
<p>Another complex aspect is how <em>theoretical</em> or <em>pragmatic</em> you are. If we classify a typical <em>Rust</em> programmer as a <em>theorist</em>, where do we put someone using <a href="http://www.ats-lang.org/" target="_blank"><em>ATS</em></a>? If a typical <em>Haskell</em> developer is a <em>theorist</em>, how do we classify someone working with <em>Agda</em> or <em>Coq</em>…?</p>
<p>I am simplifying all of this and consider programmers to be either <em>pragmatists</em> or <em>theorists</em> and take an oversimplified (binary) and a somewhat stereotypical view of what these terms mean.  </div></p>
<p>This post argues that both traits are important. We will dig deeper into both ways of thinking by analyzing some examples.</p>
<h2 id="conversations">Conversations</h2>
<p>So far, this post has tried to upset people on both sides equally. This section will be biased towards theorists, it is hard for me to present the pragmatist’s viewpoint this way. Engineers are some of the cleverest people on this planet. If someone rewrote this section from an engineer/pragmatist point of view, I would love to read it.</p>
<p>Perhaps not surprisingly, theorists are not all equally disappointed about logical software defects. Explaining this diversity is a price I have to pay for bundling all theorists together. Think physics, it is very theoretical, yet theoretical physicists are happy to do hand waving arguments. In contrast, there is no hand waving in mathematics. IMO, this stricter view plays a role in how some of us approach programming.</p>
<p>Alice: “We have a concurrency issue in our code”<br />
Bob: “Are you talking about a production issue, a failing test, or is it purely theoretical?”<br />
If Alice gets a change to explain the race condition, she may hear this response:<br />
Carol: “We did it like this before and everything was fine”.</p>
<p>Alice (nick name Negative Nancy, a theorist) considers all logical defects to be a disappointment. Bob and Carol are pragmatists and approach logical defects in a more relaxed way. It seems like a good idea for Bob and Carol to understand a little bit about how Alice approaches programming, and vice versa. Let’s analyze this dialog a little bit.</p>
<p>To Alice logical issues are kinda a big deal. She will consider it very hard to reason about code sprinkled with logical flaws. There is actually a good reason for this. Some logical flaws we examined in my previous post<a href="#fn28" class="footnote-ref" id="fnref28" role="doc-noteref"><sup>28</sup></a> are quite isolated but it is hard (if not impossible) to understand the full impact of many of them. I am like Alice, working in a complex imperative program or a poorly written functional code results in me forming a large mental repository of issues and their unclear impacts. Maintaining it is a tough mental effort.</p>
<p>Here is a story from my personal experience. A few years back I did a code review session with two (very capable) developers. I showed them one of my “bug stashes” in the project we all were contributing to. It had to do with a logically brittle use of inheritance. I demonstrated the process I go through to verify the brittle bits. This session was very productive, we all learned something from it, and this code was refactored later. Their response is something I still contemplate: “We do not go through such steps, we just assume it will work”. For me it was a learning experience I still think about, it made me realize how different our mindsets are.</p>
<p>Returning to Bob, he is a pragmatist. Notice that Bob has stratified all contexts he assumed relevant to Alice’s finding: production issue, failing test, and theoretical. To Bob, a logical issue in code is just a part of life. “It has bugs, it’s called software.” This empirical mindset, in some ways, is healthier<a href="#fn29" class="footnote-ref" id="fnref29" role="doc-noteref"><sup>29</sup></a>. It is not unusual for empirical reasoning to dismiss theoretical concerns, however in this case this is likely to be wrong. It is hard to spot or even assess the impact of some bugs (e.g. race conditions) using testing or other observation based methods<a href="#fn30" class="footnote-ref" id="fnref30" role="doc-noteref"><sup>30</sup></a>. The concurrency flaw Alice has identified can start manifesting itself all the sudden, this is what concurrency issues have been <em>observed</em> 🙂 to do.</p>
<p>Carol’s response suggests an empirical mindset as well. Carol has generalized previous observations of a working product and that generalization overrides Alice’s warning bells. Going from specific to general is what empirical process is about. Proper empirical reasoning will question, even invalidate, previous “hypothesis” if new evidence provides reasons for doing so, but Alice is not providing any empirical evidence. Alice’s argument is purely deductive, it could be helpful if she came up with a test that exposes the concurrency problem she has identified (this could be, obviously, very hard or even impossible to do).</p>
<p>Bob: “We are starting the new frontend project, I propose we keep using XYZ PL, but maybe we could add a new library to our setup?”<br />
Alice: “XYZ is fundamentally broken, we should move to something sound, like Reason.”<br />
Carol: “Alice, we do not know that PL, this will put the project at risk!”<br />
Alice: “We know so many problems about XYZ, XYZ puts the project in jeopardy too”.</p>
<p>You may be wondering why I call JavaScript XYZ? Well 🙂, XYZ was intended as a placeholder. Alice would like to use tools that help, not inhibit her deductive process and are logically sound. Alice has witnessed her colleagues (and probably herself) trip over XYZ unsound design numerous times. She considers the use of XYZ akin to building a house on a broken foundation.<br />
Bob and Carol insist on tools that have good IDE support, good debugging, and are familiar even if logically unsound. Pragmatists want to reason about execution flow, browsers use <em>JS</em>, thus, they may want a language very close to <em>JS</em>. Carol has a very valid point too, one that Alice may have hard time accepting. I admit, I sympathize with Alice, even if Carol is probably right here.</p>
<p>Alice: “You are memoizing a computation that is not referentially transparent”<br />
Carol: “I have manually tested it and, besides, this code has a 100% test coverage”<br />
Bob: “We are assuming that it is referentially transparent and want consistent results when we use it”<br />
Alice: “Remember we patched a bug by updating shared state in the middle of this computation, did you retest this scenario?”</p>
<p>This code review session shows a benefit of having someone around who keeps a repository of potential issues in their head. I have noticed that developers are typically surprised when computation behavior keeps changing, yet are mostly not willing to engage with the concept of referential transparency. I also think some do not think about what 100% test coverage implies and what it does not<a href="#fn31" class="footnote-ref" id="fnref31" role="doc-noteref"><sup>31</sup></a>.</p>
<p>Bob: “I changed the interface, you can now pass new parameters to control how the data is processed”<br />
Alice: “I changed the module, you can now use new functions (combinators) to manipulate the data”</p>
<p>Functions are great for reasoning about code, parameters are great for tweaking and experimenting.<br />
Alice’s deductive approach can really be beneficial when writing code, probably more than when troubleshooting empirically implemented code. Let’s get a little philosophical:</p>
<p>Alice: “Ideal code to me is one I would still be proud of after 10 years”<br />
Bob: “If you think about your 10 year old code as perfect, you learned nothing in these 10 years”<br />
Alice: “I am looking for something as timeless as mathematics”<br />
Carol: “Mathematics keeps improving and changing I am sure, everything does”<br />
Alice: “No, it only grows, it has not changed its mind in over 100 years”</p>
<p>This is almost an exact copy of a conversation I had with some of my coworkers. The immutability analogy I have used before works well here: mathematics is immutable while empirical sciences mutate in-place (some theorist hold this view). Bob’s argument is partially valid as there is a lot of engineering going into coding and that is likely to keep changing<a href="#fn32" class="footnote-ref" id="fnref32" role="doc-noteref"><sup>32</sup></a>. Also, formal verification has a maintenance cost<a href="#fn33" class="footnote-ref" id="fnref33" role="doc-noteref"><sup>33</sup></a> making me question how realistic Alice’s dream is. Can you think about code examples that aged very well?</p>
<p> <div class="side-note">We consider PLs that reach a certain threshold of usage as immortal. A PL could be immortal but the ideas that went into its design may have died a long time ago. Empirical needs an ability to mutate to improve. PLs are an example where, due to backward compatibility, changes are very hard to do. Empirical + immutable + immortal is a bad combination. This is why some (theorists of course) dislike mainstream PLs. Another example where immutability of formal thought is very, very useful is P2P (e.g. distributed blockchains). If distributing a code change is expensive or impossible, then the formal is needed. Inadequate amount of formal in these areas is simply unpragmatic and costly. IMO, “code that ages well” is an important topic.<br />
 </div></p>
<p>These conversation examples were not intended to be exhaustive. I invite you to think more about the differences between pragmatists and theorists in both creating and consuming the code. I invite you to think more about how each side thinks.<br />
I need to emphasize, this is not a binary separation where everyone is either pragmatist or theorist. Many of us have both traits, just one is more dominant than the other and they tend to not manifest at the same time.</p>
<p> <div class="side-note">For the longest time, I could not figure out why certain decisions about PLs, popular libraries, or programming projects are being made. I could not understand why certain bugs remain not fixed, why there are no deprecation attempts, why certain decisions have been made in the first place. The empirical mindset I have tried to explain here is my best attempt at understanding these things. E.g. I cannot explain in any other way why Java maintainers decided not to deprecate standard library classes where <code>equals</code> is not symmetric. The list of such issues is long<a href="#fn34" class="footnote-ref" id="fnref34" role="doc-noteref"><sup>34</sup></a>. Are all of these “exceptions that prove the rule of solid design”, rare and thus not important cases in the mindset of the maintainers? I consider this approach to be not pragmatic and expensive. If I was in charge of designing programming courses<a href="#fn35" class="footnote-ref" id="fnref35" role="doc-noteref"><sup>35</sup></a>, an example exercise would look like this:</p>
<blockquote>
<p>  <em>There is a common belief that TypeScript compilation flags like <code>strictNullChecks</code> prevent escaped <code>null</code> and <code>undefined</code>.<br />
  Exploit how TS defines variance to create a function that has <code>number</code> as the return type but it returns <code>undefined</code> for some of its input parameter values.</em></p>
</blockquote>
<p>The pragmatist’s take (as I see it) is: (1) the impact of these booby traps is small and with some luck you will not notice (or <em>observe</em>) them in your project, (2) the ingredients you use do not need to be sound, they are just a part of a bigger implementation noise. I do not agree with these arguments, but at least I think I know what the arguments are, (3) counter examples do not come up much when using inductive reasoning (pragmatists are more likely to think about examples rather than general concepts and counter examples).  </div></p>
<h2 id="negativity">Negativity</h2>
<p>There is quite a bit of negativity around us, the programming community is not exempt from it. I will wrap up with some loose thoughts about negativity in the context of deductive and empirical mindsets.</p>
<p>Venting frustration, IMO, does not improve how either the “venter” or the “ventee” feel. It only fuels the negativity. IMO, the best way to fight negative emotions is to employ the deductive. Figuring out the underlying context that causes negativity can save a conversation and simply engaging in that search can protect you. Using logic to confront emotions is a form of what psychologists call <a href="https://www.apa.org/topics/anger/control" target="_blank">cognitive restructuring</a>.</p>
<p>Criticism of bad code is bound to be unpleasant to its authors. Even worse, a lot of code can be criticized on purely logical grounds. This can be interpreted as a critique of the author’s competence and, I am sure, is a big source of negativity and tension. Let’s look at this differently. Look at some of the empirical sciences like neuroscience and see how much of the knowledge got adjusted if not invalidated<a href="#fn36" class="footnote-ref" id="fnref36" role="doc-noteref"><sup>36</sup></a>. IMO, programming is on a very localized and very accelerated path of the same process. Is “bad code” phenomena partially related to the empirical nature of programming? I think it is<a href="#fn37" class="footnote-ref" id="fnref37" role="doc-noteref"><sup>37</sup></a>.</p>
<p>Theorists are likely to devote a significant effort into learning. How does it feel to not be allowed to use what you worked hard to figure out? Examples like <a href="https://brianmckenna.org/blog/howtostopfp" target="_blank">How to stop functional programming</a> come to mind. This is something that can be changed on a small scale but there are only a few places who care to do so. There must be quite a few frustrated programmers out there<a href="#fn38" class="footnote-ref" id="fnref38" role="doc-noteref"><sup>38</sup></a>. I have been in that position and I know it is mentally hard. I have argued that the deductive process plays an important role and, IMO, it is in the interest of the industry to treat the “theorists” minority better.</p>
<p>I have promised at the beginning that I will not try to make too many “points”. This section contained the exceptions that prove the rule 🙂.</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>Did I sneak in any other side-“points” worth noticing? IMO, this one (if you agree that formalism is less prone to errors):</p>
<blockquote>
<p><em>Strict backward compatibility implies a need for formalism</em><br />
or, equivalently<br />
<em>Empirical, immortal, immutable, sanity: pick 3</em></p>
</blockquote>
<p>The odd discourse between formal and empirical is not unique to programming. I still remember a few jokes about “a mathematician, a physicist, a chemist, …”. Empirical vs deductive, if one needs to budge then the empirical wins, mathematics has to move. Logical correctness is often more a guideline rather than a shackle. Some (mostly functional programmers) argue that computer science and programming could benefit from a stricter application of formalism. The difference is where the source of truth is. In a pure empirical world that source has to be what we observe. In programming there can be just enough determinism to benefit from treating logical soundness concerns more seriously. I believe, we need both empirical and deductive and I hope I made a convincing case for us to try harder for their peaceful coexistence. We can start by trying to understand the other viewpoint even if we do not agree with it.</p>
<p>Thank you for reading!</p>
<h2 id="unexplored">Unexplored</h2>
<p>We can learn a thing or two about programming if we think about it as an empirical process. We can learn a thing or two about the empirical process itself if we examine programming as a case study. We did a little bit of both in this post. It seems that this synergy can be explored more.</p>
<p>Impact of education on the development of either mindset.</p>
<p>Related psychology, evolutionary biology: Humans survived and evolved by “observing” things and acting on these observations. Empirical process is in our nature. This also explains why we dismiss rare scenarios. I do not feel qualified to discuss these in more depth.</p>
<p>As we have discussed, developers approach bugs differently. This is how my interest in figuring out different programmer mindsets has started. There is a different way to look at this. Consider these 3 axes: “It has bugs, it’s called software” is the origin, testing is one axis, “correctness by design” abstractions and type safety is second, a mental repository of possible issues and their impacts is third. Pragmatists are on the first axis, theorists on the second. We need the name for the third group, let’s call them <em>perfectionists</em>. I came to FP on a correctness wagon, theory and improved coding efficiency are for me an added bonus. The question is how the <em>perfectionists</em> fit into this picture. This post bundled them with theorists, this was likely an oversimplification.</p>
<p>Implicit contexts in communication between programmers. There appears to be much more to explore here. Consider programming internet discussion forums (IMO, a Manhattan of communication skills, if you can make it there you will do really well in your project team). One can observe all kinds of context related bias issues (on the extreme end of this, some redditers do not consider reading to be a prerequisite to responding) or lack of context clarity (e.g. heavily downvoted posts with no comments). Moving away from discussion groups, teams tend to create their own localized contexts (unique vocabulary, proprietary technical solutions) which is often not ideal. IMO, context clarity is to communication what referential transparency is to programming. IMO, context is to communication what understanding of causation is to empirical science.</p>
<p>Program synthesis, you know the thing that is going to render programmers obsolete: It appears that empirical vs formal translates to 2 different approaches to do program synthesis. I have listened to a few presentations about the formal approach. I expect probabilistic models, deep learning approaches to be more mainstream and prevalent. It is interesting how this will play out, but I do not feel qualified to discuss this in much depth. <em>“The effort of using machines to mimic the human mind has always struck me as rather silly. I would rather use them to mimic something better.”</em> (Dijkstra, of course). Quite possibly we will see an equivalent of empirical vs deductive play out in this area.</p>
<p><a href="https://en.wikipedia.org/wiki/Abductive_reasoning" target="_blank">Abductive reasoning</a> and applicability of Ockham razor to software development seems like another fascinating philosophical topic that is orthogonal to what we have discussed<a href="#fn39" class="footnote-ref" id="fnref39" role="doc-noteref"><sup>39</sup></a>.</p>
<p>LLM’s like ChatGPT cab be viewed as the ultimate “inductive learners”. Does this create opportunity for human minds capable of a more deductive thought process?</p>
<p>I am sure there are many more interesting angles to explore here.</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>This inductive reasoning should be confused with mathematical (or structural in PLT) induction. Believe it or not it sometimes is.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>I use the term bias loosely as well. Scientists are concerned about bias in the design of empirical studies (which observations go in, how things are observed, etc), bias in how results are analyzed (e.g. proper stratification). Bias is a <a href="https://en.wikipedia.org/wiki/Bias_(statistics)" target="_blank">statistics</a> term, <a href="https://en.wikipedia.org/wiki/List_of_cognitive_biases" target="_blank">psychology</a> term, all of this seems very relevant to programming.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3" role="doc-endnote"><p>The term <em>formalism</em> has special meaning in mathematics, I use it colloquially (i.e. all mathematics is formal). Examples of formal approaches popular among FP-ers could be equational reasoning, use of logical implication (e.g. with Haskell type class constraints), use of mathematical or structural induction (an exercise using it is included later in this post). Readers familiar with equational reasoning may agree with me about its similarity to a refactoring process where the developer mentally verifies that the new code is equivalent to the old. The line between formal and informal is sometimes thin.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4" role="doc-endnote"><p>This is kinda fun to think about: we have only empirical evidence of mathematics as a whole being <em>correct</em>, we know we will never prove it formally. Mistakes in mathematics are very rare (more on this in next section). However, we have a lot of empirical evidence of (past) <em>incorrectness</em> in various empirical sciences. I am a pragmatist enough to say that mathematics is correct (at least comparatively speaking), the rest looks good until we learn more about it (just like bugs in software). I believe a significant part of people trained in mathematics and formal reasoning share this viewpoint. File it under IMO if you must.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5" role="doc-endnote"><p>Obviously, a well established empirical knowledge will not change for ages as well. Immutability is a result of getting things right, empirical method converges towards it, formal method starts there. My metaphor “empirical is mutating in-place” is not perfect.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn6" role="doc-endnote"><p>See this lecture by Voevodsky himself about the motivation for <a href="https://www.youtube.com/watch?v=E9RiR9AcXeE" target="_blank">Univalent Foundations: New Foundations of Mathematics</a><a href="#fnref6" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn7" role="doc-endnote"><p>This blog post: <a href="https://xenaproject.wordpress.com/2021/01/21/formalising-mathematics-an-introduction/" target="_blank">xena project, formalizing mathematics</a> is really worth reading as a whole. It points out that mathematicians do make errors too and argues about the need to add computer assisted formal verification to mathematics.<a href="#fnref7" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn8" role="doc-endnote"><p>One line of defense presented in the quoted nature article is that experiment protocols are not being published. I find this line of reasoning very concerning: think about the concept of steady state.<a href="#fnref8" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn9" role="doc-endnote"><p>Design patterns are an interesting bunch because they include some deductive work. E.g. factory decouples idiosyncratic aspects of object construction and decoupling is known to be beneficial. However, this is not that much different from, say, a wildlife biologist generalizing observed behavior of individuals to a whole species using a known symbiotic relationship in their argument.<br />
Hard to resist: They say symbiotic, we say decoupling… ;)<a href="#fnref9" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn10" role="doc-endnote"><p>Notice, I did not say “debug the crap out of it” because that could imply that we are making code improvements.<a href="#fnref10" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn11" role="doc-endnote"><p>Rate of misdiagnosis for carpal tunnel is over 80% according to this article: <a href="https://www.carpalrx.com/post/carpal-tunnel-misdiagnosis" target="_blank">carpal tunnel misdiagnosis</a>. A very sad example for the past: it is now believed that a big cause of death during Spanish Flu was too much aspirin prescribed to treat the symptoms.<a href="#fnref11" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn12" role="doc-endnote"><p>See <a href="2022-03-13-ts-types-part6.html#a-walk-in-the-park" target="_blank">A walk in the park</a>, <a href="2022-01-09-ts-types-part4.html#phantom-types" target="_blank">phantom types</a>, <a href="2022-01-09-ts-types-part4.html#preventing-information-escape" target="_blank">existential types</a> in my TS series.<a href="#fnref12" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn13" role="doc-endnote"><p>A nice presentation that illustrates interacting with the type checker to write code is: <a href="https://youtu.be/DRq2NgeFcO0?t=356">Type-driven Development of Idris, Vect</a> on youtube. Adding type level information about the size of list constricts the solution space and creates a jigsaw puzzle.<a href="#fnref13" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn14" role="doc-endnote"><p>Schönfinkel (credited for the concept of combinatory logic) was Russian and worked with Hilbert in Germany. His original work was in German. Curry worked with Hilbert as well. The term has to do with building blocks (primitive “functions”), aka S K I in SKI calculus. In LC, combinator is a lambda expression without free variables. Looks like a piece of a jigsaw puzzle to me.<a href="#fnref14" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn15" role="doc-endnote"><p>There are efforts to change that, e.g. see this reddit: <a href="https://www.reddit.com/r/haskell/comments/xao5fy/announcing_practical_haskell_bits_initiative/" target="_blank">Practical Haskell Bits</a>.<a href="#fnref15" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn16" role="doc-endnote"><p>“Haskell is the world’s finest imperative programming language” famous quote, probably originated in this paper <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/mark.pdf?from=https%3A%2F%2Fresearch.microsoft.com%2Fen-us%2Fum%2Fpeople%2Fsimonpj%2Fpapers%2Fmarktoberdorf%2Fmark.pdf" target="_blank">Simon Peyton Jones, Tackling the Awkward Squad</a><a href="#fnref16" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn17" role="doc-endnote"><p>I implemented a proprietary Haskell logger library at my work. It is interesting to think about what we want to observe when we FP. Standard logger libraries for, say, Java are “object-centric” and will allow configuration options based on which class spilled out info into the log. The library I implemented is “data-centric” and allows you to configure what data you want to see. FP is about clear inputs and outputs after all.<a href="#fnref17" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn18" role="doc-endnote"><p>I rewrote the side note and changed the exercises type based on comments from <a href="https://www.reddit.com/r/haskell/comments/yonk01/comment/ivk8pgr/?utm_source=share&amp;utm_medium=web2x&amp;context=3" target="_blank">u/kindaro</a> on reddit. Thanks!<a href="#fnref18" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn19" role="doc-endnote"><p>Exercise 2: You will need “elementary” implementation for <code>length</code> (<code>length [] = 0; length (x:xs) = 1 + length xs</code>). Recursion step becomes induction step, the conservation law is an equation, you prove it by writing bunch of equations you get from the program itself.<br />
Exercise 3: Liquid Haskell annotation just spells out the law: <code>{-@ partitionEithers :: xs:[Either a b] -&gt; {ys: ([a], [b])  | (len xs) = (len (fst ys)) + (len (snd ys))} @-}</code>. As of this writing, you can try it online <a href="http://goto.ucsd.edu:8090/index.html#?demo=lenMapReduce.hs" target="_blank">here</a>.<br />
Here is a page with more information: <a href="https://github.com/rpeszek/experiments/tree/master/partition-eithers-excercise" target="_blank">partial solutions</a>.<a href="#fnref19" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn20" role="doc-endnote"><p>The association of pragmatism and empirical process is not unique to programmers, e.g. if you google “pragmatists vs formalists” today you will probably get a link to this quote “Formalism follows deductive approach whereas pragmatism applies empirical approach” from this legal philosophy paper: <a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3423036" target="_blank">Formalism vs. Pragmatism in Legal Philosophy</a><a href="#fnref20" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn21" role="doc-endnote"><p>IMO, some experience with the formal is needed to better understand limitations of empirical. “Program testing can be used to show the presence of bugs, but never to show their absence!” is a projection of that understanding onto programming.<a href="#fnref21" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn22" role="doc-endnote"><p>See <a href="2022-08-30-code-cognitiveload.html#extraneous-nature-of-abstraction" target="_blank">Extraneous nature of abstraction</a>.<a href="#fnref22" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn23" role="doc-endnote"><p>E.g. rewrite rules in Haskell.<a href="#fnref23" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn24" role="doc-endnote"><p>I dig a deep hole by using <a href="2022-08-30-code-cognitiveload.html#germane-and-intrinsic-load-of-fp" target="_blank">Free Monad</a> as example of such a line in my previous post.<a href="#fnref24" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn25" role="doc-endnote"><p>I am not a logician, I vaguely remember some versions of logic that allow multiple moralities, in particular one where there was <em>lax</em> in addition to <em>false</em>. Few (even) mathematicians will know these.<a href="#fnref25" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn26" role="doc-endnote"><p>See my posts about maybe and alternative overuse <a href="../tags/patterns-of-erroneous-code.html" target="_blank">patterns-of-erroneous-code</a><a href="#fnref26" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn27" role="doc-endnote"><p>E.g. browsing a random website with developer tools console opened and observing all the red is, IMO, a signal of something different going on, e.g. things like <em>omission neglect</em> (psychological concept loosely described by the phrase: out of sight out of mind) should be considered.<a href="#fnref27" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn28" role="doc-endnote"><p>See <a href="2022-08-30-code-cognitiveload.html#extraneous-nature-of-abstraction" target="_blank">Extraneous nature of abstraction</a> and its footnotes.<a href="#fnref28" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn29" role="doc-endnote"><p>I have discussed RNT (repetitive negative thinking) in my <a href="2022-08-30-code-cognitiveload.html" target="_blank">previous post</a> in my previous post.<a href="#fnref29" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn30" role="doc-endnote"><p>Concurrency in particular is a good example, deductive tools like TLA+ exist.<a href="#fnref30" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn31" role="doc-endnote"><p>It does seem that there is a more general disagreement about the limitations of empirical reasoning. Few people think about physics as a collection of simplified mathematical models that only approximate reality. Even some famous physicists (e.g. Niels Bohr, if I remember correctly my reading about it) have apparently thought otherwise. Few people look deeply for bias in biological studies. <em>Hypotheses non fingo</em> is a rare position. 100% test coverage is in the “we tested it and it is correct” category.<a href="#fnref31" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn32" role="doc-endnote"><p>E.g. consider performance improvements that can be made to my <code>partitionEithers</code>. Note, Haskell code that is implemented using constructors and pattern matching only does not take advantage of rewrite rules that are already in place for combinators like <code>foldr</code>. Compare my code to the source of <a href="https://hackage.haskell.org/package/base-4.17.0.0/docs/src/Data.Either.html#partitionEithers" target="_blank"><code>paritionEithers</code></a> in <code>Data.Either</code>.<a href="#fnref32" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn33" role="doc-endnote"><p>If you use a PL with a proof assistant feature and write proofs for your programs, a refactoring will have additional cost of rethinking the proofs. This could be not an issue with Liquid Haskell, which does the proofs for you, but still may require extra work if the logic solver needs extra help. Consider refactoring my <code>partitionEithers</code> (annotated as before) to use <code>foldr</code> (which is incidentally what Base implementation uses), Liquid Haskell will tell you that your code is unsafe. It is interesting to note that QuickCheck-like property tests maintain very well.<a href="#fnref33" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn34" role="doc-endnote"><p>I will partially repeat a list I came up with in my last post. Example of non-symmetric equals is <code>java.sql.Timestamp</code> used with <code>java.sql.Date</code> or <code>java.util.Date</code>, these remain used as standard JDBC mappings for DB columns, the usage will show no deprecation warning. I wrote a blog series about <a href="../tags/TypeScript-Notes.html" target="_blank">TypeScript Types</a> and ended up presenting a lot of complexities and gotchas that probably surprise any TS developer.<br />
How do Hibernate users prevent this <a href="http://rpeszek.blogspot.com/2014/08/i-dont-like-hibernategrails-part-2.html" target="_blank">concurrency issue</a>?<br />
I remember Grails as a land mine of issues, I wrote an 11 part <a href="http://rpeszek.blogspot.com/2014/10/i-dont-like-hibernategrails-part-11_31.html" target="_blank">blog series</a> about it back in 2014.<br />
Returning to Java, its implementation of <code>Optional</code> is often <a href="https://www.sitepoint.com/how-optional-breaks-the-monad-laws-and-why-it-matters/" target="_blank">criticized</a>. Java array variance is fundamentally broken. Java Streams are broken as well: if you execute a stream twice the second attempt will fail. This (interesting take on referential transparency 🙂) is bound to create interesting intermittent issues.<br />
I wrote a <a href="2022-08-30-code-cognitiveload.html#fnref19" target="_blank">footnote</a> in the previous post about Haskell ecosystem issues as well.<a href="#fnref34" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn35" role="doc-endnote"><p>On the subject of training, how do you teach, say, equational reasoning using any of the mainstream PLs? Is equational reasoning something that a software engineer will never need? I do use it.<a href="#fnref35" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn36" role="doc-endnote"><p>The main reason typically presented for this is: improved ability to make and measure observations. In programming, we witness things going south in front of our eyes, we typically do not need to wait for a new testing technology.<a href="#fnref36" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn37" role="doc-endnote"><p>In my last post I included a trivia about Turing’s original paper having several bugs and his adviser’s (Alonso Church’s) work being bug free. This is also related to the imperative vs denotative discussion. Here is the link to that footnote: <a href="2022-08-30-code-cognitiveload.html#fn11" target="_blank">Turing</a>. It should make us feel better about ourselves if Turing himself wrote bugs.<a href="#fnref37" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn38" role="doc-endnote"><p>The job market for functional programming jobs is, frankly, dismal. At the same time, languages like Haskell and Rust had topped the weekend use stats based on stackoverflow surveys. Repeating some of what I wrote <a href="2022-03-13-ts-types-part6.html#about-simplicity" target="_blank">here</a>: Haskell was firmly in the first position for the stackoverflow weekend use statistics for several years. Here is one link: <a href="https://stackoverflow.blog/2017/02/07/what-programming-languages-weekends/" target="_blank">2017</a>. In <a href="https://stackoverflow.blog/2019/10/28/research-update-coding-on-the-weekends/" target="_blank">2019</a> Rust moved ahead of Haskell. The job ranking (based on the UK’s <a href="https://www.itjobswatch.co.uk/jobs/uk/haskell.do" target="_blank">IT Jobs Watch</a>) put Haskell at 932 as of 2022/02/06. Haskell moved ahead of COBOL in that ranking in 2017. This ranking is possibly exaggerated too, lots of jobs list Haskell as good to have but will have you code in PHP. This bias exist for any language but is stronger for something like Haskell than say COBOL.<a href="#fnref38" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn39" role="doc-endnote"><p>Abductive reasoning as a possibly relevant topic was pointed out by <a href="https://discourse.haskell.org/t/lets-agree-to-be-different-on-empirical-and-deductive-nature-of-coding/5276/7?u=rpeszek" target="_blank">hellwolf</a><a href="#fnref39" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

    </section>
</article>
]]></summary>
</entry>
<entry>
    <title>Cognitive Loads in Programming</title>
    <link href="https://rpeszek.github.io//posts/2022-08-30-code-cognitiveload.html" />
    <id>https://rpeszek.github.io//posts/2022-08-30-code-cognitiveload.html</id>
    <published>2022-08-30T00:00:00Z</published>
    <updated>2022-08-30T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on August 30, 2022
        
        
          <br /> Last Modified on Nov 07, 2022
        
        
        <div class="changelog"> <div>Revision History: <ul> <li> (2022.11.07) Draft warning removed, minor changes only </li> </ul> </div></div>
        
        
        <div class="info">Tags: <a title="All pages tagged 'patterns-of-erroneous-code'." href="../tags/patterns-of-erroneous-code.html">patterns-of-erroneous-code</a>, <a title="All pages tagged 'communication'." href="../tags/communication.html">communication</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <div class="toc"><div class="header">Table of Contents</div>
<ul>
<li><a href="#that-dreaded-yaml">That dreaded YAML</a></li>
<li><a href="#cognitive-psychology">Cognitive psychology</a></li>
<li><a href="#easy-vs-simple">Easy vs Simple</a></li>
<li><a href="#extraneous-loads-that-grow">Extraneous loads that grow</a></li>
<li><a href="#bugs-and-metacognition">Bugs and metacognition</a></li>
<li><a href="#extraneous-nature-of-abstraction">Extraneous nature of abstraction</a></li>
<li><a href="#germane-and-intrinsic-load-of-fp">Germane and intrinsic load of FP</a></li>
<li><a href="#post-summary">Post Summary</a></li>
<li><a href="#there-is-much-more-to-it">There is much more to it</a></li>
</ul>
</div>
<blockquote>
<blockquote>
<p>  <em>“My brain hurts”, a quote from a code review</em></p>
</blockquote>
</blockquote>
<p>This long post presents programming in a different light than what is commonly considered. We will look at cognitive aspects of interacting with code.</p>
<p>We will examine cognitive effort that goes into the implementation and cognitive loads on these poor souls who need to work on that code later. We will consider the programming language, its libraries, and implemented programs as <em>instructional materials</em>. We will view the developer as both an <em>instructional designer</em> and a <em>learner</em>. We will think about bugs as cognitive overload and a missed learning opportunity. We will discuss the cognitive impact of abstractions, types, and programming principles.</p>
<p>Cognitive load of working with code is rarely considered in actual project work. We ask “How long will it take?” (in fibonacci numbers, of course), we do not ask “How will it impact the overall complexity?”.<br />
I had quite a few eye opening moments when thinking about these topics. This is the main reason I decided to write and share my thoughts. This post will be a high level rant discussing programming across the industry spectrum from JavaScript to Haskell. It is written as a set of loose notes about various cognitive aspects related to working with code. The main goals are to:</p>
<ul>
<li>show how considering <em>cognitive loads</em> in context of programming projects provides valuable insights</li>
<li>present some useful terminology for reasoning about code complexity.</li>
</ul>
<p>I will try to explain psychological terminology but this post assumes readers’ (high level) familiarity with concepts of FP and OOP.</p>
<p>My pet peeve is identifying specific <a href="../tags/patterns-of-erroneous-code.html" target="_blank">patterns of erroneous code</a> and what could be causing them, there is a human factor and a technical part to these patterns.<br />
Mental processes involved in writing code are such a fascinating and broad subject. I am taking only a very narrow path through it.<br />
I am planning another high level post to discuss programming from a different but relevant angle, it will be about empirical and deductive aspects of working with code. I believe these 2 aspects impact our cognitive loads in interesting ways. So, this post will focus on cognitive challenges caused by code. The next post will focus more on the human aspect.</p>
<p>This post reflects on my personal observations accumulated over 27 years of professional programming work, augmented by a few years of academic teaching.<br />
I am not a psychologist, these are observations of a coder.</p>
<h2 id="that-dreaded-yaml">That dreaded YAML</h2>
<p>I am perusing thousands of lines in Infrastructure as Code (IAC) yaml files. I am looking at an already refactored and improved version. It is a lot of templated <em>YAML</em> of k8s configuration at my work. The underlying reason for the complexity is the PL itself<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>. Did the refactor break things? Of course it did. Complexity has consequences. With some effort, the issues were fixed. This is how things are, there is nothing we can do about it. There isn’t?</p>
<p>I want to contrast <em>YAML</em> with a configuration language called <a href="https://dhall-lang.org/" target="_blank">Dhall</a> (one of my favorites). To use <em>Dhall</em> you may need to adjust to a Haskell-like syntax, maybe learn a few new concepts (like ADTs), think about configuration that uses lambda expressions. The return on the investment are Dhall safety features. Dhall even makes the process of refactoring safe, you can compare the previous configuration against the new and Dhall will tell you if both are equivalent or why not.</p>
<p><em>Dhall</em> and <em>YAML</em> come with very different cognitive challenges.</p>
<h2 id="cognitive-psychology">Cognitive psychology</h2>
<p>Cognitive load theory defines cognitive load as the amount of information that working memory holds at one time. The idea is that the human brain is limited in that capacity. Psychologists have identified the load to be about 3-5<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> “units of information” (also called “chunks”). This space appears to be quite limited.<br />
I imagine the magic number is small in programming. However, I expect it to vary between individuals.<br />
If we can load only a limited number of “chunks” into working memory, how big can these chunks be? The answer is interesting: it seems that it does not matter!<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a><br />
In some situations, the magic number appears to be 3 (the concept + 2 constituent chunks)<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>.<br />
Notice, it would be hard to enumerate chunks involved in a classic imperative program, but that number will be &gt;&gt; 5.</p>
<p>The idea of decomposing the program into fewer (but bigger) “chunks” that interact in a clear way has been around for as long as I can remember. We will examine this idea in terms of <em>Cognitive Load Theory</em>.</p>
<p>Cognitive Load Theory is concerned with <em>instructional design</em> and improving how <em>information is presented</em> to a <em>learner</em>. Controlling the learner’s cognitive loads is an essential part of this work.</p>
<p>Continuous learning is a part of what programmers do, but implementing and modifying project code is by far the biggest cognitive effort that programmers face.<br />
I look at this as: the code itself is a very important <em>instructional material</em>, programmers are <em>learners</em> and <em>instructional designers</em> at the same time.<br />
Programs are where the <em>presentation of information</em> happens. The concepts and findings of cognitive load theory seem still relevant after this adjustment.</p>
<p>Cognitive psychology considers 3 types of cognitive load: <em>Intrinsic, Extraneous, Germane</em>. All are related to <em>information presentation</em> and we will think about them in the context of code.</p>
<ul>
<li><p><em>Intrinsic cognitive load</em> is the inherent level of difficulty associated with a specific (instructional) topic. Thinking about code, requirements are a good choice for a topic. A rough moral equivalent known to software developers is <em>essential complexity</em> (things are complex because they are, to reduce this load requirements would need to change).</p></li>
<li><p><em>Extraneous cognitive load</em> is generated by the manner in which information is presented to learners and is under the control of instructional designers. This term is often used to describe unnecessary (artificially induced) cognitive load. Thinking about code, a rough moral equivalent of high extraneous load is <em>accidental complexity</em><a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a> (things are complex because the program made it so).</p></li>
<li><p>Germane cognitive load refers to the work that is put into constructing a long-lasting store of knowledge or schema. Schema is a pattern of thought or behavior that organizes categories of information and the relationships among them. Psychologists also use the term <em>“chunk”</em> and schema construction is the process of creating these chunks in memory.<br />
Thinking about code, this roughly translates to using <em>abstractions</em>, higher level concepts, types, programming principles. An <em>OO</em> programmer may try to define intuitive object hierarchies, employ design patterns to model the business domain. An FP-er may use denotational<a href="#fn6" class="footnote-ref" id="fnref6" role="doc-noteref"><sup>6</sup></a> approach, look at how things compose (think about categories), design DSLs, blue-print the design using types…</p></li>
</ul>
<blockquote>
<p>  <em>Cognitive load theory thesis is about reducing extraneous cognitive load redirecting it towards germane load.</em></p>
</blockquote>
<p>Cognitive theory considers intrinsic load to be not movable, obviously requirements can be changed.</p>
<p>I need to emphasize that the <em>information presentation</em> under consideration facilitates understanding of the code itself and not so much the concepts (e.g. abstractions) used to create it. Knowledge of these concepts is a prerequisite. Prerequisites are an important caveat and one that ends up being contentious.</p>
<p> <div class="side-note"><strong>Prerequisites:</strong> Working on a project code will reinforce knowledge of programming concepts (psychologists call something similar a <a href="https://en.wikipedia.org/wiki/Worked-example_effect" target="_blank">worked-example effect</a>) but, for a working programmer, learning new concepts ideally needs to happen outside of project work. In reality, there is no time for this. Also, available programming concepts are limited not only by what the developer and the team know, but also by what is supported by the PL (programming language). Developer backgrounds and what is supported in a PL vary a great deal. Thus, the list of prerequisites that can go into a programming project is limited.  </div></p>
<p>This sets the stage for what I want to discuss, but before continuing let me briefly review a <strong><em>few more relevant concepts.</em></strong></p>
<p><em>Cognitive overload</em> happens when working memory is overwhelmed by the 3 cognitive loads we have described, IMO, bugs are evidence of a cognitive overload.<br />
However, psychology is not a simple arithmetic, some programmers learn how to process large cognitive loads sequentially, a few chunks of information at the time and get good at it. However, high cognitive loads will overwhelm even the most diligent among us, There is even a trivial combinatorial complexity to this (does working memory go through a binomial number reloads?).</p>
<p>I wanted to use <em>cognitive debt</em> in the title, intending it as a pun on “technical debt” because I am interested in discussing negative impacts on the team’s ability to understand and reason about the code. However, this term turns out to have a clinical meaning and I decided against using it.<br />
<em>Cognitive debt</em> is a psychological term associated with <em>repetitive negative thinking (RNT)</em>. <em>Cognitive debt</em> and RNT are hypothesized to have some very negative health consequences that can lead to depression or even dementia. RNT is described as</p>
<blockquote>
<p>  <em>“excessive and repetitive thinking about current concerns, problems, past experiences or worries about the future”</em></p>
</blockquote>
<p>I do not claim to know a lot about clinical psychology but the definition clearly is very relevant to programmers and could partially explain why programmers are often unhappy<a href="#fn7" class="footnote-ref" id="fnref7" role="doc-noteref"><sup>7</sup></a>, or why programming discussion groups are often very negative.<br />
Sadly, RNT seems to be a condition that really good programmers are likely to experience. Good programmers think about rainy day scenarios, notice design flaws, can anticipate program issues… My pet peeve, <a href="../tags/patterns-of-erroneous-code.html" target="_blank">patterns of erroneous code</a>, is an RNT. It seems important that we talk about RNT.</p>
<p>You may want to think about <em>working memory</em> and <em>cognitive loads</em> as something akin to <em>RAM</em> in a computer.<br />
What is the equivalent of a CPU cost? In this post I use <em>cognitive effort</em>, the usage of this term is not very consistent<a href="#fn8" class="footnote-ref" id="fnref8" role="doc-noteref"><sup>8</sup></a> in the literature.</p>
<p>These were cliff notes written by a non expert. There are many tricky and relevant bits like <em>information retrieval from long term memory</em>. Things I am progressively less and less competent to describe in psychological context.</p>
<h2 id="easy-vs-simple">Easy vs Simple</h2>
<p>If you have looked at my <a href="../tags/TypeScript-Notes.html" target="_blank">TypeScript types</a> series, you have seen me <a href="2022-03-13-ts-types-part6.html#about-simplicity" target="_blank">write about it already</a>. I do not claim that my definitions are the only correct way that these terms should be interpreted. However, I have seen other programmers use a similar disambiguation so I am including it here.</p>
<p>I consider the terms simple and easy to have a different meaning.<br />
Easy: A low barrier to entry (e.g. easy programming concepts). Hard is the opposite of easy and implies a high learning curve.<br />
Simple: Low effort to <em>correctly</em> reason about (e.g. code written using learned concepts). Complex is the opposite of simple (e.g. code that is hard to understand). The term “arbitrary complexity” fits this definition very well.</p>
<p>Easy means fewer prerequisites and implies low germane load, hard means many prerequisites.<br />
Simple means low extraneous load, complex means high extraneous load.</p>
<p>This differentiation could also be expressed as:</p>
<blockquote>
<p>  <em>Easy means low cost of creation, simple means low cost of consumption</em></p>
</blockquote>
<p>except, in this post my interest is the cognitive effort only not the total cost<a href="#fn9" class="footnote-ref" id="fnref9" role="doc-noteref"><sup>9</sup></a>.</p>
<p>Achieving <em>simplicity</em> on a larger project is not <em>easy</em>. Easy does not scale well. There appears to be no free lunch, cognitive load needs to be somewhere. My big preference is trying to achieve <em>hard and simple</em> rather than <em>easy and complex</em>. In other words, I prefer to spend my cognitive bandwidth on germane load over extraneous load. This, I am pleased to note, is aligned with cognitive psychology.</p>
<p>Recall the advice from cognitive psychologists is to reduce extraneous load redirecting it towards germane load. This translates to:</p>
<blockquote>
<p>  <em>Move from complex to hard</em></p>
</blockquote>
<p>An interesting way to look at easy vs simple is to think about a creative process like writing a book. Easy to write is clearly very different from simple to read. In programming these two are bundled together, a program created by one developer needs to be consumed by another dev who needs to modify it or interact with it. The term “readable code” comes to mind. I consider it different from simple. E.g. readable code does not mean no subtle bugs. Message is readable if you know what it conveys, but what it conveys could be complex or even misleading.</p>
<p>IMO, the popularity of easy and the unpopularity of simple are a systemic problem in today’s programming and elsewhere.</p>
<p>Next section discusses examples of code which was intended to be easy and ended up complex.</p>
<blockquote>
<p>  <em>“Simplicity is a great virtue but it requires hard work to achieve it and education to appreciate it. And to make matters worse: complexity sells better.”</em> Dijkstra</p>
</blockquote>
<h2 id="extraneous-loads-that-grow">Extraneous loads that grow</h2>
<p>What was the most complex code you’ve worked on?<br />
I can think about a number of contenders, but my answer will be very unimpressive: I had to maintain a web page (just one page), it was implemented using Java Struts 1. This code used no advanced concepts, all ingredients were easy: control statements, instance variables, lots of protected methods with typically no arguments and void returns that read and set the instance variables.<br />
The Java class behind it had about 200 mutating instance variables. Changing the order of 2 lines in this code was almost guaranteed to create an (often intermittent) bug, it was hard to even make sure that variables were set before they were read.<br />
This code became infamous for its complexity very fast. Interestingly, Struts were blamed, not the needless overuse of mutable state.<br />
<em>I want you to channel your inner psychologist and answer this question: what is going to happen when a new functionality is added to a Java class with 200 instance variables? Right, I agree, we will have 201 instance variables.</em><br />
This piece of code was eventually refactored. If I remember correctly, about 12 instance variables were kept, they were actually needed by Struts.</p>
<p>This experience seems to me a good example of a big extraneous load, I had to deal with a load of 200 coupled “chunks”.<br />
Let’s think about such code as an instructional material. I can attest, it was virtually impossible to even know what this code is supposed to do from looking at it.<br />
Ability to program using clear inputs and outputs (rather than void methods with no input parameters) requires a learning effort, I submit to you that this prerequisite is easier than the cognitive effort of maintaining such code.<br />
Thinking about this as instructional material, clear inputs and outputs are great learning objectives. You know the app if you understand its inputs and outputs.</p>
<p> <div class="side-note">Maintaining messy code can be stressful. Fortunately, projects like these become “infamous” very fast and you get moral support from other team members. That really helps. Be a source of such support for your teammates as well. Few words of encouragement and acknowledgment of the hardship go a long way. Also, the information will slowly percolate up and the management may become more receptive to accept the cost of a big refactor or even a complete rewrite. This is what happened in my Java Struts example.  </div></p>
<p>My second example is something that happened more recently. I worked on reimplementing a JS application. It was one of these apps that can be described as: <em>was easy to write, is hard to maintain or understand</em>. I am sure very few readers will be surprised by the existence of a hard to maintain JS application, but let’s put talking about this aspect aside. Is writing “easy” code the same as generating excessive cognitive load for the maintainers? I think it typically is, it is not that hard to incrementally develop a non penetrable maze. Maintaining some code structure to manage the cognitive load is not “easy”.<br />
The new version is still close to JS (it uses TypeScript and vanilla React) but tries to enforce these 3 principles: <a href="2022-03-13-ts-types-part6.html#referential-transparency-purity-and-explicit-types" target="_blank">referential transparency</a>, <a href="2022-03-13-ts-types-part6.html#about-clarity" target="_blank">clear, explicit</a> types that also <a href="2021-12-24-ts-types-part2.html#types-as-documentation" target="_blank">work as documentation</a>, and <em>async/await</em> abstraction to avoid callback hell.<br />
Referential transparency is an interesting dichotomy. Experiencing different results every time the code is executed typically causes surprise, in my experience developers rarely think about this during implementation. Thus, the code may feel weird and opinionated (e.g. React components avoid using hooks) but it remains accessible.</p>
<blockquote>
<p>  <em>IMO, high quality code shifts cognitive load from maintainer to implementer</em></p>
</blockquote>
<p>This works great even if both are the same person.<br />
Let’s consider the new JS app as an instructional material. Referential transparency creates learning objectives (inputs and outputs can be learned if outputs are predictable) while explicit types are an instructional material in itself (a blueprint). The biggest prerequisite for the implementers was knowledge about what to avoid.</p>
<p>Besides some common sense principles (feel free to add more), what else can we do to control extraneous load? Things are about to get more tricky.<br />
Human cognitive load is limited but we can do abstract reasoning. It is simpler for us to deal with a few generalized abstractions than with a multiplicity of concretes<a href="#fn10" class="footnote-ref" id="fnref10" role="doc-noteref"><sup>10</sup></a>. And, as we know, abstractions are a better use of our working memory <em>chunk</em> space.<br />
This suggests exploring the space of programming abstractions. Unfortunately, programming abstractions are nontrivial. That makes them hard to learn, but what is worse is that developers and language designers sometimes (if not often) mess them up. Instead of decreasing, this increases (or even explodes) the cognitive load. We will explore this topic in <a href="#extraneous-nature-of-abstraction">Extraneous nature of abstraction</a>.</p>
<p>Types are, obviously, an important tool in controlling cognitive load. Types <em>offload</em> many code verification tasks from the developer. This is significant, developers can ignore a potentially high extraneous load of a program by trusting its type. As I already mentioned, types can be an <em>instructional material</em>, a blueprint. Using a type checker can, in itself, be an interactive learning process (e.g. using REPL to ask type questions about the code).<br />
However, types are subject to similar limitations as abstractions: a learning curve, PL limitations, correctness issues (only, we call it <em>soundness</em> if types are involved).</p>
<p>Reducing cognitive load using abstractions and types is doable but requires navigating some tricky waters.</p>
<p> <div class="side-note"><strong>In a unicorn universe,</strong> projects are not allowed to exceed certain thresholds of cognitive load. When the threshold is reached abstractions that lower the load are identified, learned, and respectfully applied. If that is not possible, requirements are reexamined. Unicorn managers are automatically beatified. 🦄  </div></p>
<h2 id="bugs-and-metacognition">Bugs and metacognition</h2>
<p>Let’s define a bug as an unintended program defect. That removes all the temporary hacks from consideration. But it is the programmer’s job to figure these things out. A bug implies some issue in the mental process.</p>
<p>I consider cognitive overload to be the main cause of bugs. <em>Metacognition</em> is an important concept in cognitive psychology. It is about knowing strengths and weaknesses in our own cognitive process.<br />
I started analyzing and recording all defects I encounter at my work. My goal is to understand better what has caused and what could have prevented each issue. My records suggest that bugs <em>uncover</em> extraneous complexity. In other words, it is a good idea to ask this question: What is the underlying complexity that caused the developer to create this bug? The idea is to learn from bugs.<br />
Types, obviously, can be very helpful in bug prevention. Programmers who start using a PL with powerful types (e.g. Idris, Haskell) experience this first hand: a lot of compilation errors, many uncovering an issue in the program. Notice, this is a very interactive process and an interactive learning experience in which developers can observe how and why they failed. Developers also observe what PL features prevent the bug from escaping.</p>
<blockquote>
<p>  <em>Programming is an interactive process of finding and fixing bugs.<br />
  IMO, programming should be an interactive process of identifying and resolving the underlying causes of bugs.</em></p>
</blockquote>
<p>“Insanity is doing the same thing over and over and expecting different results”. I promise you 2 things: When you start analyzing bugs, you will start seeing patterns (similar to <a href="../tags/patterns-of-erroneous-code.html" target="_blank">patterns of erroneous code</a>). Unfortunately, you will likely have problems in communicating these patterns to developers who do not go through a similar process. I found that a code review session showing the same issue in a few places works better than trying to explain this without a concrete context (oops).</p>
<p>How about typos, trivial overlooks that are sometimes so hard to spot? That mysterious brain of ours is good at creating these. A great reading on this, in the context of (non-programming) typos, is <a href="https://www.wired.com/2014/08/wuwt-typos/" target="_blank">WUWT, Why It’s So Hard to Catch Your Own Typos</a>.<br />
Human brain has an ability to fill in gaps, self-correct things. Human brain is better at focusing on high level ideas and is perfectly happy skipping over minute details. This characteristic seems even stronger if we are on board with the big idea, and it seems fair to assume that programmers are on board with the features they are implementing. The main point is that our brain is not well designed to work at the level of statements and lexical tokens, it wants to work on big picture items.</p>
<p> <div class="side-note">Side note: This line of thought could also partially explain why programmers seem to be at home in the code they wrote even if other programmers consider it a complete mess. Sometimes just changing font or background color allows us to spot issues we have overlooked before. Our perception changes if what we interact with feels foreign (interestingly this should increase the cognitive load). It appears that some mental reset is sometimes needed.  </div></p>
<p>Error proneness of programming at the level of PL statements is also consistent with the cognitive load theory. At this level a programmer needs to consider a multitude of details, most likely overwhelming the working memory limits.<br />
An interesting piece of trivia is that Turing’s original paper (the one about universal machines and halting problem) had several bugs in it. <em>If Turing could not get it right, what chance do we have?</em><a href="#fn11" class="footnote-ref" id="fnref11" role="doc-noteref"><sup>11</sup></a></p>
<p>Static compilation can prevent a lot of trivial errors and hopefully the prevented list will grow, but that list is not exhaustive.</p>
<p><strong>Section Summary</strong></p>
<p>My first point is that programmers should start considering cognitive aspects when thinking about bugs.</p>
<p>What is that we do when we discover a bug? We write a test, right? Does this reduce the cognitive load? Of course it does not. IMO, it is more important to spend time on some intro- and retrospection and look for ways to lower the extraneous load or build some type safety. If that is not possible, improving test coverage becomes important. I want to learn from bugs. <em>Fixing bugs is the least important part of the process.</em> I should also mention that this is, unfortunately, a <em>repetitive negative thinking</em> territory.</p>
<p>Here is an example that keeps popping into my mind when thinking about trivial errors. I have seen many stack overflow errors in my life, I have seen only 2 or 3 since I moved to Haskell but they were not easy to find. They all were caused by Haskell allowing this lambda expression:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="kw">let</span> blah <span class="ot">=</span> blah </span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="kw">in</span> blah</span></code></pre></div>
<p>This to me is a good example of extraneous complexity that could be prevented by the compiler. Many PLs (e.g. anything in ML groups like OCaml, Reason will not allow such code). Here is a relevant discussion on reddit: <a href="https://www.reddit.com/r/haskell/comments/lxnbrl/ghc_proposal_norecursivelet_prevent_accidental/" target="_blank">NoRecursiveLet</a>. Thinking about reducing extraneous load could impact such discussion (in this case, by supporting the proposal).</p>
<p>My second point is the recurring one, types and abstractions can play a big role in reducing bugs. Hopefully types and abstractions themselves are bug free!</p>
<p> <div class="side-note"><strong>There is an alien civilization</strong> of programmers who analyze their bugs, Alien PL designers consider these analyses to decide which features are in and which are out. Their PLs do not even have strings. You can do a lot of harm with just strings.  </div></p>
<h2 id="extraneous-nature-of-abstraction">Extraneous nature of abstraction</h2>
<p>Summary of previous sections: Our cognitive load is limited but we are capable of abstract reasoning and can work with big <em>chunks</em> of knowledge. Abstractions seem like our best hope in reducing the overall code complexity. But …there are a few caveats.<br />
Programming abstractions are known for their germane load (for being <em>hard</em> to learn and for being less straightforward than imperative) but not so much for their extraneous nature (for being needlessly <em>complex</em>), the second aspect is much more interesting so let’s discuss it.</p>
<p><strong>Poorly implemented abstractions</strong></p>
<p>You spotted an intermittent malfunction in a code you maintain. Luckily, you see only one commit in recent history and you have a strong hunch something is wrong with that commit. Only some 50 code changes. The one that caused the issue is: <code>var1 == var2</code> changed to <code>var2 == var1</code>. Would you be able to spot it? I call this type of issue a “gotcha”.<br />
How about: your <em>finder</em> function seems to be not finding stuff, only that sounds too far fetched, the function looks correct, so you just ignore this as a possible explanation. The underlying issue is that sometimes <code>x =! x</code> and you have used an equality check to find things.</p>
<p>I like to think about this paraphrasing Gimli:</p>
<blockquote>
<p>  <em>“Computation Laws are upon you, whether you would risk them or not.”</em></p>
</blockquote>
<p>Equality is an example of an abstraction developers implement and use, but not think much about. However, the list of surprising behaviors like these is quite long affecting all kinds of abstractions. Gochas create chaos in the cognitive process. Gotchas often become mystery bugs and are resolved using workarounds.<br />
For abstractions to work as a cognitive load reducer, they need to be treated seriously by the implementer.</p>
<p>Developers I talked to often responded to such examples by saying something like: “This is just bad code, whoever implemented it should have been more careful”. Except, I can point to examples in standard libraries of popular mainstream PLs or popular frameworks<a href="#fn12" class="footnote-ref" id="fnref12" role="doc-noteref"><sup>12</sup></a>. The issues come with no deprecation warning and, if documented, are considered a ‘feature’.<br />
Are questions like “does a developer have a fighting chance of troubleshooting this feature?” even being asked?</p>
<p>_side_note_start<br />
Poorly implemented abstractions could have to do with the empirical mindset that dominates the programming community. Programmers are typically inductive learners (learn from examples and generalize from examples). It is probably hard to think about counter examples when learning from examples.  </div></p>
<p><strong>Abstractions themselves causing cognitive issues</strong></p>
<p>OOP creates a very high cognitive load, to a point that even compiler writers mess it up all the time<a href="#fn13" class="footnote-ref" id="fnref13" role="doc-noteref"><sup>13</sup></a>. I started my programming career as an OOP enthusiast and evangelist. OO programming has an appeal of simplicity and I was seduced by it for many years. It took me a long time to realize that OOP is not simple at all. Let’s talk OOP a little. Pick random training. You will probably learn that <em>Cat</em> <em>is a</em>n <em>Animal</em> and that everything is intuitive.<br />
You will not learn if any of these less obvious are (or should be) true:<br />
  function accepting a <em>Cat</em> <em>is a</em> function accepting an <em>Animal</em><br />
  array of <em>Cats</em> <em>is a</em>n array of <em>Animals</em><a href="#fn14" class="footnote-ref" id="fnref14" role="doc-noteref"><sup>14</sup></a><br />
  function with no parameters <em>is a</em> function with one parameter<a href="#fn15" class="footnote-ref" id="fnref15" role="doc-noteref"><sup>15</sup></a>.<br />
You will not learn about reduced type safety that comes with widening to a superclass<a href="#fn16" class="footnote-ref" id="fnref16" role="doc-noteref"><sup>16</sup></a>. I don’t even want to start on subtyping gotchas of variant (union and sum) types. OOP is approachable only because we hide the complex bits from the learners<a href="#fn17" class="footnote-ref" id="fnref17" role="doc-noteref"><sup>17</sup></a>. A relevant psychological concept is a cognitive bias called <em>framing effect</em>.</p>
<p>The concept of exception (i.e. <code>throw</code> and <code>catch</code> game) is another example of a risky complexity that impacts even Haskell<a href="#fn18" class="footnote-ref" id="fnref18" role="doc-noteref"><sup>18</sup></a>.<br />
Types can reduce cognitive load of understanding the code, except exceptions provide a very accessible and virally used way to bypass the types. Other “bottom” types like <code>null</code> are in the same boat. In my experience, many developers turn a blind eye on error handling in general. This seems akin to <em>omission neglect</em> (psychological concept loosely described by this popular phrase: out of sight out of mind) and some <em>optimism bias</em> (focus on sunny day scenarios only).<br />
I really like what Rust has done in this regard, you can <em>panic</em> but it is hard to recover if you do, otherwise errors are handled in an <code>Either</code>-like sum type called <code>Result</code>.<br />
Hopefully we will see more of this pragmatic approach in future PLs.</p>
<p>You may notice that the examples of <em>gotchas</em> I am coming up with have something in common. These issues can be classified under: <em>not trustworthy types</em>. Misleading types will confuse any developer, that includes developers who work in dynamically typed languages and may not think about types explicitly.<br />
<em>We think in types more than we realize.</em></p>
<p>Are there any “gotcha” free environments? Haskell comes close but is not perfect<a href="#fn19" class="footnote-ref" id="fnref19" role="doc-noteref"><sup>19</sup></a>. Proof assistants like Idris come to mind, you get very sound abstractions, and these can even verify totality. That is kinda interesting, let’s pause for a bit here… Consider the levels of abstraction used in proof assistants. It appears that our brain needs something at the level of a dependently typed lambda calculus to work correctly<a href="#fn20" class="footnote-ref" id="fnref20" role="doc-noteref"><sup>20</sup></a>. That could make sense, for things to be logical you need, well you need the logic itself. Proof assistants are not “gotcha” free though, they have different types of gotchas<a href="#fn21" class="footnote-ref" id="fnref21" role="doc-noteref"><sup>21</sup></a>.</p>
<p><strong>Wrong abstraction for the job</strong></p>
<p>Let’s talk about data structures a bit. The choice you make can impact extraneous complexity a great deal. An example which emphasizes this is <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type" target="_blank">CRDT</a>.<br />
Imagine that you are working on an app where 2 or more agents (human or not) can concurrently work on some list and your program needs to merge their work. Using a standard <em>list type</em> will be a cognitive nightmare, right? Think about one agent (R) removing items, another agent (A) adding items. How do you know if an item was removed by (R) or (A) just added it? So what do you do? You introduce some distributed locking mechanism? …Things are becoming complex very fast.<br />
The choice of which data structure is used can have a big impact on extraneous complexity. This extends to other abstractions as well.</p>
<p><strong>High levels of abstraction, an extraneous aspect</strong></p>
<p>I have seen very abstract code where the abstraction was like trees preventing developers from noticing a forest. One source of such examples is error handling. Mathematics rarely concerns itself with error messages, falsehood is falsehood. I have blogged about it in my posts about <a href="2021-01-17-maybe-overuse.html" target="_blank">Maybe Overuse</a> and <a href="2021-02-13-alternative.html" target="_blank">Alternative and errors</a>.</p>
<p> <div class="side-note">Side note: Probably not surprisingly, these were rather negatively received, heavily downvoted posts. The topic itself is very much a <em>repetitive negative thinking</em>. Incidentally, the negative comments mostly belonged in the general “what you are describing is just bad code, whoever wrote it should have been more careful” category. I want to understand how code abstractions could promote erroneous code, my interest is in what makes people not careful.<br />
 </div></p>
<p>Let’s focus on Haskell. One simple to explain and not very abstract example that still fits into this section is the <code>guard</code><a href="#fn22" class="footnote-ref" id="fnref22" role="doc-noteref"><sup>22</sup></a> combinator. I see it used and I also scratch my head when, say, a JSON parser error says only <code>"mempty"</code>. Possibly, some programmers think about the abstraction called <code>Alternative</code> when they should be thinking about something like <code>MonadFail</code>, an abstraction that allows to specify error messages.<br />
Abstractions seem to come with what psychologists call a <em>commitment bias</em> (hey, I am doing <code>MonadPlus</code> damn it!).<br />
It is us, not the tooling, Haskell ecosystem offers a very expressive variety of abstractions. E.g. consider the error handling blind spot we talked about earlier. You can think about <code>Either</code> as a <code>Bifunctor</code> or an <code>ArrowChoice</code> argument, what typically gets our attention is its throw and forget <code>Monad</code> semantics.<br />
Some of us really dig abstractions and are arguably very good at them. But we are kidding ourselves if we do not acknowledge that abstractions can also blind us.<br />
IMO the one thing we can do about it is to be aware. More diligence + awareness is typically all it takes.</p>
<p><strong>Section Summary</strong><br />
Some developers react to gotchas with something akin to <em>omission neglect</em>, while other developers appear to create a mental store of gotchas and their potential impacts. I am in the second group. Maintaining this store is not necessarily easy. I will also note a possible relationship to <em>repetitive negative thinking</em>.</p>
<p>Gotchas presented to us (thank you very much) by language designers or library implementers should technically be classified as <em>intrinsic</em> since a common bloke like me can’t do much about them other than look for a job that has a better tooling. If you look at programming as a whole, these are extraneous loads.</p>
<p>I have left the subject of abstraction vs imperative (abstractions being less straightforward and harder to map to actual execution) untouched. I plan to return to this in my next post.</p>
<p> <div class="side-note"><strong>There is a planet</strong> where PL designers treat all programming abstractions and types with respect. Only sound, correctly implemented abstractions are used.<br />
As a result, this planet has only unpopular languages, their TIOBE index starts at number 100. 🌠  </div></p>
<h2 id="germane-and-intrinsic-load-of-fp">Germane and intrinsic load of FP</h2>
<p>I expect that nothing in this section will be surprising to a functional programmer, but FP has such a unique cognitive impact that it is hard for me to not talk about.</p>
<p>Functional Programming allows us to understand computations in ways that are not possible without FP. Understanding is a big cognitive simplifier<a href="#fn23" class="footnote-ref" id="fnref23" role="doc-noteref"><sup>23</sup></a>. We are more at home with things we understand than with things we just know. Realizing that computations is something I can actually study to understand has been a game changer for me as a programmer.</p>
<p>Consider the following (middle-school?) formulas and how they relate to programming:</p>
<blockquote>
<p><span class="math inline"><em>a</em><sup>(<em>b</em> + <em>c</em>)</sup> = <em>a</em><sup><em>b</em></sup> * <em>a</em><sup><em>c</em></sup></span><br />
<span class="math inline"><em>a</em><sup>(<em>b</em> * <em>c</em>)</sup> = (<em>a</em><sup><em>b</em></sup>)<sup><em>c</em></sup></span></p>
</blockquote>
<p>These, pattern match and currying formulas, suggest that computations relate to other things we already know in ways that are almost surprising<a href="#fn24" class="footnote-ref" id="fnref24" role="doc-noteref"><sup>24</sup></a>.<br />
From the cognitive load theory point of view, an ability to map to existing knowledge needs to be viewed as a big plus (and a missed opportunity in how we learn programming).</p>
<p>FP is hard and there are 2 reasons why. One: it is simply hard (has a decent surface area but is also deep), two: it is different.</p>
<p>I was learning FP while working as a Java / Groovy developer. It took me 8 years, I estimated about 7000 hours. This effort included Category Theory, Types (my main interest), PLT, and programming in a bunch of FP languages. This has been, obviously, a big personal investment. And, I still had to internalize a lot of this when I started my actual Haskell job. Please do not interpret these stats as an argument that FP cannot be learned incrementally, or that learning FP does not provide immediate benefits. I am including these personal stats as evidence of an overall effort but also as evidence of the multitude of learning opportunities. We should resist thinking about knowledge as a binary checkbox.</p>
<p>FP requires a shift in how developers think. This shift is especially hard if the developer can only practice imperative skills at work. The tools we use impact our cognitive function.</p>
<blockquote>
<p>  “It is not only the violin that shapes the violinist, we are all shaped by the tools we train ourselves to use, and in this respect programming languages have a devious influence: they shape our thinking habits.”</p>
</blockquote>
<p>The quote is from <a href="https://chrisdone.com/posts/dijkstra-haskell-java/" target="_blank">Dijkstra letter to The University of Texas</a> protesting their Haskell -&gt; Java curriculum change. If you are into technical sports, you may have heard the term “muscle memory”. It is often harder to unlearn or adjust a body movement than learn a new one from scratch. It is even harder to “own” the old movement and the new movement at the same time. Psychologists also believe that unlearning is hard<a href="#fn25" class="footnote-ref" id="fnref25" role="doc-noteref"><sup>25</sup></a>.<br />
The required mental shift for FP is the source of all kinds of additional problems. It can form a communication barrier, it can divide the community and teams.<br />
At the same time, this cognitive shift is an opportunity to understand programs in a different way.</p>
<p>I will dig my hole a little deeper. This one line of code made a huge impact on me (it is called the <em>Free Monad</em> and is in Haskell<a href="#fn26" class="footnote-ref" id="fnref26" role="doc-noteref"><sup>26</sup></a>):</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">data</span> <span class="dt">Free</span> f a <span class="ot">=</span> <span class="dt">MkFree</span> (f (<span class="dt">Free</span> f a)) <span class="op">|</span> <span class="dt">Pure</span> a </span></code></pre></div>
<p>I decided to dedicate a full summer to learning this line and it ended up taking longer than that. There is actually quite a bit to learn and <em>understand</em> here!<br />
For example, how does it relate to this line (looks very similar, just replace <code>Free</code> with <code>Fix</code> and drop one constructor):</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw">newtype</span> <span class="dt">Fix</span> f a <span class="ot">=</span> <span class="dt">MkFix</span> (f (<span class="dt">Fix</span> f a))</span></code></pre></div>
<p>Or, how is this a monad? Does it satisfy monad laws? What does <em>free</em> mean? Can other things than monads be <em>free</em>? Can <code>Free</code>-s with different <code>f</code>-s be combined? If so, are there easier and harder ways of combining them? What is <em>freer</em>? How do I program with it? How does it (and should it) relate to <code>try-catch</code> games? And finally, what libraries implement and use <code>Free</code>? The point I am trying to make is that FP computations are a different breed. They actually have properties and the learner can build an understanding of these properties.<br />
Effect systems (the main application of <code>Free</code>) are a very powerful programming tool, they can add a lot of structure and cognitive simplicity<a href="#fn27" class="footnote-ref" id="fnref27" role="doc-noteref"><sup>27</sup></a>. I use 2 of them at work, one of them we maintain. Effect systems allow us to organize code into DSLs and interpreters. This approach creates a very high level of code reuse, testability, and defines very explicit, self-documenting types.<br />
Now, is it realistic to learn these concepts in a day or a week when starting a new project? Imagine a programmer who uses Java at work exploring this knowledge.</p>
<p>There has been some discussion about making Haskell itself more accessible (e.g. <a href="https://www.michaelpj.com/blog/2021/01/02/elementary-programming.html" target="_blank">Elementary Programming</a>) and some library effort in this direction as well (e.g. <a href="https://github.com/digitallyinduced/ihp" target="_blank">IHP</a>).<br />
Some teams separate <em>hard</em> micro services with high levels of abstraction from the rest.<br />
Some places separate a possibly very advanced implementation from a simple to use API (I believe Facebook’s Haxl does it). Creating a progression from easy to hard is less trivial.</p>
<p>FP is a niche, I think FP has a stable niche in programming. Correctness and understanding of computations are problems almost nobody in the industry cares about but they are sometimes needed. This reminds me of a Terry Pratchett Diskworld character: Esmerelda (Granny) Weatherwax</p>
<blockquote>
<p>  <em>“Esme Weatherwax hadn’t done nice. She’d done what was needed.”</em></p>
</blockquote>
<p>Wanted means popular, needed means stable. However, basic principles of FP will probably find a wider use (as discussed in <a href="#extraneous-loads-that-grow">Extraneous loads that grow</a>).</p>
<p> <div class="side-note"><strong>In a parallel dimension</strong> Alonso Church did not take a temporary break from lambda calculus and showed it to his student, Alan Turning. The first computer hardware was based on SKI calculus. In that dimension kids learn mathematics with proofs, imperative programming is considered a great addition after programmers learn the principles. In that dimension software has very few bugs, however, this universe has fewer programs, even fewer programmers, and the error messages suck. 🌌  </div></p>
<h2 id="post-summary">Post Summary</h2>
<p>My readers may get the impression that this post is a criticism of imperative programming. Applying cognitive load theory to programming does not translate to “imperative is complex”, rather it translates to “too much of imperative in one place (logically coupled) is complex”. IMO, some amount of imperative is often helpful.<br />
I plan to return to this topic in my next blog.</p>
<p>I am sure you have noticed that I think a lot about code complexity. And, yes, I do not feel comfortable working in messy code. Assessing and controlling the level of code complexity is crucial to me.</p>
<p>It has dawned on me that my dislike of code complexity may not be shared by others. <em>False consensus effect</em> is assuming that everyone else thinks like me.<br />
I remain convinced that some programmers react negatively to code complexity, but now I think that many programmers feel at home in code with a high cognitive load. This motivated me to work on this and the next post. IMO it is important that we try to understand each other a little better.</p>
<p>Are we doing a good job in managing code complexity? I think this a fair question to ask even if you think that simplicity is not crucially important. This post has argued that we are mostly failing on that front. In this post, we looked at how project complexity grows unnoticed, how bugs are a missed opportunity to learn about how we fail, and how FP changes the cognitive process but can be hard to learn. As a whole this post has been a bit of <em>repetitive negative thinking</em>, but I hope you found some positives and useful ideas in it as well. The main point of this post was to advocate for including cognitive aspects of programming projects into consideration and to present some useful terminology for doing it.</p>
<blockquote>
<blockquote>
<p>  <em>“It is time to unmask the computing community as a Secret Society for the Creation and Preservation of Artificial Complexity”</em> Dijkstra (of course).</p>
</blockquote>
</blockquote>
<h2 id="there-is-much-more-to-it">There is much more to it</h2>
<p>This post took a very narrow path through the very broad subject of cognitive aspects of programming.</p>
<p>These were observations of a programmer, this post does not try to cover research on this topic or provide a good list of reading materials. I do not feel qualified to provide either. This topic is also related to code quality and this has been vastly discussed.</p>
<p>My focus was coding rather than process. I did not discuss things like cognitive loads in pool requests, cognitive considerations during sprint planning, git hygiene, etc.</p>
<p>Size of program files is an obvious, related topic I did not discuss.</p>
<p>Monorepo vs single projects has interesting relevance. Dependency graphs of or sorts (version, library deps) are a similar interesting topic.</p>
<p>Coding efficiency and the 10X programmer in the context of cognitive loads is an interesting (but contentious) topic.</p>
<p>Low Code: The idea of distributing cognitive load across different components is not new. The terms “decoupling” or “isolation of concerns” are in this space. Low code is an idea of a very lopsided distribution in which most of the complexity falls onto the infrastructure. I started writing about it but decided to remove my notes as this post feels already too long.</p>
<p>Some PLs (Haskell is a good example of this) suffer from what some people call the <em>Lisp curse</em>. Instead of using established libraries, proprietary or one-off tools are often created. It is interesting why this happens and what to do about it. Could love of abstractions be causing it (reuse abstractions, not a code)? Is writing it from scratch a lower cognitive effort than learning and applying an existing solution? The end result, obviously, increases the cognitive load.</p>
<p>Cognitive load should be viewed as a resource problem, one that does not scale very well, and one that is not well understood. Cognitive load is greatly impacted by turn over rates, switching of code ownership, and by installed processes. Context switching is very expensive, the programmer’s inability to find contiguous blocks of time to focus could be viewed as an indication of an under-resourced project. Needless to say, under-resourced yields quick and dirty code.</p>
<p>Linting, formatting, aesthetics are all very interesting cognitive load topics. Most programmers seem to be very sensitive to how the code is presented, (e.g. would you ever use a light background in your code editor?). Similarly, syntax vs semantics, it seems syntax has a huge cognitive role even if we think about it as bikeshed.</p>
<p>Habit formation and unlearning are a big and very interesting topic.</p>
<p>Cognitive biases in the context of coding seem like very interesting topics too. In particular <em>bandwagon effect</em> (TypeScript is popular and hence must be very good), <em>framing effect</em> (new cool technology), <em>commitment bias</em> (we done it like this before, it has been tried and tested), <em>functional fixedness</em> (we do not need another PL), <em>omission neglect</em> (things we do not know are not important), <em>groupthink</em> (we want to work with people who think like us), <em>bikeshedding</em> (possibly most of this post 🙂).</p>
<p>Point-free code, I stayed away from discussing it.</p>
<p>Cognitive aspects of troubleshooting are something I only touched on.</p>
<p>Imperative vs denotative is something I only touched on.</p>
<p>One topic I do plan to discuss (in the next post) is a distinction between empirical and formal processes in programming and how it impacts cognitive loads and acts as a divider.</p>
<p>Cognitive loads are related to stress, I intend to return to this topic in the future as well.</p>
<p>This post did not run out of topics, rather I have run out of steam. I hope I gave you things to think about. Thank you for reading!</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>E.g. see <a href="https://solutionspace.blog/2021/12/04/every-simple-language-will-eventually-end-up-turing-complete/" target="_blank">Every Simple Language Will Eventually End Up Turing Complete</a><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>Corrected from 7 to 3-5 based on feedback from <a href="https://www.reddit.com/r/haskell/comments/x2du9d/comment/imjc87f/?utm_source=share&amp;utm_medium=web2x&amp;context=3" target="_blank">u/Fereydoon37</a>. The number 7 came from earlier studies which have been observing higher levels of “chunkability”, 3-5 seems more relevant. I have also removed my use of technical sports as an analogy (motor skills seem not very relevant to our discussion).<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3" role="doc-endnote"><p>See this wiki page: <a href="https://psychology.fandom.com/wiki/Chunking" target="_blank">Chunking</a>. For me, thinking about a big chunk without a context, e.g. <em>OOP</em> or <em>Geometry</em> triggers some high level information plus a seemingly random example, thinking more causes my brain to wander down some path. “Tell me everything you know about …” is not something I am capable of. So I do not think we load a whole huge chunk into working memory, but we can operate using chunks of seemingly unlimited size.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4" role="doc-endnote"><p>Examples of concepts in programming that come with 2 chunks: assignment statement, function (input and output), function application (function and input), function composition. This pattern allows for very big chunks, here are examples with 2 big chunks I have worked a lot with in the past: algebraic topology, compensated compactness (using measure theory to study PDEs). Moving closer to programming, good big chunk examples are: computation laws, computation properties, operational semantics rules, Curry-Howard correspondence. Mathematical theorems mostly follow this pattern (e.g. in the context of programming “Simply typed lambda calculus is strongly normalizing”). A candy: “Dhall is strongly normalizing”.<br />
It appears that we can, indeed, do great things focusing on 2 chunks, However, I have not found much psychological research on 3 being the magic number.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5" role="doc-endnote"><p>Terms <em>accidental</em> and <em>essential complexity</em> come from <a href="https://en.wikipedia.org/wiki/No_Silver_Bullet" target="_blank">No Silver Bullet</a> paper.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn6" role="doc-endnote"><p>Denotational approach means mapping requirements to mathematical concepts such as monoids, identifying categorical structures (things that compose), etc.<a href="#fnref6" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn7" role="doc-endnote"><p>Interesting youtube: <a href="youtube.com/watch?v=NdA6aQR-s4U" target="_blank">Why Do So Many Programmers Lose Hope?</a><a href="#fnref7" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn8" role="doc-endnote"><p>See the discussion in <a href="https://www.researchgate.net/publication/220963754_A_Computational_Analysis_of_Cognitive_Effort" target="_blank">A Computational Analysis of Cognitive Eﬀort</a>. The term <em>cognitive cost</em> is typically used to mean a negative impact on a cognitive function induced by stress and the usage is also not very consistent, I am avoiding its use.<a href="#fnref8" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn9" role="doc-endnote"><p>Note that these terms imply some context. E.g. <em>simple to reason about correctness</em> could be very different from <em>simple to reason about performance</em>. The most popular context is “this code needs to work” with a somewhat relaxed definition of what “works” means, typically implying a reasonable level of correctness. This was pointed out to me in the conversation with <a href="https://discourse.haskell.org/t/cognitive-loads-in-programming/4994/12?u=rpeszek" target="_blank">hasufell in Haskell discourse</a><a href="#fnref9" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn10" role="doc-endnote"><p>As a side note, concrete thinking is not always bad. An interesting article on this in a broader context: <a href="https://www.healthline.com/health/concrete-thinking" target="_blank">Concrete Thinking: Building Block, Stumbling Block, or Both?</a>.<a href="#fnref10" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn11" role="doc-endnote"><p>“If Turing could not get it right, what chance do we have?”- is a phrase I remember from a Dana Scott lecture.<br />
The bugs were only discovered during the actual implementation work (see <a href="https://blog.wolframalpha.com/2010/06/23/happy-birthday-alan-turing/" target="_blank">Alan Turing</a>). Church’s <a href="https://plato.stanford.edu/entries/church/supplementA.html" target="_blank">take on undecidability</a> followed shortly after Turing’s. As far as I know nobody found any issues with it (even if untyped lambda calculus is rather unruly and the topic itself was hotly debated).<a href="#fnref11" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn12" role="doc-endnote"><p>Example of non-symmetric equals is <code>java.sql.Timestamp</code> used with <code>java.sql.Date</code> or <code>java.util.Date</code>, these remain used as standard JDBC mappings for DB columns, the usage will show no deprecation warning. <code>[] !== []</code> and <code>[] != []</code> in JS (incidentally <code>[] == ""</code>), working in JS often feels like explosives engineering.<br />
I wrote a blog series about <a href="../tags/TypeScript-Notes.html" target="_blank">TypeScript Types</a> and ended up presenting a lot of complexities and gotchas that probably surprise any TS developer.<br />
How do Hibernate users prevent this <a href="http://rpeszek.blogspot.com/2014/08/i-dont-like-hibernategrails-part-2.html" target="_blank">concurrency issue</a>?<br />
I remember Grails as a land mine of issues, I wrote an 11 part <a href="http://rpeszek.blogspot.com/2014/10/i-dont-like-hibernategrails-part-11_31.html" target="_blank">blog series</a> about it back in 2014.<br />
Java Streams have a very interesting take on referential transparency: if you execute a stream twice the second attempt will fail. This is probably the first and only attempt at dynamically typed linear types 🙂 and is bound to create interesting intermittent issues.<a href="#fnref12" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn13" role="doc-endnote"><p>“even compiler writers mess it up all the time” is a quote from (<a href="https://doc.rust-lang.org/nomicon/subtyping.html" target="_blank">Rust Subtyping Documentation</a>)<a href="#fnref13" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn14" role="doc-endnote"><p>Keeping things easy, arrays are mutable. Sadly, you can explore the answer on your own by asking a mainstream compiler like Java or TS and the answer will, unfortunately, be the incorrect <em>yes</em>.<a href="#fnref14" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn15" role="doc-endnote"><p>In TS and JS the answer is yes. In TS this is a subtyping rule.<a href="#fnref15" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn16" role="doc-endnote"><p>This is a gotcha generator especially in OOP languages that have some level of type inference. E.g. here are some gotchas in <a href="2021-12-12-ts-types-part1.html#compilation-bloopers" target="_blank">TS</a> that involve widening to <code>unknown</code> which is a top type in TS, here is a discussion about these in <a href="http://rpeszek.blogspot.com/2017/07/scala-whats-wrong-with-you_29.html" target="_blank">Scala</a>.<a href="#fnref16" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn17" role="doc-endnote"><p>For example, it is hard to imagine that the unsound implementation of variance in a PL like TS was accidental. It must have been a decision to keep things easy.<a href="#fnref17" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn18" role="doc-endnote"><p>I sometimes see this argument “It is impossible to statically reason about termination in a Turing complete PL, thus, all hope is lost”. Firstly, this is inaccurate: it is possible to statically verify totality on a subset of programs. Secondly: if non-termination is like accidentally hurting your foot, then exception is like shooting yourself in the foot. A missing DB record should, IMO, rarely be treated as non-termination. (I use the terms <em>total</em>, <em>terminating</em> and <em>partial</em>, <em>non_terminating</em> interchangeably.)<a href="#fnref18" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn19" role="doc-endnote"><p>Haskell dedicates a significant effort to soundness. E.g. see <a href="https://www.youtube.com/watch?v=hIZxTQP1ifo">Type Classes vs. the World</a>. Not everything is perfect however. Haskell allows for easy to abuse error non-termination (e.g. <code>error</code>, <code>undefined</code> functions), however ability to <code>catch</code> is more limited than in most PLs. Non-termination in itself throws a wrench, one Haskell should not be blamed for, see <a href="http://math.andrej.com/2016/08/06/hask-is-not-a-category/">Hask is not a category</a> and <a href="http://blog.sigfpe.com/2009/10/what-category-do-haskell-types-and.html">What Category do Haskell Types and Functions Live In</a>. Overall Haskell language comes with much fewer surprises if compared to the mainstream.<br />
The Haskell ecosystem (including its standard library) are more lax than the language itself. Michael Snoyman’s <a href="https://www.snoyman.com/blog/2020/10/haskell-bad-parts-1/" target="_blank">Haskell Bad Parts</a> is a great series on this topic. The most recent surprise for me is how <em>Aeson</em> (the most popular Haskell library for dealing with JSON) <a href="https://github.com/haskell/aeson/issues/961" target="_blank">generic instances work</a>.<a href="#fnref19" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn20" role="doc-endnote"><p><em>Standard ML</em> is known for its soundness, I do not know <em>ML</em> family that well, but I do know it has exceptions and <code>throw/catch</code> (in this case <code>raise/handle</code>) games. Possibly a more accurate point here is that we need strict formal semantics, it does not need to be dependently typed.<a href="#fnref20" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn21" role="doc-endnote"><p>Languages like Idris push the limits of what a compiler can do. I have experienced compiler hanging, compilation error messages overflowing my terminal buffer… (the second happened to me in Haskell too but the scenario warranted it more). These issues happen if you start doing certain type level things (I was just trying to implement a block chain on the type level 🙂).<a href="#fnref21" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn22" role="doc-endnote"><p>For readers not familiar with Haskell, <code>guard</code> allows to reject a computation based on a boolean expression. It is defined using a very general concept of <code>Alternative</code> and at this level of generality specifying an error message is not possible. In real life I see it used with parsers and other computations that could really use an error message.<a href="#fnref22" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn23" role="doc-endnote"><p>The terms understanding and knowledge are often conflated. The difference, however, is significant, here it is described in the context of learning math: <a href="https://japan-math.com/blogs/news/understanding-versus-knowledge" target="_blank">“Understanding” Versus “Knowledge” – What’s The Difference?</a>. This has been very much my experience as a mathematics learner and educator. Things become both easy and simple once you understand them.<a href="#fnref23" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn24" role="doc-endnote"><p>Category Theory will never cease to surprise<a href="#fnref24" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn25" role="doc-endnote"><p>see 2.1 section in <a href="https://core.ac.uk/download/pdf/77240027.pdf" target="_blank">Unlearning before creating new knowledge: A cognitive process.</a><a href="#fnref25" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn26" role="doc-endnote"><p>For readers not familiar with this concept and curious about what this does, <code>Free f a</code> allows to construct monadic (whatever that means) syntax trees with instructions provided by <code>f</code>. In the context of this article, the only important point is that this one line of code has a lot of properties that can be learned (and that many computations come with a similar learning potential).<a href="#fnref26" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn27" role="doc-endnote"><p>Any extraneous cognitive loads associated with effects? Yes, there are a few, especially on the implementation side. Also like most other tools, effects can be abused. I sometimes see a single DSL instruction interpreted directly to IO (more Haskell terminology here, IO is what we call a sin-bin) and used in a brute-force IO code. This just adds cognitive overhead of effects without taking advantage of what they have to offer.<a href="#fnref27" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

    </section>
</article>
]]></summary>
</entry>
<entry>
    <title>Type Enthusiast's Notes about TypeScript. Part 6. Reasoning using Types</title>
    <link href="https://rpeszek.github.io//posts/2022-03-13-ts-types-part6.html" />
    <id>https://rpeszek.github.io//posts/2022-03-13-ts-types-part6.html</id>
    <published>2022-03-13T00:00:00Z</published>
    <updated>2022-03-13T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on March 13, 2022
        
            by Robert Peszek
        
        
        
        <div class="changelog"> <div>Revision History: <ul> <li> (2021.03.15) Edited <a href="#enums">Enums</a> section</li> <li> (2022.04.30 - 2022.05.29) Added Types vs TDD side-note in <a href="#about-maintainability">About Maintainability</a> + Minor edits </li> <li> (2022.05.29) Draft warning removed </li> </ul> </div></div>
        
        
        <div class="info">Tags: <a title="All pages tagged 'TypeScript-Notes'." href="../tags/TypeScript-Notes.html">TypeScript-Notes</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <div class="toc"><div class="header">Table of Contents</div>
<ul>
<li><a href="#nutshell">Nutshell</a></li>
<li><a href="#about-clarity">About Clarity</a>
<ul>
<li><a href="#declare-function-return-types">Declare function return types</a></li>
<li><a href="#variables-named-x">Variables named <code>x</code></a></li>
<li><a href="#enums">Enums</a></li>
<li><a href="#clarity-vs-encapsulation">Clarity vs encapsulation</a></li>
<li><a href="#referential-transparency-purity-and-explicit-types">Referential transparency, purity, and explicit types</a></li>
</ul></li>
<li><a href="#about-productivity">About Productivity</a>
<ul>
<li><a href="#a-walk-in-the-park">A walk in the park</a></li>
<li><a href="#inference-reversed-and-typedd">Inference reversed and T(ype)DD</a></li>
</ul></li>
<li><a href="#about-simplicity">About Simplicity</a>
<ul>
<li><a href="#total-vs-partial">Total vs Partial</a></li>
</ul></li>
<li><a href="#about-safety">About Safety</a>
<ul>
<li><a href="#monads">Monads</a></li>
</ul></li>
<li><a href="#about-correctness">About Correctness</a></li>
<li><a href="#about-maintainability">About Maintainability</a></li>
<li><a href="#universal">Universal</a>
<ul>
<li><a href="#advanced-types-as-patterns">Advanced Types as Patterns</a></li>
</ul></li>
<li><a href="#unpopular">Unpopular</a>
<ul>
<li><a href="#gradual-progress">Gradual Progress</a></li>
</ul></li>
<li><a href="#final-words">Final words</a></li>
</ul>
</div>
<p><em>Please Leave Feedback in: <a href="https://github.com/rpeszek/rpeszek.github.io/discussions/1" target="_blank">git discussions</a></em></p>
<p>Previous post: <a href="2022-02-13-ts-types-part5.html" target="_blank">Part 5. Advanced Types</a>.<br />
Back to the beginning post: <a href="2021-12-12-ts-types-part1.html" target="_blank">Part 1. Typing in Anger</a></p>
<h2 id="nutshell">Nutshell</h2>
<p>This post wraps up my series about types in TS. In this series, we explored type-centric approaches to writing code and pushed TS to its limits, sometimes a little beyond its limits.</p>
<p>In any mainstream programming language there is a group of users interested in using types. Similarly to the rest of the industry, this group is a very small subset of the TS community. Developers interested in types tend to be unappreciated and underutilized.</p>
<p>Types in programming get very formal and are very interesting for mathematically inclined developers. Mathematical inclinations are probably a necessary condition for enjoying types. This partially explains why types are such a niche, but IMO there are other reasons I will try to discuss some of them in this post.</p>
<p>This post will discuss these aspects of types: types are</p>
<ul>
<li>About Clarity</li>
<li>About Productivity</li>
<li>About Simplicity</li>
<li>About Safety</li>
<li>About Correctness</li>
<li>About Maintainability</li>
<li>Universal</li>
<li>Unpopular</li>
</ul>
<p>I will finish my series with a short rant about each of the bullet points. This will allow me to revisit and summarize some of the things we have discussed in previous parts and mention a few things this series did not cover.<br />
This post will be mostly a high level rant. I want to talk a bit about what is possible. Some of the discussion will not be very relevant to TS as the language lacks the capabilities. I think these topics are still relevant to TS developers as the ideas behind these concepts can still be useful.<br />
Simply put, my goal is to discuss how types (including advanced stuff) can be used in TS.</p>
<p>Like all of my other posts in the series, this one is a big longish and tries to cover a lot of ground. I hope you will find it worth the effort.</p>
<p>Some readers may disagree when reading this post. You may have valid reasons for disagreeing with me. Please let me know what they are.</p>
<h2 id="about-clarity">About Clarity</h2>
<p>What are coding conventions and standards? When I hear these terms being used, I know I will soon hear about code formatting and linting, importance of code comments, even things like readme files and git hygiene. However, I am unlikely to hear about types. It is not that types are not important, they are. They are also harder to discuss. This phenomenon has a name, it is called <a href="https://wiki.haskell.org/Wadler%27s_Law" target="_blank">Wadler’s Law</a> or <a href="https://bikeshed.com/" target="_blank">bikeshed</a>.</p>
<p>I have discussed using types to achieve code clarity in <a href="2021-12-24-ts-types-part2.html#referential-transparency" target="_blank">referential transparency</a> and <a href="2021-12-24-ts-types-part2.html#types-as-documentation" target="_blank">types as documentation</a> sections of Part 2. Let’s revisit the topic here.</p>
<p>It is much harder to comprehend the whole program than it is to comprehend its types. Types can provide a high level information about the program the way that theorems provide high level information about proofs in mathematics. Types can give a valid high level representation of the app. Programs often can’t, they often contain tedious details, performance optimizations, and lots of other persisted developer’s sweat.<br />
When done right, one can use types as <em>specs</em>, at least on a unit level types could be viewed as specifications.</p>
<p>Types have a synergy with FP. This series was not about FP but it was hard for me to completely stay away, the synergy is so deep. Types help express functional concepts clearly.</p>
<p>Advanced types come with a learning curve. It is important to acknowledge that clarity is subjective and can easily be replaced with confusion unless developers are familiar with the concepts.</p>
<blockquote>
<p><em>“WTFPM: WTF Per Minute is an actual measurement for code value.”</em></p>
</blockquote>
<p>I imagine some topics covered in <a href="2022-01-09-ts-types-part4.html" target="_blank">Part 4</a> or <a href="2022-02-13-ts-types-part5.html" target="_blank">Part 5</a> could have a high WTFPM number. IMO, types used in production projects should be accessible to the project contributors. This means pushing the envelope just a bit but not too far.</p>
<p>The following subsections examine a few concepts related to clarity.</p>
<h3 id="declare-function-return-types">Declare function return types</h3>
<p>TS does not require it but why would you not do that?</p>
<h3 id="variables-named-x">Variables named <code>x</code></h3>
<p>Are variable names essential to clarity? It is a bit of a telltale. One of the most common criticisms of languages that heavily use types (like Haskell) is:</p>
<blockquote>
<p><em>“Whats up with the variable names, why everything is <code>x,y,z</code>, <code>xs,ys,zs</code>, <code>f,g,h</code> or <code>a,b,c</code>?”</em></p>
</blockquote>
<p>Part of the reason is that the code can be very general. If a variable can be literally anything, why not call it <code>x</code>? The other reason is where the reader gets the information from: variable names or types? Personally, I believe in more explicit variable names when implementing a specific business logic and the implementation is long. However, even in such cases, the info should be in the types. If it is not, the code probably can benefit from refactoring.</p>
<h3 id="enums">Enums</h3>
<p><em>This section is now somewhat misplaced. I am keeping this edited version for consistency with my original post. It reflects, IMO, valid points made by the reddit community. My original position was that <em>enums</em> are obsoleted by union types.</em></p>
<p>Are enums clearer than literal types? This code has 3 types (“foo”, “bar” and the union):</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a>type FooBar <span class="op">=</span> <span class="st">&quot;foo&quot;</span> <span class="op">|</span> <span class="st">&quot;bar&quot;</span></span></code></pre></div>
<p>This code defines one type (<code>FooBar</code>) and is more verbose:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">enum</span> FooBar {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>  Foo <span class="op">=</span> <span class="st">&quot;foo&quot;</span><span class="op">,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a>  Bar <span class="op">=</span> <span class="st">&quot;bar&quot;</span><span class="op">,</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>}</span></code></pre></div>
<p>What is the advantage of using this <code>enum</code>?</p>
<p><strong>EDIT:</strong> <em>I got a pushback to my criticism of <code>enum</code> (<a href="https://www.reddit.com/r/typescript/comments/tdymsp/comment/i0p5q8c/?utm_source=share&amp;utm_medium=web2x&amp;context=3" target="_blank">reddit</a>). Here is a list of valid reasons to use <code>enum</code> in TS:</em></p>
<ul>
<li><em>Intellisense is likely to work better (go to definition, search for usage - these niceties may not even make sense with union types)</em></li>
<li><em>Type inference is likely to be much better as the name of the <code>enum</code> is specified at the usage point</em></li>
<li><em>Enums can be more descriptive than some literals (e.g. <code>OK</code> is nicer than <code>200</code>)</em></li>
<li><em>Enums are nominally typed which can be often useful. Two enums have different types even if they have the same content. (note that in TS even classes are structurally typed, class content matters, class name not so much)</em></li>
</ul>
<p><em>However, most type safety appears to be identical. For example, switch statement exhaustive checks are equivalent. IMO literal string types are often more readable. Readers familiar with languages that have a nice implementation of Algebraic Data Types are likely to gravitate towards unions.</em></p>
<h3 id="clarity-vs-encapsulation">Clarity vs encapsulation</h3>
<p>Encapsulation does not help clarity. I consider encapsulation to be very useful when designing micro-services, not so much when designing programs. Encapsulation often means not expressive types. Encapsulating is hiding things from the types. It often makes types simpler than they should be. To get the benefits of types, we need to give them a chance. Type checker will not type check what is invisible to it.</p>
<p>On the other hand, explicit types (types that contain a lot of information) have a higher maintenance cost.<br />
I like to compare this to documentation. If app functionality changes you should change the documentation. You are likely to do that only in the most obvious places. Explicit types are different, they create domino effects forcing you to propagate the changes to all the relevant places.<br />
This overhead is not always desirable and there are patterns and tools to minimize such cost (e.g. TS subtyping, existential types, FP concept called <em>smart constructors</em>). Changing functionality should create compilation errors but ideally these errors should not be hard to fix.</p>
<p>Encapsulated code does not test well and often requires mocking frameworks. You will know you are doing something right when you stop using mocks for unit tests.</p>
<h3 id="referential-transparency-purity-and-explicit-types">Referential transparency, purity, and explicit types</h3>
<p>I have discussed these concepts already in <a href="2021-12-24-ts-types-part2.html#referential-transparency" target="_blank">Referential Transparency (Part 2)</a>. I want to return to this topic for another rant.<br />
Referential transparency does not have an agreed upon formal definition. It typically means:</p>
<p><em>A computation is referentially transparent if it can be safely replaced with its return value<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</em></p>
<p>This is clearly related to clarity and simplicity. It is hard to reason about code that does different things every time it is called.</p>
<p><em>Function is pure if it does not perform any side-effects (e.g. does not mutate things)</em></p>
<p>These concepts are related but not equivalent, e.g. a function that finds a shortest path in a graph is likely to be referentially transparent even if its implementation mutates its local variables (most standard graph algorithms are imperative and mutate stuff). Such functions “look” pure from the outside and maybe in some cases that is good enough. I may want to care about referential transparency more than about strict purity.</p>
<p>I like to treat referential transparency loosely. In my loose approach, referential transparency simply means that what the function does is exposed in its type (so <em>referentially transparent</em> and <em>has an explicit type</em> become the same thing). Thinking in these lines makes referential transparency less of a checkbox and more a progress bar.</p>
<p>Consider the following versions of code that are supposed to establish a WebSocket using some imaginary API (we are implementing a PetStore):</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="co">//(1) gets config from a global place and globally stores WS connection</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="kw">const</span> initWs<span class="op">:</span> ()<span class="op">:</span> <span class="kw">void</span> <span class="op">=</span> <span class="op">...</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a><span class="co">//(2) gets config from a global place</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a><span class="kw">const</span> connectWs<span class="op">:</span> () <span class="kw">=&gt;</span> WsConnection <span class="op">=</span> <span class="op">...</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a><span class="co">//(3) gets config from passed parameter, incomplete return type</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a><span class="kw">const</span> connectWs<span class="op">:</span> (conf<span class="op">:</span> PetStoreConfig) <span class="kw">=&gt;</span> WsConnection <span class="op">=</span> <span class="op">...</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a><span class="co">//(4) gets config from passed parameter, incomplete return type</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true"></a><span class="kw">const</span> connectWs<span class="op">:</span> (conf<span class="op">:</span> {<span class="dt">loggerConf</span><span class="op">:</span> LoggerConfig<span class="op">;</span> <span class="dt">wsUrl</span><span class="op">:</span> Url}) <span class="kw">=&gt;</span> WsConnection <span class="op">=</span> <span class="op">...</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true"></a><span class="co">//(5) null, option, optional, maybe ... types do not contain much error context</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true"></a><span class="kw">const</span> connectWs<span class="op">:</span> (conf<span class="op">:</span> {<span class="dt">loggerConf</span><span class="op">:</span> LoggerConfig<span class="op">;</span> <span class="dt">wsUrl</span><span class="op">:</span> Url}) <span class="kw">=&gt;</span> WsConnection <span class="op">|</span> <span class="kw">null</span> <span class="op">=</span> <span class="op">...</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true"></a></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true"></a><span class="co">//(6) gets config from passed parameter, complete return type </span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true"></a><span class="co">//(most likely will involve subtyping at usage point) </span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true"></a><span class="kw">const</span> connectWs<span class="op">:</span> (conf<span class="op">:</span> {<span class="dt">loggerConf</span><span class="op">:</span> LoggerConfig<span class="op">;</span> <span class="dt">wsUrl</span><span class="op">:</span> Url}) <span class="kw">=&gt;</span> WsConnection <span class="op">|</span> WsError <span class="op">=</span> <span class="op">...</span></span></code></pre></div>
<p>Note that there is no much benefit between using <code>PetStoreConfig</code> and <code>{loggerConf: LoggerConfig; wsUrl: Url}</code> or between <code>WsConnection | null</code> vs <code>WsConnection | WsError</code> from the point of view of strict referential transparency. There is, however, a big difference if you think about the information contained in the types.<br />
(1) and (2) are very opaque, (3) and (4) are similar but not all <code>PetStoreConfig</code> is relevant, thus (4) type is more transparent and precise.<br />
In my experience, even programmers who know a lot about types end up not thinking about exceptions and will code some equivalent of (5). The goal is to get to (6). (6) is very explicit, IMO it is the best. Subtyping is likely to be used at some point as <code>PetStoreConfig</code> probably will be passed to it. However, the first (the least explicit) approach is probably more commonly used at large.</p>
<p>Readers working with React can alternatively think about a component that uses an internal state hook (encapsulates state) vs a component that accepts a setter callback and a getter property as input arguments. You can also think about React Context API or a similar approach and compare it with explicit setters/getters.</p>
<p>Each computation has an input and an output even if TypeScript / JavaScript code does its darndest to hide it. TS code can pull the inputs out of thin air (configuration, stuff stored on the window object, etc) and sink the output by saving it somewhere. The above <code>initWs</code> is guilty of both of these felonies. Still, there is a referentially transparent computation hiding somewhere. In the above example the last <code>connectWs</code> type describes the inputs and output within the heavily encapsulated <code>initWs</code>.</p>
<p>Inputs and outputs are essential to clarity. The developer should try to understand what these inputs and outputs are at the very least. Ideally, the explicitly typed computation within can be factored out. This is not just for clarity, you are likely to find future uses for it (e.g. the last example above could be factored out of the PetStore and used in other apps or used to open 2 connections). And, it will be easier to test.</p>
<h2 id="about-productivity">About Productivity</h2>
<p>We are not leaving clarity behind. Clarity and productivity are obviously related. I am only changing the angle.</p>
<p>When I write code in an untyped language, I still think about types, the only difference is that I do not have any verification from the type checker. Not having a type checker or working with poorly designed types slows me down. Moving to a strongly typed functional language has made me much more effective, possibly 4x-5x times more effective (I am sure such stats are very personal and depend on many factors).</p>
<p>IMO all developers, whether they admit it or not, use types in their heads. The question is: how effectively?</p>
<p>The following subsections examine some concepts related to productivity.</p>
<h3 id="a-walk-in-the-park">A walk in the park</h3>
<p>Types can guide the process of writing code. I can write code by ‘following the types’ if the API gives me well designed types to follow. The analogy is following a path in the park.</p>
<p>We have seen examples of this in <a href="2021-12-24-ts-types-part2.html" target="_blank">Part 2</a> where I twisted <em>office.js</em> arm to get the types right and was able to type predicate myself to a much faster to write and safer code.</p>
<p>We have also seen this in Part 4 (<a href="2022-01-09-ts-types-part4.html#preventing-information-escape" target="_blank">preventing information escape</a>, <a href="2022-01-09-ts-types-part4.html#phantom-types" target="_blank">phantom types</a>) where types formed jigsaw puzzles allowing the computations to fit together in only certain ways.</p>
<p> <div class="side-note">There is a technique often called <em>Hole Driven Development</em> in which the developer interacts with the type checker to write code. You can try to use this <a href="2021-12-12-ts-types-part1.html#type-holes" target="_blank">type hole</a> with a mixed success to accomplish some of it in TS.<br />
The idea is that by examining the type of a still missing code (the hole) you should be able to figure out the right piece of the puzzle to fit in (replace that hole with a piece of code that has the needed type). The new piece can have some type holes too and the process is iterative. The language that provides the best experience (and a lot of fun) doing this is Idris. You can implement certain functions by just using keyboard shortcuts to deconstruct, pattern match, search solution space for the right function in scope and insert it to the program<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>.<br />
OK, TS does not do that, but you do not need such tooling to benefit from the jigsaw approach to designing your code.  </div></p>
<h3 id="inference-reversed-and-typedd">Inference reversed and T(ype)DD</h3>
<p>This section is not very relevant to TS, but I think it is interesting to note.</p>
<p>Type inference allows a programming language to compute the types without needing the developer to specify them.<br />
Ideally, the future will bring tooling where the developer defines the types and the compiler computes the program.</p>
<p> <div class="side-note">A lot of this unicornish utopia is available today in some FP languages like Haskell. Certain code is considered boilerplate and the tooling can derive it automatically. Examples are: equality, ordering, JSON parsing/formatting, <code>map</code> functions for non-list types, folding/unfolding for non-list types, traversing non-list types, recursion scheme folds and unfolds, optics… All of this boilerplate would be available for free for something like the <code>Json</code> grammar example from Part 1 and Part 5. Programming in Haskell often involves creating some involved custom type and automatically deriving a lot of boilerplate for it.<br />
The are also many experimental (typically short lived) projects for auto generating code. I have curated a short list <a href="https://github.com/rpeszek/IdrisTddNotes/wiki/Part2_Sec3_2_3_gen#program-synthesis-some-relevant-links" target="_blank">here</a> (I expect it to be mostly outdated now).<br />
The stronger the types, the more code generation is possible (I have already mentioned interactive code development in the dependently typed Idris).  </div></p>
<p>TS will not automatically implement code for us, however, starting with types and following with (a manually written for now) programs is often quite productive. This is the TDD approach to programming, only T means <em>Type</em>. The simplest way to go about it is to start on a small unit level (define types for small building blocks first). It helps to know some solid building blocks (e.g. FP types) and to use a lot of type variables. <a href="#about-maintainability">About Maintainability</a> section will say a bit more about TDD and Types.</p>
<h2 id="about-simplicity">About Simplicity</h2>
<p>I consider the terms <em>simple</em> and <em>easy</em> to have different meaning. Easy: A low barrier to entry (e.g. language). Simple: Low effort to reason about (e.g. code written in that language). There is no free lunch, to get simplicity you need to accept that things will not be easy.<br />
Simplicity is about ability to reason about things and as such is closely related to all other bullet points in this post.<br />
IMO, the popularity of easy and the unpopularity of simple are a systemic problem in today’s programming and elsewhere.</p>
<p>I consider TS to be complex (the opposite of simple). I devoted <a href="2022-01-03-ts-types-part3.html" target="_blank">Part 3</a> to explaining why.</p>
<p>On some basic level, simplicity is associated with strictness. Flexibility seems to cause complexity. <em>Flow’s exact</em> objects are strict and simple, existential types are more flexible and more complex, subtyping makes types very flexible and very complex. However, there are many modern concepts that programming languages are still trying to figure out, e.g. dependent types (Idris, Agda, Coq), linear types (Rust, Haskell v.9, Idris 2). These concepts should be filed under strict and complex today. I have a feeling that in 5 years I will consider them less complex than subtyping (see the current <a href="https://doc.rust-lang.org/nomicon/subtyping.html" target="_blank">subtyping</a> doc from Rust).</p>
<p>One aspect critical to simplicity that is <em>easy</em> to explain and one that we have not discussed yet is <em>totality</em>.</p>
<h3 id="total-vs-partial">Total vs Partial</h3>
<p>Another related term is non-termination. Does the function return a value as expected? Bunch of things can go wrong: function can throw exception, loop forever, have unbounded recursion, return unexpected <code>null</code> or <code>undefined</code><a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a>. Functions that return a result for all inputs are called <em>total</em> otherwise are called <em>partial</em>.</p>
<p>Total is simple. Reasoning about partial functions is much harder. Any non-termination bypasses the type checker. <em>Using partial functions means that the types are misleading.</em></p>
<p>Exceptions seem to be the most frequent reason for the non-termination. Developers who like types avoid throwing exceptions, they will favor TS union types instead.</p>
<p>Let’s think again about computations from the input-output perspective and consider conditional control flow of the program.<br />
TS’s ternary can be viewed as a function. <em>if-else</em> not so much. <em>if_else</em> does not have a type. It was designed for mutating things and in today’s more immutable approach to programming it should feel antiquated. However, it is idiomatic to both JS and TS and is impossible to avoid.<br />
I use <em>if_else</em> blocks only with return statements (with exception of <code>void</code> functions). I do not use <em>if</em> without the matching <em>else</em> even if the code looks repetitive (again, with exception of <code>void</code> functions).<br />
If you think about the “referentially transparent computation within”, you will notice that <em>if</em> without <em>else</em> is partial. Several programming languages offer <em>if_else</em> syntax without the <em>if</em> only option.</p>
<p>One could go (IMO too) far with this approach and use <code>if-else</code> as a lambda:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">// if-else as a lambda, this seems overkill</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="kw">const</span> x <span class="op">=</span> (() <span class="kw">=&gt;</span> {</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a>    <span class="cf">if</span> (condition) {</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a>        <span class="op">...</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a>        <span class="cf">return</span> <span class="st">&quot;yes&quot;</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a>    } </span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a>    <span class="cf">else</span> {</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a>        <span class="op">...</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true"></a>        <span class="cf">return</span> <span class="st">&quot;no&quot;</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true"></a>    }</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true"></a>    }) ()</span></code></pre></div>
<p>I fully expect some pushback on this. My view is opposite to what you can frequently find on the internet (<em>else</em> is sometimes considered evil). Developers consider a sequence of <em>if</em>-s better than <em>if_else</em> chains.<br />
For simple control flows it should not matter. For more complex code using partial <em>if</em>-s is concerning.</p>
<p> <div class="side-note">In general case verifying totality is <em>undecidable</em> (it is impossible to write a static analysis tool (e.g. compiler) that checks if programs are total or not). This is the famous Turing’s counterexample to Hilbert’s <a href="https://en.wikipedia.org/wiki/Entscheidungsproblem" target="_blank">decidability problem</a>. You may also know it as the <a href="https://en.wikipedia.org/wiki/Halting_problem" target="_blank">halting problem</a>. However, interesting things can be done and languages like Agda, COQ, Idris can guarantee totality for a large subset of programs<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>.<br />
Outside of this small set of dependently typed languages, the totality is something that developers need to try to enforce on their own (that obviously includes TS).  </div></p>
<h2 id="about-safety">About Safety</h2>
<p>Here are some interesting examples of safety that could be provided by types: safe routing in a single page app (no broken routes), safe use of environment configuration (e.g. types prevent accessing arbitrary environment variables), safe backend communication (imagine the same types in frontend and backend with safety preventing broken urls and payload parsing errors).<br />
Safety can be very interesting, we have seen some examples specific to TS e.g. <a href="2022-01-09-ts-types-part4.html#preventing-information-escape" target="_blank">no information escape</a>, <a href="2022-01-09-ts-types-part4.html#safety-preventing-unknown" target="_blank">no <code>unknown</code></a>, <a href="2022-01-09-ts-types-part4.html#safety-preventing-subtyping" target="_blank">no subtyping</a>.</p>
<p> <div class="side-note">Here are some very sophisticated examples of safety (outside of TS scope): safe linear algebra (e.g. consistent sizes of matrices in matrix multiplication), safety preventing deadlocks, safe resource management (e.g. no memory leaks, type safety over which resources are used, etc.). One of the wildest type guarantees I have encountered was a guarantee for a linear computation cost.</p>
<p>Safety is really needed and often missing. Here are some examples outside of TS scope (related to memory manangement): <a href="https://visualstudiomagazine.com/articles/2019/07/18/microsoft-eyes-rust.aspx" target="_blank">Microsoft eyes Rust</a>, <a href="https://www.zdnet.com/article/chrome-70-of-all-security-bugs-are-memory-safety-issues/" target="_blank">Security bugs in Chrome</a>.</p>
<p>To summarize what has been said:</p>
<ul>
<li>Best programming practices are not good enough to avoid these problems</li>
<li>Approaches like smart pointers in newer versions of C++ are not good enough either</li>
<li>Type safety: works  </div></li>
</ul>
<p>It should be noted that <code>subtyping</code> reduces safety. We have discussed it extensively in <a href="2022-01-03-ts-types-part3.html#subtyping" target="_blank">Part 3</a></p>
<p>We are seeing a slow industry shift towards a more sophisticated use of types, IMO, TS could play a role in that shift.</p>
<h3 id="monads">Monads</h3>
<p>I have stayed away from the topic thinking that there are enough monad tutorials already, but it is hard to not mention this concept. Monad types provide interesting safety: monads can control the ability to leave monadic computation. A value can easily enter a monad but once there it is hard to leave. This is clearly interesting from the safety standpoint and can be used to achieve all kinds of interesting guarantees. Things get really very interesting in the jigsaw puzzle building department with the addition of dependent types<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a>.</p>
<p>Monads allow for a very imperative code. However, this requires some syntax sugar that the programming language needs to offer. This is called <em>do notation</em> in several languages or <em>for comprehension</em> in Scala. TS does not offer it. That makes monadic computing far less accessible.</p>
<p><a href="https://www.npmjs.com/package/fp-ts" target="_blank"><em>fp-ts</em></a> library provides support for monads and other functional types in TS. Thumbs up to all developers who use it or work on <em>fp-ts</em>.<br />
I am not using <em>fp-ts</em> in my TS project (even though Haskell development is my main job function).<br />
Each project needs to decide on the level of abstraction it allows to make developers working in it productive.</p>
<h2 id="about-correctness">About Correctness</h2>
<p>This series is not about formal verification, types and correctness could be its own blog (or book) series, one I am not qualified to write. I will only point out that, gradual typing or not, in TS correctness and soundness are a baby thrown with the bath water.<br />
Making things conceptually easy at the cost of correctness (e.g. <a href="2022-01-03-ts-types-part3.html#variance-problems" target="_blank">incorrect variance</a>, incorrect or at least very unclear <a href="2022-01-03-ts-types-part3.html#semantics-rejected-overlap" target="_blank">narrowing semantics</a>) should not be on the table.</p>
<p>Subtle falsehoods can sometimes be more concerning than the obvious once.<br />
Here is a coding challenge: There is a common belief that compilation flags like <code>strictNullChecks</code> prevent escaped <code>null</code> and <code>undefined</code>. Exploit the incorrectness of variance in TS to create a partial function that has <code>number</code> return type but returns <code>undefined</code> for some of its input parameter values.</p>
<p>This series discouraged the use of TS’s <code>any</code> type. Undeniably, combining <code>any</code> with stricter types can lead to some very interesting and useful code if one is careful. In a way, I view <code>any</code> to be more straightforward and less damaging than other violations of logical soundness in the TypeScript language.</p>
<h2 id="about-maintainability">About Maintainability</h2>
<p>Considering who is still reading this, I am now only preaching to the quire so I will keep this short. Clearly all the points I have made so far are very related to maintainability.</p>
<p>My favorite definition of high code quality is, simply, a code with a low maintenance cost. IMO, everything else is subjective. Types have a big beneficial impact on that cost.</p>
<p>Developers often go to great lengths to avoid compilation errors. Sure, committing code that does not compile is not very professional but this attitude sometimes goes beyond that. IMO, designing types to be resilient to changes in functionality is not what you want to do. Compilation errors are why we use types, compilation errors are a good thing. What you want are errors that are easy to fix.</p>
<p>It is well known that types can prevent trivial errors (like using a <code>string</code> instead of <code>object</code>). It is hard to catch all such cases in tests and they do show up in production. This is the reason, I believe, TS is used in most of its projects.<br />
Let me point out a less trivial high level bit. Types can simplify adding new functionality! If you think about the app as a big union type of various requirements (this is an oversimplification but let me keep going), then adding a new piece of functionality to that union could give you compilation errors unless you fix all the relevant places. Think about TS-s <a href="2022-01-03-ts-types-part3.html#switch-exhaustive-check" target="_blank"><code>switch</code></a> or <a href="https://dev.to/gvergnaud/bringing-pattern-matching-to-typescript-introducing-ts-pattern-v3-0-o1k" target="_blank"><em>ts-pattern</em></a> library exhaustive checks<a href="#fn6" class="footnote-ref" id="fnref6" role="doc-noteref"><sup>6</sup></a>.</p>
<p> <div class="side-note"><strong>Types and T(est)DD:</strong> If you can just follow the types to add a new functionality, how does this relate to the TDD approach of writing the tests first? To implement new functionality we modify / expand some union types. Hmm, you need to implement the new functionality for the app to compile. You could implement it (at least in TS) by throwing some “Not Implemented” error or maybe changing compilation flags, but that means not taking advantage of the type safety.<br />
IMO types go before TDD. I prefer to write tests after the implementation when types guide my implementation process.  </div></p>
<h2 id="universal">Universal</h2>
<p>Types are more fundamental than a programming language. For example, most FP languages are effectively a syntax sugar over some version of lambda calculus. Lambda calculi come with very well understood formal type semantics.<br />
I am reminded about the <a href="https://www.youtube.com/watch?v=IOiZatlZtGU&amp;t=1816s" target="_blank">Propositions as Types</a> presentation by Phil Wadler himself. It makes a compelling and funny argument that the movie <em>Independence Day</em> got it all wrong. Aliens would not have used C. C is being created by an engineering effort, types and LC are being discovered<a href="#fn7" class="footnote-ref" id="fnref7" role="doc-noteref"><sup>7</sup></a>. Aliens would have discovered typed lambda calculi or have engineered something much different than C or Java.<br />
This is very philosophical, but it also has a pragmatic implication. Discovered programs are, by definition, timeless. If Wadler is right (and if we will keep programming in the future) that would be kinda amazing. In Part 4, I have referenced the <a href="https://www.goodreads.com/book/show/112252.Types_and_Programming_Languages" target="_blank">TAPL</a> book, IMO, the best textbook to learn types. This book is 20 years old. Recursion schemes (Part 5) are 20+ years old. Rank-2 types discussed in Part 4 were studied in 1980-ties and 90-ties. Many language features we consider new and modern are really old ideas, some date back to 1970ties.</p>
<p>Robert Harper has coined a term <a href="https://existentialtype.wordpress.com/2011/03/27/the-holy-trinity/" target="_blank">The Holly Trinity of CS</a> and types are one of the three.</p>
<p>Types are playing an increasing role in foundations of mathematics, the new and “hot” topic is <a href="https://en.wikipedia.org/wiki/Homotopy_type_theory" target="_blank">HoTT</a>.</p>
<p>This series was written by a TypeScript newb. I am using TS since November 2021 and only on one project.<br />
We have covered a lot of ground that probably is not well known to many seasoned TypeScripters. I think the existence of this series provides a good verification for my claim: it is more about knowing the types than it is about knowing the programming language.</p>
<p>Types could unify how we think and talk about programs. Effective development teams are small, the threshold seems to be somewhere around 4-5. Why is that? I had worked once inside a team of 8 (two teams with different core competencies were merged to work on a new project). Design meetings, OMG, we had a hard time agreeing on anything.<br />
Nobody disputes that natural numbers satisfy <code>2 + 2 = 4</code>, and that has to do with types. One of my goals in this series was to sell the idea that types are fundamental to programming and are mostly not something open to endless debates. Types could help facilitate an agreement.</p>
<h3 id="advanced-types-as-patterns">Advanced Types as Patterns</h3>
<p>Advanced types are worth learning even if TypeScript is not able to support them. Advanced uses of types often come with very well behaving principled computations. TypeScript may not be able to express such types in full generality, but it is often possible to use the principled approach as a pattern.<br />
An example is the <a href="https://github.com/rpeszek/ts-experiments/blob/master/ts-notes/src/RecSchemes.ts" target="_blank">Recursion Scheme</a> code I wrote for Part 5. I see <code>map</code> being added to all kinds of types as a pattern. Monads are used as a pattern too. The concept of <em>async</em>, <em>await</em> uses monads as a pattern. <a href="https://www.npmjs.com/package/fast-check" target="_blank"><em>fast_check</em></a> library uses monadic computing as a pattern to accomplish randomized property testing.</p>
<p>The burden to understand the principles lies on the authors of libraries and APIs. For example, developers using <em>async</em> / <em>await</em> do not need to understand the concept of a monad. You need to understand it to create the <em>async / await</em> concept.<br />
It is also much easier to learn the underlying concept after experiencing examples of its use.</p>
<h2 id="unpopular">Unpopular</h2>
<p>There are two directions to writing high quality low defect rate software. Both approaches complement each other.</p>
<ol type="1">
<li>Increase project effort and cost (e.g. testing)</li>
<li>Increase effort / cost outside of project scope (e.g. learning types)</li>
</ol>
<p>In the industry focused on short term goals 2 will be unpopular even if benefits of 2 are significant.<br />
The ramp up time for the projects needs to be short. This explains why all mainstream languages look and feel alike. As far as programming languages go, the software industry is not innovation friendly. Any progress needs to be very gradual. Developers need to be able to “hit the ground running” when using a new language.</p>
<p>I have already mentioned <a href="https://wiki.haskell.org/Wadler%27s_Law" target="_blank">Wadler’s Law</a> and <a href="https://bikeshed.com/" target="_blank">bikeshed</a>. Types are about semantics. That puts them at the far end of the popularity ranking scale. I have mentioned the easy vs simple dilemma. Simple is less popular. Types are theoretical, that makes them less popular as well.</p>
<p>Let’s look at the job market. The job market for typed functional programming jobs is, frankly, dismal. At the same time, languages like Haskell and Rust top the weekend use stats based on stackoverflow surveys<a href="#fn8" class="footnote-ref" id="fnref8" role="doc-noteref"><sup>8</sup></a>.</p>
<p>How can we explain both of these phenomena? One issue is that only a small minority of programmers are interested in the more principled methods of writing code. Weekend learners playing with Rust appear to outnumber devs doing weekend project work in, say, PHP. That is good, but the numbers are still not there. There needs to be a critical mass of enthusiasts and there isn’t one. At the very minimum, managers expect to have a solid supply of headcounts. Managers will consider use of an FP language risky. You do not get fired or criticized for selecting Java.<br />
The other issue is the correlation between interest in types with interest in mathematics. Current rush towards machine learning sways the precious few mathematically inclined CS students towards well paying data science careers.<br />
Yet another issue is education and how mathematics and CS are being taught.</p>
<p>Let’s take a bit more controversial take on this. A stronger version of “Someone is wrong on the internet” is this statement:</p>
<p><em>Lack of popularity is a necessary (not sufficient) condition of doing something right.</em></p>
<p>“Popular =&gt; wrong” is a law (or hypothesis) of life that dates back to at least Socrates.<br />
If you assume this to be true, you can view the progress as a process of being less and less wrong.<br />
People look at the history of the software industry and see a never ending aggressive progress. A more insightful hindsight exposes a history of embracing bad ideas (e.g. <code>null</code>) and resisting good ideas (e.g. type parameters<a href="#fn9" class="footnote-ref" id="fnref9" role="doc-noteref"><sup>9</sup></a>).<br />
You probably think of all of this as too hyperbolic. The benefit of taking this stance is a chance of noticing things that others don’t.</p>
<p>Lack of popularity can translate to some <strong>frustration</strong> for the type enthusiasts. The frustration comes in the form of rejected designs, rejected pool requests, failed job interviews. I heard stories and experienced some of it first hand. That is just part of life, the criticism can have validity as more advanced programming techniques could make the project confusing and not accessible to its contributors.<br />
It also should be expected. One comment I received about Part 1 of this series said “this code is quite different from what we do”. “Different” could imply worth pursuing but unavoidably will at some point lead to a confrontation.</p>
<p>Types lack the critical mass of acceptance to become disruptive, they work well when the team is ready and/or when applied in a gradual way. Thumbs up to projects and developer teams who learn types and select the unpopular!</p>
<blockquote>
<p><em>“Only in our dreams are we free. The rest of the time we need wages.”</em> <em>Terry Pratchett and <a href="https://wiki.lspace.org/Hwel" target="_blank">Hwel</a>. A good metaphor to describe the life of a programmer.</em></p>
</blockquote>
<h3 id="gradual-progress">Gradual Progress</h3>
<p>There is a steady and slow progress. Mainstream languages are introducing a little bit of types and FP.<br />
<code>async</code>-<code>await</code> is now supported by many languages. <em>Sum/variant types</em> are supported by many languages (TS’s union types stand out for their readability). Record types are being introduced as well (e.g. Java 14 <em>records</em>, C# 10 record struts, …). C# has type safe <code>equals</code>. Advanced types in TS we have discussed in <a href="2022-01-09-ts-types-part4.html" target="_blank">Part 4</a> and <a href="2022-02-13-ts-types-part5.html" target="_blank">Part 5</a>. The list slowly grows.</p>
<h2 id="final-words">Final words</h2>
<p>TS does a poor job implementing types. However, it has types and it even allows to do some advanced things with them. The last two installments (<a href="2022-01-09-ts-types-part4.html" target="_blank">Part 4</a> and <a href="2022-02-13-ts-types-part5.html" target="_blank">Part 5</a>) allowed me to go places I would not be able to reach in most mainstream languages.</p>
<p>If developers start using types, the languages will expand support for them.<br />
This will feed some gradual change. The hope for existence of such a feedback loop is what prompted me to write this series.</p>
<p><strong>Should more advanced types be used in a project?</strong> Ideally (and IMO) that decision should be made by the developers. I have presented plenty of <em>pros</em>. The biggest obstacle is the learning curve. I am afraid this learning needs to happen outside of the project work. In reality, this means that the decision has to be made based on what the team knows already. So the answer for some teams could <em>yes</em> today, for some could be <em>no</em> today but a <em>yes</em> in the future.<br />
My personal approach is to make sure that TS code is approachable and my goal is to make it principled within this constraint.<br />
This is not very easy to do, it is much easier to use principled types than principled patterns. It is also easier to write principled code in an environment where principled is not considered odd.<br />
It is also good to be able to scratch the itch and keep practicing the real thing, I have my backend work to do that, lots of people do not have that luxury.</p>
<p>This series was a long journey, I am happy I took it, but I am also happy the effort is now mostly behind me. Big thanks to all of you who stayed with me all the way to this end.<br />
Thank you to everyone who messaged me corrections and comments. Please let me know your thoughts on this installment.<br />
Good luck with your projects, I hope you will use types!</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>Not very relevant to TS, but could be interesting to philosophically inclined readers. Can a function returning, say, the current time be ever referentially transparent? Some languages (like Idris or Haskell) are big on referential transparency. In such languages functions can be executed only inside <code>main</code>. Evaluating a timestamp function would not return the current time. Instead, it would return a computation that returns the time when eventually executed inside <code>main</code>. This allows some purists to claim that the function is referentially transparent and has practical implications too. You could simulate a similar purity in TS by returning <a href="2022-02-13-ts-types-part5.html#thunks-and-callbacks-never-and-unknown." target="_blank">thunks</a>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>You can see some of it in this <a href="https://www.youtube.com/watch?v=DRq2NgeFcO0" target="_blank">youtuble</a><a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3" role="doc-endnote"><p>In TS, of course, we have the ability to configure the compiler to verify null safety. In some languages (e.g. JS) you also get partial function by writing code that in certain cases simply does not return. TS is relatively good in preventing this situation, compiler will say “Not all code paths return a value”.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4" role="doc-endnote"><p>These languages can be used as proof assistants. One could prove anything (including falsehoods) using, say, unbound recursion. Proof assistant that does not check totality would not be assisting well, would it?<br />
Side note: When using proof assistant, you are proving a type and you are proving it by implementing it.<br />
This equivalence has a name: Curry-Howard correspondence. “Propositions are types, proofs are programs”.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5" role="doc-endnote"><p><a href="https://www.manning.com/books/type-driven-development-with-idris" target="_blank">TDD in Idris</a> book contains some very interesting and accessible examples of monadic computations in dependently typed setting.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn6" role="doc-endnote"><p>It is good to note that this safety is unique to union types, you will not get the same safety when adding a property to an object. It is interesting and telling that the industry is adding co-product types to programming languages just now.<a href="#fnref6" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn7" role="doc-endnote"><p>A similar and relevant philosophical discussion has been happening in mathematics for centuries (see <a href="https://en.wikipedia.org/wiki/Philosophy_of_mathematics#Mathematical_realism" target="_blank">wikipedia</a>). My opinion on this is that a creative process tends to be iterative leaving a historical evidence of iterations. Mathematics, for most part, has been additive. There was rarely a need to rewire an old theory. As far as I know not in last 100 years.<a href="#fnref7" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn8" role="doc-endnote"><p>I remember Haskell being firmly in the first position for the stackoverflow weekend use statistics for several years. I found this link: <a href="https://stackoverflow.blog/2017/02/07/what-programming-languages-weekends/" target="_blank">2017</a>. These stats are hard to find but I also found this one: <a href="https://stackoverflow.blog/2019/10/28/research-update-coding-on-the-weekends/" target="_blank">2019</a>. In 2019 Rust moved ahead of Haskell.<br />
At the same time, the job ranking (based on the UK’s <a href="https://www.itjobswatch.co.uk/jobs/uk/haskell.do" target="_blank">IT Jobs Watch</a>, I have not found a similar ranking for the US.) puts Haskell at 932 as of 2022/02/06. Haskell moved ahead of COBOL in that ranking in 2017.<br />
This ranking is possibly exaggerated too, lots of jobs list Haskell and good to have but will have you code in PHP. This bias exist in any language but is stronger for something like Haskell than say COBOL.<a href="#fnref8" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn9" role="doc-endnote"><p>As an example, Java has resisted type variables for a long time. “Although our generics extensions were put on hold for six years, Sun developed a much keener interest in the compiler I had written for GJ. It proved to be more stable and maintainable than their first Java compiler. So they decided to make the GJ compiler the standard javac compiler from their 1.3 release on, which came out in 2000.” (<a href="https://www.artima.com/articles/the-origins-of-scala" target="_blank">quote from Martin Odersky</a>). Generics remained disabled until Java version 1.5 (2004). Oderky is always very diplomatic in his statements.<a href="#fnref9" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

    </section>
</article>
]]></summary>
</entry>
<entry>
    <title>Type Enthusiast's Notes about TypeScript. Part 5. Advanced Types</title>
    <link href="https://rpeszek.github.io//posts/2022-02-13-ts-types-part5.html" />
    <id>https://rpeszek.github.io//posts/2022-02-13-ts-types-part5.html</id>
    <published>2022-02-13T00:00:00Z</published>
    <updated>2022-02-13T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on February 13, 2022
        
            by Robert Peszek
        
        
        
        <div class="changelog"> <div>Revision History: <ul> <li> (2022.02.14) Fixed list example <a href="#fn2">footnote [2]</a> </li> <li> (2022.02.15) Clarifying comments in <a href="#type-level-programming" < a>type level programming</a> code examples</li> <li> (2022.05.29) Draft warning removed </li> </ul> </div></div>
        
        
        <div class="info">Tags: <a title="All pages tagged 'TypeScript-Notes'." href="../tags/TypeScript-Notes.html">TypeScript-Notes</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <div class="toc"><div class="header">Table of Contents</div>
<ul>
<li><a href="#nutshell">Nutshell</a></li>
<li><a href="#recursive-types">Recursive types</a></li>
<li><a href="#type-level-programming">Type level programming</a></li>
<li><a href="#subtyping">Subtyping</a></li>
<li><a href="#thunks-and-callbacks-never-and-unknown.">Thunks and callbacks, <code>never</code> and <code>unknown</code>.</a></li>
<li><a href="#next-and-the-final-chapter">Next and the final Chapter</a></li>
</ul>
</div>
<p><em>Please Leave Feedback in: <a href="https://github.com/rpeszek/rpeszek.github.io/discussions/1" target="_blank">git discussions</a></em></p>
<p>Previous post: <a href="2022-01-09-ts-types-part4.html" target="_blank">Part 4. Programming with Type Variables</a>.</p>
<p><strong>Disclaimers:</strong> (imagine this is a very small font, read it very fast in a half whisper)<br />
<em>I assume strict compiler flags are on, something you get by default with scaffolding, e.g. using <code>create-react-app my-project --template typescript</code> is close enough.<br />
The code examples have been tested with TypeScript v4.5.2.<br />
This post is a pandoc output of a markdown document and code examples are not interactive.<br />
Most of the code examples are published in <a href="https://github.com/rpeszek/ts-experiments/tree/master/ts-notes" target="_blank">ts-notes</a> folder in this github repo: <a href="https://github.com/rpeszek/ts-experiments" target="_blank">ts-experiments</a>.</em></p>
<p><strong>Motivating Quote for the series:</strong></p>
<blockquote>
<p>“TypeScript began its life as an attempt to bring traditional object-oriented types to JavaScript so that the programmers at Microsoft could bring traditional object-oriented programs to the web. As it has developed, TypeScript’s type system has evolved to model code written by native JavaScripters. The resulting system is <em>powerful, interesting and messy.</em>”</p>
</blockquote>
<p><em>From typescriptlang <a href="https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html" target="_blank">TypeScript for Functional Programmers</a></em></p>
<h2 id="nutshell">Nutshell</h2>
<p>This is the fifth post in the series devoted to types in TypeScript. In this series, I explore type-centric approaches to writing code and push TS to its limits in doing so. I am writing these posts for like minded developers who are interested in types and either use or consider using TypeScript.</p>
<p>In the last post I referenced the <a href="https://www.goodreads.com/book/show/112252.Types_and_Programming_Languages" target="_blank">Types and Programming Languages</a> book. Similarly to the previous post, this installment will be a little more advanced and a little TAPL-ish. I will also introduce a tiny bit of Category Theory. A great blog series (really a book) about Categories is <a href="https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/" target="_blank">Category Theory for Programmers</a>, here it is <a href="https://www.goodreads.com/en/book/show/33618151-category-theory-for-programmers" target="_blank">on goodreads</a>.</p>
<h2 id="recursive-types">Recursive types</h2>
<p><code>type JsonVal</code> from <a href="2021-12-12-ts-types-part1.html#typescript-is-great" target="_blank">Part 1</a> surprised me. It is recursive, the name <code>JsonVal</code> appears on both the LHS and the RHS of the definition. Here is this definition repeated:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a>type JsonVal <span class="op">=</span> </span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;object&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> <span class="bu">Map</span><span class="op">&lt;</span>string<span class="op">,</span> JsonVal<span class="op">&gt;</span>}</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;array&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> JsonVal[]}</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;string&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> string}</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;number&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> number}</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;bool&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> boolean}</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;null&quot;</span>}</span></code></pre></div>
<p>and there are TAPLish reasons why this is interesting:</p>
<p> <div class="side-note">The two established approaches for implementing recursive types in a programming language are</p>
<ul>
<li><em>iso-recursion</em> (good fit for nominal types<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>). If you know <em>recursion schemes</em>, the compilation technique is very similar to how the <code>Fix</code> type and the <em>recursion schemes</em> work in nominally typed languages like <em>Scala</em>, <em>Haskell</em>, etc. You kinda roll (Fix) or unroll (unFix) one layer of recursion at the time.</li>
<li><em>equi-recursion</em> (good fit for structural types). There is no <code>Fix/unFix</code> game. The structure is already unraveled into a potentially infinite beast. The compiler needs to deal with the whole beast. This approach is much harder to implement.</li>
</ul>
<p><code>JsonVal</code> looks like an equi-recursive definition. The methodology behind equi-recursion involves monotone functions and other things I never found time to understand very well. Hard stuff and quite a bit of math. I have not dug deep enough to know how TS compiles <code>JsonVal</code> like types. No matter what it does, it is IMO impressive.<br />
 </div></p>
<p><code>JsonVal</code>-like types appear to be hard on the TS type checker. I have played with some advanced recursive types and have experienced it first hand. I got quite a few</p>
<blockquote>
<p>‘Type instantiation is excessively deep and possibly infinite’</p>
</blockquote>
<p>compiler errors (e.g. code in <a href="https://github.com/rpeszek/ts-typecheck-peano" target="_blank">https://github.com/rpeszek/ts-typecheck-peano</a>). However, I did not succeed in creating a simple example to demonstrate this.</p>
<p>Here is another example of a recursive type:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a>type List<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">=</span> </span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;nil&quot;</span>} </span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;cons&quot;</span><span class="op">,</span> <span class="dt">head</span><span class="op">:</span> T<span class="op">,</span> <span class="dt">tail</span><span class="op">:</span> List<span class="op">&lt;</span>T<span class="op">&gt;</span>}</span></code></pre></div>
<p>That is a recursive definition of a functional <em>cons</em> list<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>.</p>
<p>IMO, it is impressive that TS is able to pull these off. I consider this feature very useful and underutilized by the ecosystem. Here is some more advanced use of recursive types:</p>
<p> <div class="side-note">The github repo with code examples for this series includes <a href="https://github.com/rpeszek/ts-experiments/blob/master/ts-notes/src/RecSchemes.ts" target="_blank">RecSchemes.ts</a>. This module contains code that allows for <em>folding</em> (TS/JS ecosystem tends to use the term <em>reduce</em>) and <em>unfolding</em> of arbitrary JSON values (expressed as the above <code>JsonVal</code>).</p>
<p>Such approach is called <em>Recursion Schemes</em>. If you are not familiar with this concept, you are likely to have two reactions: “the code looks surprisingly terse” and “WTF is going on”. IMO any code that solicits these 2 reactions is worth exploring. The first suggests a principled code, the second suggests an opportunity to internalize some fundamental principles.</p>
<p>One high level intuition about recursion schemes is that they abstract out/hide recursion.<br />
Readers not familiar with Recursion Schemes should try implementing an analogous <code>fold</code> for the above <code>List</code> type and compare its type with TS’s array <code>reduce</code>. Recursion schemes are not easy, at least they were not easy to learn for me.<br />
Since this technique can be applied to many other recursive types, recursion schemes could be used as a pattern in TS.</p>
<p>Recursion Schemes are firmly rooted in theory. For example, the <code>fold</code> and <code>unfold</code> definitions in my example follow from categorical concepts explained <a href="https://bartoszmilewski.com/2017/02/28/f-algebras/" target="_blank">here</a><a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a>.<br />
This technique is also very useful. Examples are: manipulating XML documents, rewriting AST (syntax trees) of interpreted programs. A lot of code at my current work is using recursion schemes (we are not doing it in TS though). In the TS/JS world, you could think about presenting very nested data by folding it into a nested React component. Working with any recursive type is likely to benefit from using recursion schemes.</p>
<p>Even though TS is not capable of implementing recursion schemes the way they are done in Haskell or Scala, there is some simplifying benefit of TS’s structural typing. The linked code examples explain this in code comments. It is really nice that code like this is possible in TS.  </div></p>
<h2 id="type-level-programming">Type level programming</h2>
<p>TS literal types are singletons (i.e. type <code>"boo"</code> has exactly one value <code>"boo":"boo"</code>). This allows singletons to magically connect types with values and values with types. That provides a lot of power to create very precise types.<br />
Literal types should not be that hard to implement in a programming language and it is interesting why they are so uncommon. Kudos to TS for introducing these! They are, clearly, a great fit for JS.<br />
However, TS literal types are very limited in scope (I remember reading somewhere that it was a design decision). For example, you can do some very basic type level string manipulation but you cannot concatenate strings or do any arithmetic on type level numbers and you have no way of defining any additional features on your own (e.g. DIY number addition).</p>
<p>TypeScript allows for type-level ternary (<em>Conditional Types</em>) as well as various type-level built-in functions (e.g. <code>keyof</code>).<br />
Apparently, the type level programming in TypeScript is <em>Turing Complete</em> (see <a href="https://github.com/microsoft/TypeScript/issues/14833" target="_blank">https://github.com/microsoft/TypeScript/issues/14833</a>).<br />
However, type level programming in TS is focused on creating type safety for various JS code idioms rather than creating a foundation for DIY type level programming. IMO this makes it harder to learn. The <em>Turing completeness</em> appears to be a completely accidental language feature.</p>
<p>Type level programming can be very useful, we have seen some of it in action in the <a href="2022-01-09-ts-types-part4.html" target="_blank">previous post</a> where we were able to prevent subtyping and prevent compiler from using the <code>unknown</code> type.</p>
<p>IMO the best language design direction is for the type level and the value level code to look the same (e.g. dependently typed language like Idris). The second best approach is for type level and value level to be very similar (e.g. Haskell).<br />
TS cannot and should not do either. We do not want JavaScript (or very similar) on the type level!</p>
<p>At the same time, the lack of synergy between type level and value level programs makes things very complicated. E.g.:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="co">//example type from https://www.typescriptlang.org/docs/handbook/2/conditional-types.html</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a>type Flatten<span class="op">&lt;</span>Type<span class="op">&gt;</span> <span class="op">=</span> Type <span class="kw">extends</span> <span class="bu">Array</span><span class="op">&lt;</span>infer Item<span class="op">&gt;</span> <span class="op">?</span> Item <span class="op">:</span> Type<span class="op">;</span> </span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a><span class="kw">const</span> head <span class="op">=</span> <span class="op">&lt;</span>T<span class="op">&gt;</span> (t<span class="op">:</span> T[]) <span class="op">:</span> Flatten<span class="op">&lt;</span>T[]<span class="op">&gt;</span> <span class="kw">=&gt;</span> {</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a>    <span class="cf">return</span> t[<span class="dv">0</span>] <span class="co">//compiles (as expected)</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a>}</span></code></pre></div>
<div class="sourceCode" id="cb4"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="dt">const</span> generalizedHead = &lt;T&gt; (t: T) : Flatten&lt;T&gt; =&gt; {</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a>    <span class="kw">if</span>(<span class="bu">Array</span>.<span class="fu">isArray</span>(t)) </span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a>        <span class="kw">return</span> t[<span class="dv">0</span>]  <span class="co">//still compiles (as expected)</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a>    <span class="kw">else</span> </span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a>        <span class="kw">return</span> t <span class="co">//compiler error: Type 'T' is not assignable to type 'Flatten&lt;T&gt;' (not as I would expect)</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a>}</span></code></pre></div>
<p>here is another one:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a>type HasContent<span class="op">&lt;</span>C<span class="op">&gt;</span> <span class="op">=</span> {<span class="dt">content</span><span class="op">:</span> C}</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>type GetContent<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">=</span> T <span class="kw">extends</span> HasContent <span class="op">&lt;</span>infer C<span class="op">&gt;</span> <span class="op">?</span> C <span class="op">:</span> T</span></code></pre></div>
<div class="sourceCode" id="cb6"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="dt">const</span> getContent = &lt;C, T <span class="kw">extends</span> HasContent&lt;C&gt;&gt; (t: T): GetContent&lt;T&gt; =&gt; {</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>   <span class="co">//return t.content //compiler error:  Type 'C' is not assignable to type 'GetContent&lt;T&gt;' (not as expected)</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>}</span></code></pre></div>
<p>It feels clunky. It feels like type level and value level have a broken marriage. This lack of synergy also feels very confusing.</p>
<p>I think TS type level programming will keep improving and we may see some very interesting use cases in the future.</p>
<h2 id="subtyping">Subtyping</h2>
<p>This series has discussed subtyping already. I will keep this section comparatively short.</p>
<p>Personally, I try to avoid using subtyping features. Subtyping is related to Object Orientation. OO programming has an appeal of simplicity and I was seduced by it for many years. It took me a long time to realize that OO is not that simple. Today, I think about OO as very complex. Even language designers often get it wrong (this series has provided a lot of evidence for this statement in the context of TypeScript). The first 3 parts of this series could have been alternatively titles “Dangers of OO with examples in TS”<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>. This negative view of OO should be filed under IMO as many developers disagree.</p>
<p>Before continuing reading pass this code, please try to implement (at least in your head) the <code>amIFooOrBar</code> function:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="kw">function</span> verifyExtends<span class="op">&lt;</span>T2 <span class="kw">extends</span> T1<span class="op">,</span> T1<span class="op">&gt;</span>() {}</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a><span class="co">//more specific, fewer variants</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a>type FooOrBar <span class="op">=</span>  </span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">foo</span><span class="op">:</span> string} </span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">bar</span><span class="op">:</span> string}</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a><span class="co">//A challenge: implement this function:</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">amIFooOrBar</span>(o<span class="op">:</span> FooOrBar)<span class="op">:</span> <span class="st">&quot;foo&quot;</span> <span class="op">|</span> <span class="st">&quot;bar&quot;</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">genFooOrBar</span>()<span class="op">:</span> FooOrBar</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a><span class="co">//more general, more variants</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a>type FooOrBarOrBuz <span class="op">=</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">foo</span><span class="op">:</span> string} </span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">bar</span><span class="op">:</span> string}</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">baz</span><span class="op">:</span> string}</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true"></a></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">genFooOrBarOrBuz</span>()<span class="op">:</span> FooOrBarOrBuz</span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true"></a></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true"></a><span class="kw">const</span> fooOrBarOrBuz<span class="op">:</span> FooOrBarOrBuz <span class="op">=</span> <span class="fu">genFooOrBar</span>() <span class="co">//compiles assigns specific to more general </span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true"></a><span class="co">//const fooOrBar: FooOrBar = genFooOrBarOrBuz() //will not compile tries to assign general to more specific</span></span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true"></a></span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span>FooOrBar<span class="op">,</span> FooOrBarOrBuz<span class="op">&gt;</span>() <span class="co">//compiles, FooOrBar extends FooOrBarOrBuz</span></span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true"></a><span class="co">//verifyExtends&lt;FooOrBarOrBuz, FooOrBar&gt;() //does not compile, FooOrBarOrBuz does not extend FooOrBar</span></span></code></pre></div>
<p>The thing to remember is that <code>{foo: string} | {bar: string}</code> extends <code>{foo: string} | {bar: string}| {baz: string}</code> not the other way around.</p>
<p>Did you implement <code>amIFooOrBar</code>, great, let’s move on.</p>
<p>Subtyping in object types will feel familiar to OO developers. Roughly speaking, you can assign object with more properties to object with fewer properties:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a>type FooAndBar <span class="op">=</span> {<span class="dt">foo</span><span class="op">:</span> string<span class="op">,</span> <span class="dt">bar</span><span class="op">:</span> string} <span class="co">//more general</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">genFooAndBar</span>()<span class="op">:</span> FooAndBar</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>type FooAndBarAndBaz <span class="op">=</span> {<span class="dt">foo</span><span class="op">:</span> string<span class="op">,</span> <span class="dt">bar</span><span class="op">:</span> string<span class="op">,</span> <span class="dt">baz</span><span class="op">:</span> string} <span class="co">//more specific</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">genFooAndBarAndBaz</span>()<span class="op">:</span> FooAndBarAndBaz</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a><span class="kw">const</span> fooAndBar<span class="op">:</span> FooAndBar <span class="op">=</span> <span class="fu">genFooAndBarAndBaz</span>()  <span class="co">//specific assigned to general is valid assignment</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a><span class="co">//const fooAndBarAndBuz: FooAndBarAndBaz = genFooAndBar() // will not compile, tries to assign general to specific</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span>FooAndBarAndBaz<span class="op">,</span> FooAndBar<span class="op">&gt;</span>() <span class="co">//compiles, FooAndBarAndBaz extends FooAndBar</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a><span class="co">//verifyExtends&lt;FooAndBar, FooAndBarAndBaz&gt;() //does not compile, FooAndBar does not extend FooAndBarAndBaz</span></span></code></pre></div>
<p>Subtyping gets more involved if you combine adding properties to objects and variants to union types. In TS subtyping extends to functions which makes things even more complex (leading to what I called <a href="2021-12-12-ts-types-part1.html#compilation-bloopers" target="_blank">compilation bloopers</a> in Part 1). But I think the above examples cover the basic idea.</p>
<p>Now let’s revisit the above challenge. What will your function return in this call:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="co">// challenge check:</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="co">// what does your function return when used on this value?</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a><span class="co">// </span><span class="al">NOTE</span><span class="co"> this does compile, you can assign FooAndBar to FooOrBar, since 'and' implies 'or'</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a><span class="kw">const</span> whatIsThat <span class="op">=</span> <span class="fu">amIFooOrBar</span>({<span class="dt">foo</span><span class="op">:</span> <span class="st">&quot;foo&quot;</span><span class="op">,</span> <span class="dt">bar</span><span class="op">:</span> <span class="st">&quot;bar&quot;</span>}) </span></code></pre></div>
<p>This is just one of the many gotchas associated with subtyping.</p>
<h2 id="thunks-and-callbacks-never-and-unknown.">Thunks and callbacks, <code>never</code> and <code>unknown</code>.</h2>
<p>To finish this post I want to pick 4 concepts fundamental to TypeScript: variables, callbacks, <code>never</code> and <code>unknown</code> types and discuss how they relate in a somewhat more theoretical setting. I believe the relationship between these concepts is not commonly understood.</p>
<p>We have seen <code>&lt;T&gt; () =&gt; T</code> before, we called it a <a href="2021-12-12-ts-types-part1.html#type-holes" target="_blank">type hole <code>_: &lt;T&gt;() =&gt; T</code></a>. Now I am changing my mind and want to call it a generic thunk.<br />
We can think about it as a ‘lazy’ value.<br />
Instead of defining <code>const t: T</code> (which, incidentally, TS does not allow on the top level<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a>) I can define a function that, when called, will return me that <code>t</code>. Basically thunks are variables you put the <code>()</code> after. A referentially transparent (no side-effects) <code>&lt;T&gt; () =&gt; T</code> thunk is morally equivalent to a variable of type <code>T</code>.</p>
<p>A thunk produces a value of type <code>T</code>. A generic callback <code>&lt;T&gt; (_: T) =&gt; void</code> consumes a value of type <code>T</code>. There is, clearly, some type of duality between thunks and callbacks.<br />
Incidentally, many programming languages define a <em>unit</em> type often denoted as <code>()</code> instead of the <em>C</em>-style <code>void</code>. If this was the case for TS, we would have written: <code>&lt;T&gt; T =&gt; ()</code> for the callback and <code>&lt;T&gt; () =&gt; T</code> for the thunk. You can get from one type to the other by reversing the arrow <code>=&gt;</code>. These concepts become dual in the categorical sense. This post is not about Category Theory but this section has just a tiny bit of it.</p>
<p>In TS, the generic thunk <code>&lt;T&gt; () =&gt; T</code> type is equivalent to <code>never</code>.<br />
You may remember that the <a href="2021-12-12-ts-types-part1.html#type-holes" target="_blank">type hole <code>_()</code></a>, was implemented by throwing an error (that is <code>never</code> in TS).<br />
<code>never</code> assigns to everything but you cannot assign anything else to it. Well, except for the generic thunk:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="co">//thunk assigned to never</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="kw">const</span> nevr <span class="op">:</span> never <span class="op">=</span> <span class="fu">_</span>()</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a><span class="im">export</span> <span class="kw">const</span> __neverFn<span class="op">:</span> () <span class="kw">=&gt;</span> never <span class="op">=</span>  _</span></code></pre></div>
<p>In other words <code>&lt;T&gt; () =&gt; T</code> and <code>() =&gt; never</code> can be assigned to each other, thus, I consider them equivalent.</p>
<p>If you replay the same argument with arrows reversed, you will establish equivalence between the generic callback <code>&lt;T&gt; (_: T) =&gt; ()</code> and the <code>unknown</code> callback types:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">someUnknownCallback</span>(t<span class="op">:</span> unknown)<span class="op">:</span> <span class="kw">void</span> </span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a><span class="kw">const</span> overbar<span class="op">:</span> <span class="op">&lt;</span>T<span class="op">&gt;</span>(_<span class="op">:</span>T) <span class="kw">=&gt;</span> <span class="kw">void</span> <span class="op">=</span>  someUnknownCallback</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a>declare <span class="kw">function</span> someOverbar<span class="op">&lt;</span>T<span class="op">&gt;</span>(t<span class="op">:</span>T)<span class="op">:</span> <span class="kw">void</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a><span class="kw">const</span> unknownCallback<span class="op">:</span> (_<span class="op">:</span> unknown) <span class="kw">=&gt;</span> <span class="kw">void</span> <span class="op">=</span> someOverbar</span></code></pre></div>
<p>The <code>never</code> type is the TS’s <em>bottom</em> type (can be assigned to anything), while the <a href="2021-12-24-ts-types-part2.html#note-about-the-unknown-type" target="_blank"><code>unknown</code></a> type is the TS’s <em>top</em> type (anything can be assigned to it). These concepts are also dual in the sense of reversing the direction of assignment.</p>
<p>Let’s think about referential transparency again. There are no interesting referentially transparent functions that return <code>void</code>. To do something meaningful, such a function would need to mutate some shared state or do some other effectful things. E.g. when coding in React, a callback could compute a new state (let me call it <code>r: R</code>) and invoke a state hook to make the change. I like to think about such a callback as having an imaginary type <code>&lt;T,R&gt; (t: T) =&gt; R</code>.</p>
<p>The duality between variables/thunks and callback is quite fascinating and has some depth.<br />
Let’s fix the type variable <code>T</code> to, say, <code>Person</code>. Any type would do, I just want to remove the quantification (remove the genericity) to simplify my explanation.<br />
JS / TS programs often use higher order functions that accept callbacks as parameters. Consider a callback that accepts a callback <code>(f: (_: Person) =&gt; void) =&gt; void</code> and computes the same value. The imaginary referentially transparent type for it could be</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="co">//TS-like pseudocode</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="op">&lt;</span>R<span class="op">&gt;</span> (f<span class="op">:</span> (_<span class="op">:</span> Person) <span class="kw">=&gt;</span> R) <span class="kw">=&gt;</span> R</span></code></pre></div>
<p>As it turns out, this type is equivalent (isomorphic) to the thunk <code>() =&gt; Person</code> (or, ignoring side-effects, to just <code>Person</code>)!<br />
They are not equivalent based on assignments, they are equivalent because one can be easily converted to the other.</p>
<p>It kinda makes sense for a dual of a dual to end up back where we started.<br />
However, this equivalence is a bit stronger, in a sense that it holds for every fixed type <code>T</code>. It is also weaker, since what we get is only isomorphism<a href="#fn6" class="footnote-ref" id="fnref6" role="doc-noteref"><sup>6</sup></a>.</p>
<p>This equivalence is a special case of <a href="https://bartoszmilewski.com/2015/09/01/the-yoneda-lemma/" target="_blank"><em>Yoneda Lemma</em></a> in Category Theory<a href="#fn7" class="footnote-ref" id="fnref7" role="doc-noteref"><sup>7</sup></a>.</p>
<p>I can express this succinctly in TS (note a <a href="2022-01-09-ts-types-part4.html#higher-rank-types" target="_blank">higher rank type</a> is used) as:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="co">//all of these compile</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a>type Yoneda<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">=</span> () <span class="kw">=&gt;</span> <span class="op">&lt;</span>R<span class="op">&gt;</span>(f<span class="op">:</span> (_<span class="op">:</span> T) <span class="kw">=&gt;</span> R) <span class="kw">=&gt;</span> R</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a>type Thunk<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">=</span> () <span class="kw">=&gt;</span> T</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a><span class="co">//Yoneda&lt;T&gt; is isomorphic to Thunk&lt;T&gt;</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true"></a><span class="co">//here are functions defining the isomorphism:</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true"></a><span class="kw">const</span> toYoneda <span class="op">=</span> <span class="op">&lt;</span>T<span class="op">&gt;</span> (th<span class="op">:</span> Thunk<span class="op">&lt;</span>T<span class="op">&gt;</span>)<span class="op">:</span> Yoneda<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="kw">=&gt;</span> {</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true"></a>   <span class="kw">const</span> res <span class="op">=</span> () <span class="kw">=&gt;</span> <span class="op">&lt;</span>R<span class="op">&gt;</span> (<span class="dt">f</span><span class="op">:</span> (<span class="dt">_</span><span class="op">:</span> T) <span class="kw">=&gt;</span> R)<span class="op">:</span> R <span class="kw">=&gt;</span> <span class="fu">f</span>(<span class="fu">th</span>())</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true"></a>   <span class="cf">return</span> res</span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true"></a>}</span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true"></a></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true"></a><span class="kw">const</span> fromYoneda <span class="op">=</span> <span class="op">&lt;</span>T<span class="op">&gt;</span> (y<span class="op">:</span> Yoneda<span class="op">&lt;</span>T<span class="op">&gt;</span>)<span class="op">:</span> Thunk<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="kw">=&gt;</span> {</span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true"></a>    <span class="kw">const</span> res <span class="op">=</span> ()<span class="op">:</span> T <span class="kw">=&gt;</span> <span class="fu">y</span>()(x <span class="kw">=&gt;</span> x)</span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true"></a>    <span class="cf">return</span> res</span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true"></a> }</span></code></pre></div>
<p>Programmers are divided into 2 camps when exploring this type of information: some consider it fascinating and important, some consider it a lot of useless nonsense. If you are still reading this series, chances are you are in the first camp.</p>
<p> <div class="side-note"><strong>Callback in JS:</strong> I believe, JavaScripters intuitively know that equivalence and callbacks are viewed almost as a coding style. JS uses callbacks to accomplish all kinds of things. Except, for some reason, JS decided to endure <em>callback hell</em> for about 2 decades. Today’s <code>async</code> / <code>await</code> code finally brings an end to that mystery.<br />
Understanding that programming with callbacks (often called <em>Continuation Passing Style</em><a href="#fn8" class="footnote-ref" id="fnref8" role="doc-noteref"><sup>8</sup></a>) and vanilla synchronous programming can offer very similar interface dates back to very early 1990-ties. This has to do with the programming abstraction that also comes from Category Theory called <em>Monad</em>.  </div></p>
<p>Category Theory is very related to types and to programming in general, I found it only fitting to finish this installment with a note that discussed a little bit of it.<br />
Bartosz Milewski’s CTFP book linked above starts with code examples in C++ and in Haskell. Bartosz gives up on C++ very fast. I think it would be possible to stay on a little longer by selecting TS instead of C++. Kudos to TS!</p>
<h2 id="next-and-the-final-chapter">Next and the final Chapter</h2>
<p>I will finish the series with some final thoughts and rants.<br />
The last 2 installments got a little on an advanced side of things. One question I have been asking myself is: When should more advanced types be used in a TS project?</p>
<p>Here is the link: <a href="2022-03-13-ts-types-part6.html" target="_blank">Part 6</a></p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>As we have discussed in <a href="2022-01-09-ts-types-part4.html#phantom-types" target="_blank">Part 3</a>, TS types are structural. That means the name <code>Person</code> in <code>type Person = {firstNm: String, lastNm: String}</code> is only an alias, what defines the type is the RHS of the definition, not the LHS. Contrast this with an OO class definition in a language like Java. Two structurally identical classes are still considered different types (this is called <em>nominal</em> typing).<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>I originally posted an issue related to the list and I misinterpreted the problem behind it. Thanks to <a href="https://www.reddit.com/user/joelahoover" target="_blank">u/joelahoover</a> for pointing it out. TS tends to widen literal strings to <code>string</code>. So a value defined as <code>const v = {type: "cons", 1, tail: {type: 'nil'}}</code> or even <code>const empty = {type: 'nil'}</code> are not valid lists unless you use something like <code>as const</code>, e.g. <code>const empty = {type: "nil"} as const</code>. TS does not try to infer the best possible type (it does not care about what is called principal typing).<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3" role="doc-endnote"><p>The code examples in the linked CTFP chapter require a <code>Fix</code> type that allows for rolling (applying <code>Fix</code>) and unrolling (deconstructing <code>Fix</code>), this complexity is due to nominal typing and iso-recursion, TS makes things actually simpler.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4" role="doc-endnote"><p>This was not intentional. In fact, I have not realized until finishing the series that many compilation gotchas I have presented in Part 1 are rooted in OO.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5" role="doc-endnote"><p>You can use <code>const t: T</code> only inside functions that declare <code>T</code> in its type.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn6" role="doc-endnote"><p>Think about isomorphism as being able to convert one type to the other without any information loss or gain<a href="#fnref6" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn7" role="doc-endnote"><p>It is Yoneda applied to the <em>Identity functor</em><a href="#fnref7" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn8" role="doc-endnote"><p>I believe the term Continuation Passing Style goes back as far as 1950ties.<a href="#fnref8" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

    </section>
</article>
]]></summary>
</entry>
<entry>
    <title>Type Enthusiast's Notes about TypeScript. Part 4. Programming with Type Variables</title>
    <link href="https://rpeszek.github.io//posts/2022-01-09-ts-types-part4.html" />
    <id>https://rpeszek.github.io//posts/2022-01-09-ts-types-part4.html</id>
    <published>2022-01-09T00:00:00Z</published>
    <updated>2022-01-09T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on January  9, 2022
        
            by Robert Peszek
        
        
        
        <div class="changelog"> <div>Revision History: <ul> <li> (2022.05.10 - 2022.05.29) Minor edits </li> <li> (2022.05.29) Draft warning removed </li> </ul> </div></div>
        
        
        <div class="info">Tags: <a title="All pages tagged 'TypeScript-Notes'." href="../tags/TypeScript-Notes.html">TypeScript-Notes</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <div class="toc"><div class="header">Table of Contents</div>
<ul>
<li><a href="#nutshell">Nutshell</a></li>
<li><a href="#safety-preventing-unknown">Safety preventing <code>unknown</code></a></li>
<li><a href="#type-variable-scoping">Type variable scoping</a></li>
<li><a href="#higher-rank-types">Higher Rank types</a></li>
<li><a href="#existential-types">Existential types</a>
<ul>
<li><a href="#replacing-factory-pattern">Replacing factory pattern</a></li>
<li><a href="#preventing-information-escape">Preventing information escape</a></li>
</ul></li>
<li><a href="#safety-preventing-subtyping">Safety preventing subtyping</a></li>
<li><a href="#phantom-types">Phantom types</a></li>
<li><a href="#next-chapter">Next Chapter</a></li>
</ul>
</div>
<p><em>Please Leave Feedback in: <a href="https://github.com/rpeszek/rpeszek.github.io/discussions/1" target="_blank">git discussions</a></em></p>
<p>Previous post: <a href="2022-01-03-ts-types-part3.html" target="_blank">Part 3. TS Complexity</a>.</p>
<p><strong>Disclaimers:</strong> (imagine this is a very small font, read it very fast in a half whisper)<br />
<em>I assume strict compiler flags are on, something you get by default with scaffolding, e.g. using <code>create-react-app my-project --template typescript</code> is close enough.<br />
The code examples have been tested with TypeScript v4.5.2.<br />
This post is a pandoc output of a markdown document and code examples are not interactive.<br />
Most of the code examples are published in <a href="https://github.com/rpeszek/ts-experiments/tree/master/ts-notes" target="_blank">ts-notes</a> folder in this github repo: <a href="https://github.com/rpeszek/ts-experiments" target="_blank">ts-experiments</a>.</em></p>
<p><strong>Motivating Quote for the series:</strong></p>
<blockquote>
<p>“TypeScript began its life as an attempt to bring traditional object-oriented types to JavaScript so that the programmers at Microsoft could bring traditional object-oriented programs to the web. As it has developed, TypeScript’s type system has evolved to model code written by native JavaScripters. The resulting system is <em>powerful, interesting and messy.</em>”</p>
</blockquote>
<p><em>From typescriptlang <a href="https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html" target="_blank">TypeScript for Functional Programmers</a></em></p>
<h2 id="nutshell">Nutshell</h2>
<p>This is the fourth post in the series devoted to types in TypeScript. In this series, I explore type-centric approaches to writing code and push TS to its limits in doing so. I am writing these posts for like minded developers who are interested in types and either use or consider using TypeScript.</p>
<p>This post will be a little more advanced and will focus on programming with type variables.</p>
<p><a href="https://www.goodreads.com/book/show/112252.Types_and_Programming_Languages" target="_blank">Types and Programming Languages</a> is the book about types I recommend to everyone (… even if not very successfully). Reading TAPL will be a big eye opener for many developers. The good news is that types dramatically increase programming efficiency so learning them is a good investment.<br />
This section of the post will be a little more TAPL-ish with some more advanced CS. The topics I am about to present are IMO very useful and I will try my best to present them in a digestible way.</p>
<p>I will discuss type variable scoping, rank-2 types, and existential types. Some examples show a level of safety that I did not expect to be able to pull off! As it turns out, we can even prevent subtyping in TS.</p>
<p>Before we start I need to build up some tooling. I will start with a tiny bit of type level programming.</p>
<h2 id="safety-preventing-unknown">Safety preventing <code>unknown</code></h2>
<p>In previous posts, we have seen examples where TS decided to widen types to <code>unknown</code> rather than report a compilation error.<br />
Interestingly, TS allows enough type level programming so we can try to fix such issues ourselves.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a>type IsUnknown<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">=</span> unknown <span class="kw">extends</span> T<span class="op">?</span> <span class="kw">true</span><span class="op">:</span> <span class="kw">false</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a><span class="kw">function</span> verifyUnknown<span class="op">&lt;</span>T<span class="op">&gt;</span>(p<span class="op">:</span> IsUnknown<span class="op">&lt;</span>T<span class="op">&gt;,</span> t<span class="op">:</span> T)<span class="op">:</span> T {</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a>    <span class="cf">return</span> t</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a>}</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="fu">verifyUnknown</span>(<span class="kw">false</span><span class="op">,</span> <span class="st">&quot;test&quot;</span>)</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a><span class="kw">const</span> unk<span class="op">:</span> unknown <span class="op">=</span> {}</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a><span class="fu">verifyUnknown</span>(<span class="kw">true</span><span class="op">,</span> unk)</span></code></pre></div>
<div class="sourceCode" id="cb2"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="co">//Compilation Error</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="co">//Argument of type 'false' is not assignable to parameter of type 'true'.ts(2345)</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a><span class="fu">verifyUnknown</span>(<span class="kw">false</span>, unk)</span></code></pre></div>
<p>In my first post, I had an example of incorrect code <a href="2021-12-12-ts-types-part1.html#bumps-on-the-path" target="_blank"><code>body4</code></a> inferred as <code>unknown</code> instead of a <code>string</code>. Wrapping such code in <code>verifyUnknown(false, body4)</code> would have alerted me with a compilation error.<br />
You may point out that a much simpler solution is to just type annotate: <code>const body4: string</code>.<br />
I agree. However, having a more generic solution at our disposal is also useful. We will see shortly why.</p>
<p>Here is a short TAPL-ish explanation of what just happened. TS allows me to use type level ternaries. <code>IsUnknown&lt;T&gt;</code> is a type level function (TAPL’sh term for this is <em>Type Family</em>) that maps types <code>T</code> to literal boolean types <code>true</code> or <code>false</code>. These types have only a single (a <em>singleton</em>) value: <code>true: true</code> and <code>false: false</code>. If I write <code>verifyUnknown(false, someExpression)</code>, TS will figure out that it has to use <code>false</code> as the type. <code>false</code> matches the second part of the type level ternary and, thus, implies that the ternary predicate <code>unknown extends T</code> is not true. Hence <code>T</code> is not <code>unknown</code>.</p>
<p>I will use <code>verifyUnknown</code> to do some type level trickery. You may wonder if we can extend this approach to other types, not just to <code>unknown</code>. I will get there in this post as well.</p>
<h2 id="type-variable-scoping">Type variable scoping</h2>
<p>Type variable scoping has two aspects. Let’s start with the most obvious one. The type variable being visible inside of the implementation body. This is simple stuff, I just want to share an obvious gotcha that got me at some point:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="im">export</span> <span class="kw">const</span> bodyScopeExample1 <span class="op">=</span> <span class="op">&lt;</span>T<span class="op">&gt;</span>(value<span class="op">:</span> T <span class="op">|</span> <span class="kw">undefined</span> <span class="op">|</span> <span class="kw">null</span>)<span class="op">:</span> <span class="kw">void</span> <span class="kw">=&gt;</span> {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a>    <span class="cf">if</span>(value) {</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a>        <span class="kw">const</span> <span class="dt">t</span><span class="op">:</span> T <span class="op">=</span> value <span class="co">//you can access type variables in function body</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a>    }</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a>}</span></code></pre></div>
<p>This approach to defining type signatures (actually my preferred way to write function type signatures) puts <code>T</code> out of scope:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a>export <span class="dt">const</span> bodyScopeExample2: &lt;T&gt;(_: T | undefined | <span class="kw">null</span>) =&gt; <span class="dt">void</span> = value =&gt; {</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a>    <span class="kw">if</span>(value) {</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a>        <span class="dt">const</span> t: T = value  <span class="co">//Cannot find name 'T'.ts(2304)</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a>    }</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a>}</span></code></pre></div>
<p>For the type variables to be visible in the implementation they need to be on the <em>RHS</em> of <code>=</code>.</p>
<p>The other aspect of type variable scoping is much more interesting:</p>
<h2 id="higher-rank-types">Higher Rank types</h2>
<p>Consider these 2 function declarations:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a>declare <span class="kw">function</span> fn1<span class="op">&lt;</span>T<span class="op">&gt;</span> (f<span class="op">:</span>(t<span class="op">:</span>T)<span class="kw">=&gt;</span> <span class="kw">void</span>)<span class="op">:</span> <span class="kw">void</span> </span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">fn2</span>(f<span class="op">:</span> <span class="op">&lt;</span>T<span class="op">&gt;</span>(t<span class="op">:</span>T)<span class="kw">=&gt;</span> <span class="kw">void</span>)<span class="op">:</span> <span class="kw">void</span> </span></code></pre></div>
<p>In <code>fn2</code> the scope of <code>T</code> is much narrower. In TAPL-ish this would be called a rank-2 type.<br />
So what is the difference? Let’s try to use both:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="kw">const</span> useStr <span class="op">=</span> (s<span class="op">:</span>string)<span class="op">:</span> <span class="kw">void</span> <span class="kw">=&gt;</span> {}</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="fu">fn1</span>(useStr)</span></code></pre></div>
<div class="sourceCode" id="cb7"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="co">//Compilation Error:</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="co">//const useStr: (s: string) =&gt; void</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a><span class="co">//Argument of type '(s: string) =&gt; void' is not assignable to parameter of type '&lt;T&gt;(t: T) =&gt; void'.</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a><span class="co">//  Types of parameters 's' and 't' are incompatible.</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a><span class="co">//    Type 'T' is not assignable to type 'string'.ts(2345)</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a><span class="fu">fn2</span>(useStr)</span></code></pre></div>
<p>Basically <code>fn2</code> requires the argument to be fully generic and <code>useStr</code> is not.</p>
<p>I can play the same games with generic arguments that return <code>T</code></p>
<div class="sourceCode" id="cb8"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">fn4</span>(f<span class="op">:</span> <span class="op">&lt;</span>T<span class="op">&gt;</span>() <span class="kw">=&gt;</span> T)<span class="op">:</span> <span class="kw">void</span></span></code></pre></div>
<p>but will not do that here as these tend to be less practically useful.</p>
<p>Here is how I think about it:</p>
<p><em>Higher rank means generics are first class</em></p>
<h2 id="existential-types">Existential types</h2>
<p>In TAPL-ish this is called <em>existential quantification</em> and it has to do with the ownership of definitions. In OO you would say “code to interfaces, not implementation”, it is also related to the OO concepts of <em>inversion of control</em> and <em>dependency injection</em>. Here is how the story goes:</p>
<h3 id="replacing-factory-pattern">Replacing factory pattern</h3>
<div class="sourceCode" id="cb9"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="kw">interface</span> Foo {</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a>    <span class="dt">foo</span><span class="op">:</span> string</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>}</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a><span class="kw">class</span> MyFoo <span class="kw">implements</span> Foo{</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a>    <span class="dt">foo</span><span class="op">:</span> string</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a>    <span class="fu">constructor</span>() {</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a>        <span class="kw">this</span><span class="op">.</span><span class="at">foo</span> <span class="op">=</span> <span class="st">&quot;bar&quot;</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a>    }</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a>}</span></code></pre></div>
<p>We want to be able to hide which implementation of <code>Foo</code> we are passing to a callback.<br />
Our first approach tries to use a vanilla TS generic function with a callback argument.<br />
The input function parameter uses some <code>&lt;T extends Foo&gt;</code> of an unknown exact implementation type:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a>function factoryWithCallback&lt;T <span class="kw">extends</span> Foo&gt; (f:(_:T) =&gt; <span class="dt">void</span>): <span class="dt">void</span> {</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a>    <span class="co">//Argument of type 'MyFoo' is not assignable to parameter of type 'T'.</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a>    <span class="co">// 'MyFoo' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Foo'.ts(2345)</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>    <span class="fu">f</span>(<span class="kw">new</span> <span class="fu">MyFoo</span>()) <span class="co">//Compilation Error</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a>}</span></code></pre></div>
<p>It does not work and it should not work! We need to use a rank-2 definition:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="co">//Compiles!</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">existencialFactory</span>(f<span class="op">:</span> <span class="op">&lt;</span>T <span class="kw">extends</span> Foo<span class="op">&gt;</span>(_<span class="op">:</span>T) <span class="kw">=&gt;</span> <span class="kw">void</span>)<span class="op">:</span> <span class="kw">void</span> {</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a>    <span class="fu">f</span>(<span class="kw">new</span> <span class="fu">MyFoo</span>())</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a>}</span></code></pre></div>
<p>This simulates what is called an existential type. The function that accepts a callback owns the definition of the exact type that is passed to the callback. The callback itself needs to be generic and accept any possible implementation.<br />
Note the scoping of <code>T</code> inside the type defining the function parameter.</p>
<p>This <em>inverts the control</em> from the implementation of the callback to the caller.</p>
<p> <div class="side-note"><strong>Note on terminology:</strong> Repeating the above definitions</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a>declare <span class="kw">function</span> fn1<span class="op">&lt;</span>T<span class="op">&gt;</span> (f<span class="op">:</span>(t<span class="op">:</span>T)<span class="kw">=&gt;</span> <span class="kw">void</span>)<span class="op">:</span> <span class="kw">void</span> </span></code></pre></div>
<p>The <code>fn1</code> needs to be defined for all possible types <code>T</code>. The name for it is <em>universal quantification</em>. Some languages even use the <code>forall</code> keyword to describe it.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">fn2</span>(f<span class="op">:</span> <span class="op">&lt;</span>T<span class="op">&gt;</span>(t<span class="op">:</span>T)<span class="kw">=&gt;</span> <span class="kw">void</span>)<span class="op">:</span> <span class="kw">void</span> </span></code></pre></div>
<p><code>fn2</code> function parameter <code>f</code> needs to be defined for all possible types <code>T</code>. However, <code>fn2</code> can pick whatever type it wants for <code>T</code> and use <code>f</code> with it.<br />
In other words, there exists some type <code>T</code> that will be used but <code>f</code> has no way of knowing which. The name for it is <em>existential quantification</em>. Some languages even use the <code>exists</code> keyword to describe it.</p>
<p>The general concept of existentials is broader than what I am describing here and what TS supports. However, this by itself is plenty powerful.  </div></p>
<h3 id="preventing-information-escape">Preventing information escape</h3>
<p>I am drawing a blank trying to think about an OO analogy for this. It is somewhat related to friend classes in C++, package-private scope in Java … only not exactly.<br />
This example will accomplish more than the above ‘factory’ pattern and will not use any interfaces or classes:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="co">// Using higher rank to protect data</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="co">// Imaginary world without debuggers, JSON.stringify, etc</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a>type Api <span class="op">=</span> {<span class="dt">getGoodies</span><span class="op">:</span> string[]}</span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a><span class="co">//provides access to API, password needs to be protected</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a>declare <span class="kw">function</span> login<span class="op">&lt;</span>Password<span class="op">&gt;</span>(p<span class="op">:</span> Password)<span class="op">:</span> Api </span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true"></a></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true"></a><span class="co">//provide password to a computation, that computation should be able to use the password but shouldn't return it</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true"></a><span class="kw">const</span> secretive <span class="op">=</span> <span class="op">&lt;</span>R<span class="op">&gt;</span> (fn<span class="op">:</span> <span class="op">&lt;</span>Password<span class="op">&gt;</span> (p<span class="op">:</span> Password) <span class="kw">=&gt;</span> R)<span class="op">:</span> R  <span class="kw">=&gt;</span> {</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true"></a>   <span class="kw">const</span> <span class="dt">s</span> <span class="op">:</span> any <span class="op">=</span> <span class="st">&quot;topsecret&quot;</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true"></a>   <span class="cf">return</span> <span class="fu">fn</span> (s)</span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true"></a>}</span></code></pre></div>
<p>The example is somewhat contrived with the main goal of illustrating the point.<br />
This code exposes building blocks that work together. To get the access to the <code>Api</code> type, you have to use <code>login</code> and you have to use it inside the provided <code>secretive</code> function. Working with an API like this is like assembling a jigsaw puzzle. Types prevent from jamming a square peg into a round hole.<br />
Note, <code>Password</code> is a type variable and we have used the existential type trick.</p>
<p>This code uses the building blocks:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="kw">const</span> goodProgram <span class="op">=</span> <span class="op">&lt;</span>Password<span class="op">&gt;</span>(p<span class="op">:</span> Password)<span class="op">:</span> string[] <span class="kw">=&gt;</span> {</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a>    <span class="kw">const</span> api <span class="op">=</span> <span class="fu">login</span>(p)</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a>    <span class="cf">return</span> api<span class="op">.</span><span class="at">getGoodies</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a>}</span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a><span class="kw">const</span> stealPassword <span class="op">=</span> <span class="op">&lt;</span>Password<span class="op">&gt;</span>(p<span class="op">:</span> Password)<span class="op">:</span> Password <span class="kw">=&gt;</span> p</span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a><span class="fu">secretive</span>(goodProgram)</span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a><span class="fu">secretive</span>(stealPassword)</span></code></pre></div>
<p>Unfortunately, <code>secretive(stealPassword)</code> compiles. Somewhat typical of TS, instead of providing robust type safety, the compiler infers <code>unknown</code> and accepts my questionable code. Hovering over <code>secretive</code> shows me this:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="co">//const secretive: &lt;string[]&gt;(fn: &lt;Password&gt;(p: Password) =&gt; string[]) =&gt; string[]</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a><span class="fu">secretive</span>(goodProgram)</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a><span class="co">//const secretive: &lt;unknown&gt;(fn: &lt;Password&gt;(p: Password) =&gt; unknown) =&gt; unknown</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true"></a><span class="fu">secretive</span>(stealPassword)</span></code></pre></div>
<p>That is why I have created the <code>verifyUnknown</code> safety in the previous section:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a><span class="kw">const</span> valid <span class="op">=</span> <span class="fu">verifyUnknown</span>(<span class="kw">false</span><span class="op">,</span> <span class="fu">secretive</span>(goodProgram)) <span class="co">//valid: string[]</span></span></code></pre></div>
<div class="sourceCode" id="cb18"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="co">//Argument of type 'false' is not assignable to parameter of type 'true'.ts(2345)</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true"></a><span class="dt">const</span> invalid = <span class="fu">verifyUnknown</span>(<span class="kw">false</span>, <span class="fu">secretive</span>(stealPassword)) <span class="co">//does not compile!</span></span></code></pre></div>
<p>To make it a bit nicer we can package <code>verifyUnknown</code> and <code>secretive</code> into one function:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="kw">const</span> verySecretive <span class="op">=</span> <span class="op">&lt;</span>R<span class="op">&gt;</span> (_<span class="op">:</span> IsUnknown<span class="op">&lt;</span>R<span class="op">&gt;,</span> fn<span class="op">:</span> <span class="op">&lt;</span>Password<span class="op">&gt;</span> (p<span class="op">:</span> Password) <span class="kw">=&gt;</span> R)<span class="op">:</span> R  <span class="kw">=&gt;</span> {</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true"></a>    <span class="kw">const</span> <span class="dt">s</span> <span class="op">:</span> any <span class="op">=</span> <span class="st">&quot;topsecret&quot;</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true"></a>    <span class="cf">return</span> <span class="fu">fn</span> (s)</span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true"></a> }</span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true"></a></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true"></a><span class="kw">const</span> valid <span class="op">=</span> <span class="fu">verySecretive</span>(<span class="kw">false</span><span class="op">,</span> goodProgram) <span class="co">//valid: string[]</span></span></code></pre></div>
<div class="sourceCode" id="cb20"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true"></a><span class="dt">const</span> invalid = <span class="fu">verySecretive</span>(<span class="kw">false</span>, stealPassword) </span></code></pre></div>
<p>This creates some interesting safety. Obviously you could still do a lot of mischief if you wanted to. There is a need for some ‘gentlemen’s agreements’ to not use casting, <code>JSON.stringify</code>, to not use <code>true</code> in <code>verySecretive</code> etc. However, if you think about creating clear contract APIs, this approach could be very powerful.</p>
<p>Existentials are not exactly equivalent to OO. However, using existential types can often accomplish a lot of the same things and often in a cleaner way. Using existentials and disabling OO features like <code>unknown</code> feels a bit contrived, but IMO is still useful. It would be nice if TS provided a cleaner way to disable the use of <code>unknown</code>.<br />
I do not know how robust this type of coding is. I have not played enough with this approach in TS to give you a list of gotchas. In my very limited experience, this seems similar to the rest of TS, TS stops working if I start pushing harder.</p>
<p> <div class="side-note"><strong>Existentials and higher rank at large:</strong> These concepts have lead to some amazing programming.<br />
For example, existentials are related to dependent pairs (dependent sums) in depenently typed programming languages. Dependent typing provides some very strong types. One example could be lists with a type checked length. You want to be able to use such lists when processing runtime data that can have arbitrary size. That size ‘exists’ but cannot be known statically at the compile time. This is in essence an existential construction.</p>
<p>Another amazing example is an old (1993) code called <em>State Threads (ST)</em> (currently part of std base library in Haskell). It allows to use a local mutable state to define computations that have to be <em>referentially transparent</em> (I have discussed referential transparency in <a href="2021-12-24-ts-types-part2.html#referential-transparency" target="_blank">Part 2</a>). This is possible because the access to mutate the state cannot escape outside of these computations. ST API remains unchanged since it was created 30 year ago, you can’t improve on perfection!</p>
<p>I see higher rank types, mostly rank-2 being used a lot. Having ability to pass generic (polymorphic) functions around is very useful. In my non-TS projects, the problem of ‘separating interface from implementation’ is typically solved by defining an EDSL (Embedded Domain Specific Language) and an interpreter. Interpreters are polymorphic (generic in TS lingo). Rank-2 types have to be used to make them first class and pass them around.  </div></p>
<h2 id="safety-preventing-subtyping">Safety preventing subtyping</h2>
<p>Many TS users have observed the need for this. The term <em>exact type</em> is floating around, I believe <em>flow</em> introduced this name. I have seen solutions like this one being proposed:</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true"></a><span class="kw">function</span> exact<span class="op">&lt;</span>T<span class="op">&gt;</span>(item<span class="op">:</span>T)<span class="op">:</span> T {</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true"></a>    <span class="cf">return</span> item</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true"></a>}</span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true"></a></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true"></a>type Hello <span class="op">=</span> {<span class="dt">hello</span><span class="op">:</span> string}</span></code></pre></div>
<div class="sourceCode" id="cb22"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true"></a><span class="co">//Argument of type '{ hello: string; since: number; }' is not assignable to parameter of type 'Hello'.</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true"></a><span class="co">//  Object literal may only specify known properties, and 'since' does not exist in type 'Hello'.ts(2345)</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true"></a>exact&lt;Hello&gt;({hello: <span class="st">&quot;world&quot;</span>, since:<span class="dv">2002</span>})</span></code></pre></div>
<p>This safety is fragile (and a TS design inconsistency IMO) as the following example shows:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true"></a><span class="kw">const</span> helloSince <span class="op">=</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;world&quot;</span><span class="op">,</span> <span class="dt">since</span><span class="op">:</span><span class="dv">2002</span>}</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true"></a></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true"></a>exact<span class="op">&lt;</span>Hello<span class="op">&gt;</span>(helloSince) <span class="co">//complies</span></span></code></pre></div>
<p>To create something more robust, here is a code that combines the above <code>unknown</code> verification idea with existentials:</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true"></a>type Same<span class="op">&lt;</span>P<span class="op">,</span>T<span class="op">&gt;</span> <span class="op">=</span> P <span class="kw">extends</span> T<span class="op">?</span> (T <span class="kw">extends</span> P<span class="op">?</span> <span class="kw">true</span><span class="op">:</span> <span class="kw">false</span>)<span class="op">:</span> <span class="kw">false</span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true"></a></span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true"></a><span class="kw">const</span> verifySame <span class="op">=</span> <span class="op">&lt;</span>P<span class="op">&gt;</span> () <span class="kw">=&gt;</span> <span class="op">&lt;</span>T<span class="op">&gt;</span> (_<span class="op">:</span> Same<span class="op">&lt;</span>P<span class="op">,</span>T<span class="op">&gt;,</span> t<span class="op">:</span>T)<span class="op">:</span> T <span class="kw">=&gt;</span> t</span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true"></a></span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true"></a>verifySame<span class="op">&lt;</span>Hello<span class="op">&gt;</span>()(<span class="kw">true</span><span class="op">,</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;world&quot;</span>}) <span class="co">//'true' indicates that type matches</span></span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true"></a>verifySame<span class="op">&lt;</span>Hello<span class="op">&gt;</span>()(<span class="kw">false</span><span class="op">,</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;world&quot;</span><span class="op">,</span> <span class="dt">since</span> <span class="op">:</span> <span class="dv">2020</span>}) <span class="co">//'false' is needed to acknowledge types are different </span></span></code></pre></div>
<div class="sourceCode" id="cb25"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true"></a><span class="co">//Argument of type 'true' is not assignable to parameter of type 'false'.ts(2345)</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true"></a>verifySame&lt;Hello&gt;()(<span class="kw">true</span>, {hello: <span class="st">&quot;world&quot;</span>, since : <span class="dv">2020</span>})</span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true"></a>verifySame&lt;Hello&gt;()(<span class="kw">true</span>, helloSince)</span></code></pre></div>
<p>You may have noticed a case of typing euphoria here. I used rank-2 construction because it allows me to type annotate with only one type variable. This is nice but often not essential.</p>
<p>Here is an implementation of <code>safePush</code> that acts invariant, it does not use any existential tricks:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true"></a><span class="co">//Note to get 'safePush' I ended up with casting, this is a quick and dirty example and can be done slightly better</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true"></a><span class="co">// However, this cast could be an indication that we are changing how TS compiler works</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true"></a><span class="co">// Kinda makes sense, to overrule the compiler I may need to cast</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true"></a><span class="kw">const</span> safePush <span class="op">=</span> <span class="op">&lt;</span>P<span class="op">,</span> T<span class="op">&gt;</span> (_<span class="op">:</span> Same<span class="op">&lt;</span>P<span class="op">,</span>T<span class="op">&gt;,</span> ps<span class="op">:</span> P[]<span class="op">,</span> t<span class="op">:</span> T)<span class="op">:</span> number <span class="kw">=&gt;</span> ps<span class="op">.</span><span class="fu">push</span>(t <span class="im">as</span> any)</span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true"></a></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true"></a><span class="kw">const</span> intlist<span class="op">:</span> number[] <span class="op">=</span> [<span class="dv">1</span><span class="op">,</span><span class="dv">2</span><span class="op">,</span><span class="dv">3</span>]</span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true"></a><span class="kw">const</span> unklist<span class="op">:</span> unknown[] <span class="op">=</span> intlist  <span class="co">//exploits array covariance</span></span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true"></a>unklist<span class="op">.</span><span class="fu">push</span>(<span class="st">&quot;not a number&quot;</span>) <span class="co">//unsafe 'push' adds a 'string' to 'intlist'</span></span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true"></a></span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true"></a><span class="fu">safePush</span>(<span class="kw">true</span><span class="op">,</span> intlist<span class="op">,</span> <span class="dv">1</span>) <span class="co">//this is safe</span></span></code></pre></div>
<div class="sourceCode" id="cb27"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true"></a><span class="fu">safePush</span>(<span class="kw">true</span>, unklist, <span class="dv">1</span>)    <span class="co">//this is risky and will not compile </span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true"></a><span class="fu">safePush</span>(<span class="kw">true</span>, unklist, <span class="st">&quot;not a number&quot;</span>) <span class="co">//this is risky (here wrong) and will not compile </span></span></code></pre></div>
<p>Note, to be even safer I would need to prevent <code>unknown</code> as well:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true"></a><span class="kw">const</span> unkstr<span class="op">:</span> unknown <span class="op">=</span> <span class="st">&quot;not a number&quot;</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true"></a><span class="fu">safePush</span>(<span class="kw">true</span><span class="op">,</span> unklist<span class="op">,</span> unkstr)  <span class="co">//unfortunately compiles</span></span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true"></a></span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true"></a><span class="co">//An even safer version of 'Same'</span></span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true"></a>type SameAndKnown<span class="op">&lt;</span>P<span class="op">,</span>T<span class="op">&gt;</span> <span class="op">=</span> P <span class="kw">extends</span> T<span class="op">?</span> (T <span class="kw">extends</span> P<span class="op">?</span> (unknown <span class="kw">extends</span> T<span class="op">?</span> <span class="kw">false</span><span class="op">:</span> <span class="kw">true</span>)<span class="op">:</span> <span class="kw">false</span>)<span class="op">:</span> <span class="kw">false</span></span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true"></a></span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true"></a><span class="kw">const</span> verySafePush <span class="op">=</span> <span class="op">&lt;</span>P<span class="op">,</span> T<span class="op">&gt;</span> (_<span class="op">:</span> SameAndKnown<span class="op">&lt;</span>P<span class="op">,</span>T<span class="op">&gt;,</span> ps<span class="op">:</span> P[]<span class="op">,</span> t<span class="op">:</span> T)<span class="op">:</span> number <span class="kw">=&gt;</span> ps<span class="op">.</span><span class="fu">push</span>(t <span class="im">as</span> any)</span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true"></a></span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true"></a><span class="fu">verySafePush</span>(<span class="kw">true</span><span class="op">,</span> intlist<span class="op">,</span> <span class="dv">1</span>)  <span class="co">//this is safe</span></span></code></pre></div>
<div class="sourceCode" id="cb29"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true"></a><span class="fu">verySafePush</span>(<span class="kw">true</span>, unklist, unkstr) <span class="co">//this is risky and will not compile!</span></span></code></pre></div>
<p>We have discussed problems with the TS approach to variance in the <a href="2022-01-03-ts-types-part3.html#variance-problems" target="_blank">previous installment</a>. We have a DIY approach to fight back!</p>
<p>Side Note: The linked github repo has an existentially typed version of <code>safePush</code> (<code>safePush2</code>) that has just one top level type variable. That version is more cumbersome to use. TS ends up not working well with it.</p>
<p>Another fun exercise:</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true"></a><span class="kw">const</span> safeEq <span class="op">=</span> <span class="op">&lt;</span>P<span class="op">,</span> T<span class="op">&gt;</span> (_<span class="op">:</span> Same<span class="op">&lt;</span>P<span class="op">,</span>T<span class="op">&gt;,</span> a<span class="op">:</span> P<span class="op">,</span> b<span class="op">:</span> T)<span class="op">:</span> boolean <span class="kw">=&gt;</span> a <span class="op">===</span> (b <span class="im">as</span> unknown)</span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true"></a></span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true"></a><span class="fu">safeEq</span>(<span class="kw">true</span><span class="op">,</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;word&quot;</span>}<span class="op">,</span> {<span class="dt">hello</span><span class="op">:</span><span class="st">&quot;dolly&quot;</span>})</span></code></pre></div>
<div class="sourceCode" id="cb31"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true"></a><span class="fu">safeEq</span>(<span class="kw">true</span>, {hello: <span class="st">&quot;word&quot;</span>}, {hello:<span class="st">&quot;word&quot;</span>, since:<span class="dv">2022</span>}))</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true"></a><span class="fu">safeEq</span>(<span class="kw">true</span>, <span class="dv">1</span>, <span class="st">&quot;str&quot;</span>)</span></code></pre></div>
<p>We have discussed problems with TS approach to <code>===</code> narrowing in the <a href="2022-01-03-ts-types-part3.html#complexity-of-ts-types" target="_blank">previous installment</a>. Again, we have a DIY approach to fight back.</p>
<p>This section is related to a number of feature requests: <a href="https://github.com/microsoft/TypeScript/issues/12936" target="_blank">TypeScript issue 12936</a> and <a href="https://github.com/microsoft/TypeScript/issues/7481" target="_blank">TypeScript issue 7481</a>. Hopefully a future version of TS will provide a simpler way to achieve invariance and disable subtyping.</p>
<h2 id="phantom-types">Phantom types</h2>
<p>TypeScript is somewhat unique in supporting <em>Structural Types</em>. Types like <code>type Person = {firstNm: string, lastNm: string}</code> are structural. That means the name <code>Person</code> is only an alias, what defines the type is the RHS of the definition, not the LHS. Contrast this with an OO class definition in a language like Java. Two structurally identical classes are still considered different types (this is called <em>nominal typing</em>).</p>
<p>It is sometimes convenient to be able to define different types that share the same structure. <em>Phantom types</em> are a way to do that. We say <em>phantom</em> because these types have no impact on runtime values.</p>
<p>Somewhere around 2006, haskell wiki published a write-up about <a href="https://wiki.haskell.org/Phantom_type" target="_blank">phantom types</a>. The write-up was expanded in 2010 to include a form validation example. Since then all blogs (in any programming language) about phantoms show a validation example. I decided to be as unoriginal as everyone else. This will allow me to better focus on how it is done in TS.</p>
<p>My first attempt at phantom types in TS will fail. But this code should make the idea behind phantoms clear:</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true"></a><span class="co">//Marker type</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true"></a>type Validated <span class="op">=</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;validated&quot;</span>}</span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true"></a></span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true"></a><span class="co">//For simplicity this is just a string</span></span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true"></a>type ValidationError <span class="op">=</span> string</span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true"></a></span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true"></a><span class="co">//Extra phantom type variable 'T' </span></span>
<span id="cb32-8"><a href="#cb32-8" aria-hidden="true"></a>type Person<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">=</span> {<span class="dt">firstNm</span><span class="op">:</span> string<span class="op">,</span> <span class="dt">lastNm</span><span class="op">:</span> string}</span>
<span id="cb32-9"><a href="#cb32-9" aria-hidden="true"></a></span>
<span id="cb32-10"><a href="#cb32-10" aria-hidden="true"></a></span>
<span id="cb32-11"><a href="#cb32-11" aria-hidden="true"></a><span class="co">//Validate person in some way returning 'Validated' phantom marker</span></span>
<span id="cb32-12"><a href="#cb32-12" aria-hidden="true"></a>declare <span class="kw">function</span> validate<span class="op">&lt;</span>T<span class="op">&gt;</span>(p<span class="op">:</span> Person<span class="op">&lt;</span>T<span class="op">&gt;</span>)<span class="op">:</span>  ValidationError <span class="op">|</span> Person<span class="op">&lt;</span>Validated<span class="op">&gt;</span> </span>
<span id="cb32-13"><a href="#cb32-13" aria-hidden="true"></a></span>
<span id="cb32-14"><a href="#cb32-14" aria-hidden="true"></a><span class="co">//Function to be used only if phantom 'T' is the 'Validated' type </span></span>
<span id="cb32-15"><a href="#cb32-15" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">doSomethingValidated</span>(p<span class="op">:</span> Person<span class="op">&lt;</span>Validated<span class="op">&gt;</span>)<span class="op">:</span> <span class="kw">void</span></span></code></pre></div>
<p>Again, these types are trying to create a jigsaw puzzle. One I can assemble in a specific way only.<br />
If the puzzle machinery works, I will have to call <code>validate</code> first to be able to use <code>doSomethingValidated</code>.</p>
<p>Only, this machinery does not work. The following code compiles:</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true"></a><span class="kw">function</span> validatedOrNot<span class="op">&lt;</span>T<span class="op">&gt;</span>(p<span class="op">:</span> Person<span class="op">&lt;</span>T<span class="op">&gt;</span>)<span class="op">:</span> <span class="kw">void</span> {</span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true"></a>    <span class="fu">doSomethingValidated</span>(p)</span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true"></a>}</span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true"></a></span>
<span id="cb33-5"><a href="#cb33-5" aria-hidden="true"></a>type ClearlyNotValidated <span class="op">=</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;notvalidated&quot;</span>}</span>
<span id="cb33-6"><a href="#cb33-6" aria-hidden="true"></a></span>
<span id="cb33-7"><a href="#cb33-7" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">notValidated</span> (p<span class="op">:</span> Person<span class="op">&lt;</span>ClearlyNotValidated<span class="op">&gt;</span>)<span class="op">:</span> <span class="kw">void</span> {</span>
<span id="cb33-8"><a href="#cb33-8" aria-hidden="true"></a>    <span class="fu">doSomethingValidated</span>(p)</span>
<span id="cb33-9"><a href="#cb33-9" aria-hidden="true"></a>}</span></code></pre></div>
<p>The fix is to provide a value level information about <code>T</code> in an optional property.<br />
This type definition replaces the one above:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true"></a><span class="co">//Modified definition adds value level representation `phantom?: T` </span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true"></a>type Person<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">=</span> {<span class="dt">firstNm</span><span class="op">:</span> string<span class="op">,</span> <span class="dt">lastNm</span><span class="op">:</span> string<span class="op">,</span> phantom<span class="op">?:</span> T}  </span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true"></a></span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true"></a><span class="co">//provide a way to create person that ignores the additional 'phantom' property:</span></span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true"></a><span class="kw">const</span> createPerson <span class="op">:</span> <span class="op">&lt;</span>T<span class="op">&gt;</span>(fst<span class="op">:</span> string<span class="op">,</span> lst<span class="op">:</span> string) <span class="kw">=&gt;</span> Person<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">=</span> (fst<span class="op">,</span> lst) <span class="kw">=&gt;</span> {</span>
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true"></a>    <span class="cf">return</span> {<span class="dt">firstNm</span><span class="op">:</span> fst<span class="op">,</span> <span class="dt">lastNm</span><span class="op">:</span> lst}</span>
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true"></a>} </span></code></pre></div>
<p>Now this compiles:</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">validated</span>(p<span class="op">:</span> Person<span class="op">&lt;</span>Validated<span class="op">&gt;</span>)<span class="op">:</span> <span class="kw">void</span> {</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true"></a>    <span class="fu">doSomethingValidated</span>(p)</span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true"></a>}</span></code></pre></div>
<p>But these no longer do:</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true"></a><span class="co">// Compilation Error</span></span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true"></a><span class="co">// Argument of type 'Person&lt;T&gt;' is not assignable to parameter of type 'Person&lt;Validated&gt;'.</span></span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true"></a><span class="co">//   Type 'T' is not assignable to type 'Validated'.ts(2345)</span></span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true"></a>function validatedOrNot&lt;T&gt;(p: Person&lt;T&gt;): <span class="dt">void</span>{</span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true"></a>    <span class="fu">doSomethingValidated</span>(p)</span>
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true"></a>}</span>
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true"></a></span>
<span id="cb36-8"><a href="#cb36-8" aria-hidden="true"></a><span class="co">// Compilation Error</span></span>
<span id="cb36-9"><a href="#cb36-9" aria-hidden="true"></a><span class="co">// Argument of type 'Person&lt;ClearlyNotValidated&gt;' is not assignable to parameter of type 'Person&lt;Validated&gt;'.</span></span>
<span id="cb36-10"><a href="#cb36-10" aria-hidden="true"></a><span class="co">//   Type 'ClearlyNotValidated' is not assignable to type 'Validated'.</span></span>
<span id="cb36-11"><a href="#cb36-11" aria-hidden="true"></a><span class="co">//     Types of property 'type' are incompatible.</span></span>
<span id="cb36-12"><a href="#cb36-12" aria-hidden="true"></a><span class="co">//       Type '&quot;notvalidated&quot;' is not assignable to type '&quot;validated&quot;'.ts(2345)</span></span>
<span id="cb36-13"><a href="#cb36-13" aria-hidden="true"></a>function <span class="fu">notValidated</span> (p: Person&lt;ClearlyNotValidated&gt;): <span class="dt">void</span> {</span>
<span id="cb36-14"><a href="#cb36-14" aria-hidden="true"></a>    <span class="fu">doSomethingValidated</span>(p)</span>
<span id="cb36-15"><a href="#cb36-15" aria-hidden="true"></a>}</span></code></pre></div>
<p>I believe phantom types are used by some FP libraries in TS, e.g. <em>fp-ts</em>, these libraries use somewhat different techniques to get phantoms. There may be advantages to doing phantom types differently than what I have presented. The above approach is the simplest I can think of.</p>
<p> <div class="side-note"><strong>Phantom types at large:</strong> Phantom types can be used to do a lot of crazy type level stuff. The most wild use I have seen is <a href="https://iohk.io/en/research/library/papers/ghosts-of-departed-proofsfunctional-pearls/" target="_blank">Ghosts of Departed Proofs</a> (this uses Haskell).<br />
Here is a simplified and easy to understand example in TS. Think about a non-mutable list, your function accepts a list and does something with it, your code needs the list to be sorted to work. You can encapsulate this and conservatively sort it just in case (this approach seems not performance optimal), you can document your function by saying that it is the caller responsibility to sort (do developers read documentation?), … or you can introduce a phantom type:</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true"></a><span class="co">//Sort status as a phantom type,  'List' has type level information about its sort status.</span></span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true"></a>type List<span class="op">&lt;</span>T<span class="op">,</span> SortStatus<span class="op">&gt;</span> <span class="op">=</span> <span class="op">...</span></span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true"></a></span>
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true"></a><span class="kw">interface</span> Comparator<span class="op">&lt;</span>T<span class="op">&gt;</span> {</span>
<span id="cb37-5"><a href="#cb37-5" aria-hidden="true"></a>    <span class="fu">compare</span> (<span class="dt">o1</span><span class="op">:</span> T<span class="op">,</span> <span class="dt">o2</span><span class="op">:</span> T)<span class="op">:</span> number</span>
<span id="cb37-6"><a href="#cb37-6" aria-hidden="true"></a>}</span>
<span id="cb37-7"><a href="#cb37-7" aria-hidden="true"></a></span>
<span id="cb37-8"><a href="#cb37-8" aria-hidden="true"></a>declare <span class="kw">function</span> sortAscending <span class="op">&lt;</span>T <span class="kw">extends</span> Comparator<span class="op">&lt;</span>T<span class="op">&gt;,</span> AnyStatus<span class="op">&gt;</span> (list<span class="op">:</span> List<span class="op">&lt;</span>T<span class="op">,</span> AnyStatus<span class="op">&gt;</span>)<span class="op">:</span>  List<span class="op">&lt;</span>T<span class="op">,</span> <span class="st">&quot;ascending&quot;</span><span class="op">&gt;</span></span>
<span id="cb37-9"><a href="#cb37-9" aria-hidden="true"></a></span>
<span id="cb37-10"><a href="#cb37-10" aria-hidden="true"></a>declare <span class="kw">function</span> doSomethingWithSortedList <span class="op">&lt;</span>T <span class="kw">extends</span> Comparator<span class="op">&lt;</span>T<span class="op">&gt;&gt;</span> (list<span class="op">:</span> List<span class="op">&lt;</span>T<span class="op">,</span> <span class="st">&quot;ascending&quot;</span><span class="op">&gt;</span>)<span class="op">:</span> <span class="kw">void</span></span></code></pre></div>
<p>Again, notice the types form pieces of a puzzle and can be fitted only in a specific way.<br />
You can think about a <code>sort</code> as something that not only does what it says, but also provides a <em>token</em> to use later to prove that the sort was done. This <em>token</em> is a phantom type. You can think about creating a library that helps orchestrate a similar approach to programming and this is what the linked article talks about.<br />
Many FP programming languages support GADTs, these are very powerful types and limit the popularity of (subsume) phantom typing.<br />
 </div></p>
<p>Phantom types could be a very powerful API building tool.<br />
I am sure you can think about many other interesting use cases, … like state machines<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</p>
<h2 id="next-chapter">Next Chapter</h2>
<p>I want to talk about recursive types and type level programming. It will be more of a review of TS capabilities in these areas.</p>
<p>I need to take a break from writing posts. The next installment will take me longer, maybe a month or a little more, to finish.<br />
Thank you for reading. Happy New Year!</p>
<p>Here is the link: <a href="2022-02-13-ts-types-part5.html" target="_blank">Part 5</a>.</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>As an example, office.js is very stateful. It has <em>uninitialized</em> state known to cause problems, there is the application state (e.g. user is writing new email), and much more. My experience with office.js is that the code I write is very sensitive to where is placed and can be very brittle. API like this could be made both safe and self-documenting by using phantom types.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

    </section>
</article>
]]></summary>
</entry>
<entry>
    <title>Blog styling update</title>
    <link href="https://rpeszek.github.io//posts/2022-01-05-Styling.html" />
    <id>https://rpeszek.github.io//posts/2022-01-05-Styling.html</id>
    <published>2022-01-05T00:00:00Z</published>
    <updated>2022-01-05T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on January  5, 2022
        
        
        
        
        <div class="info">Tags: <a title="All pages tagged 'announcement'." href="../tags/announcement.html">announcement</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <p><em><a href="https://github.com/rpeszek/rpeszek.github.io/discussions/1" target="_blank">git discussions</a></em></p>
<p>This blog has used a vanilla Hakyll styling with slightly modified standard pandoc CSS for code blocks.<br />
Some of the readers have experienced very weird font size irregularities, making it hard to read my blog.</p>
<p>I did a full CSS reset using <a href="https://meyerweb.com/eric/tools/css/reset/reset.css" target="_blank">meyerweb reset.css</a> and have restyled all posts. <em>Sans-Serif</em> fonts are now a hard-coded default (before the <em>font-family</em> was not specified leaving it to the browser defaults to do whatever mischief they fancied).</p>
<p>Please let me know in git discussions if you still experience styling issues.</p>
<p>Thank you to everyone who alerted me about the styling problems.</p>
    </section>
</article>
]]></summary>
</entry>
<entry>
    <title>Type Enthusiast's Notes about TypeScript. Part 3. TS Complexity</title>
    <link href="https://rpeszek.github.io//posts/2022-01-03-ts-types-part3.html" />
    <id>https://rpeszek.github.io//posts/2022-01-03-ts-types-part3.html</id>
    <published>2022-01-03T00:00:00Z</published>
    <updated>2022-01-03T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on January  3, 2022
        
            by Robert Peszek
        
        
        
        <div class="changelog"> <div>Revision History: <ul> <li> (2022.01.03 - Present) Changes are documented in <a href="#summary-of-final-edits">Summary of final edits</a>. </li> <li> (2022.05.29) Draft warning removed </li> <li> (2022.08.30) added <a href="../tags/patterns-of-erroneous-code.html">patterns-of-erroneous-code</a> tag </li> </ul> </div></div>
        
        
        <div class="info">Tags: <a title="All pages tagged 'TypeScript-Notes'." href="../tags/TypeScript-Notes.html">TypeScript-Notes</a>, <a title="All pages tagged 'patterns-of-erroneous-code'." href="../tags/patterns-of-erroneous-code.html">patterns-of-erroneous-code</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <div class="toc"><div class="header">Table of Contents</div>
<ul>
<li><a href="#nutshell">Nutshell</a></li>
<li><a href="#interesting-safety">Interesting safety</a>
<ul>
<li><a href="#apple-orange-type-safety"><code>apple !== orange</code> type safety</a></li>
<li><a href="#switch-exhaustive-check"><code>switch</code> exhaustive check</a></li>
<li><a href="#null-undefined-safety"><code>null</code> / <code>undefined</code> safety</a></li>
</ul></li>
<li><a href="#complexity-of-ts-types">Complexity of TS types</a>
<ul>
<li><a href="#semantics-rejected-overlap"><code>===</code> semantics, rejected overlap</a></li>
<li><a href="#semantics-whats-an-overlap"><code>===</code> semantics, what’s an overlap?</a></li>
<li><a href="#hidden-blooper-side-note">Hidden blooper (side note)</a></li>
<li><a href="#diy-equality">DIY equality</a></li>
<li><a href="#subtyping">Subtyping</a></li>
<li><a href="#comparative-complexity-rant">Comparative complexity rant</a></li>
<li><a href="#variance-problems">Variance problems</a></li>
<li><a href="#summary">Summary</a></li>
</ul></li>
<li><a href="#next-chapter">Next Chapter</a></li>
<li><a href="#summary-of-final-edits">Summary of final edits</a></li>
</ul>
</div>
<p><em>Please Leave Feedback in: <a href="https://github.com/rpeszek/rpeszek.github.io/discussions/1" target="_blank">git discussions</a></em></p>
<p>Previous post: <a href="2021-12-24-ts-types-part2.html" target="_blank">Part 2. Typing Honestly</a>.</p>
<p><strong>Disclaimers:</strong> (imagine this is a very small font, read it very fast in a half whisper)<br />
<em>I assume strict compiler flags are on, something you get by default with scaffolding, e.g. using <code>create-react-app my-project --template typescript</code> is close enough.<br />
The code examples have been tested with TypeScript v4.5.2.<br />
This post is a pandoc output of a markdown document and code examples are not interactive.<br />
Most of the code examples are published in <a href="https://github.com/rpeszek/ts-experiments/tree/master/ts-notes" target="_blank">ts-notes</a> folder in this github repo: <a href="https://github.com/rpeszek/ts-experiments" target="_blank">ts-experiments</a>.</em></p>
<p><strong>Motivating Quote for the series:</strong></p>
<blockquote>
<p>“TypeScript began its life as an attempt to bring traditional object-oriented types to JavaScript so that the programmers at Microsoft could bring traditional object-oriented programs to the web. As it has developed, TypeScript’s type system has evolved to model code written by native JavaScripters. The resulting system is <em>powerful, interesting and messy.</em>”</p>
</blockquote>
<p><em>From typescriptlang <a href="https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html" target="_blank">TypeScript for Functional Programmers</a></em></p>
<h2 id="nutshell">Nutshell</h2>
<p>Happy New Year! Let’s hope 2022 it will be way better than 2021. It has to be.</p>
<p>This is the third post in the series devoted to types in TypeScript. In this series, I explore type-centric approaches to writing code and push TS to its limits in doing so. I am writing these posts for like minded developers who are interested in types and either use or consider using TypeScript.</p>
<p>In this post we will see TS struggle. We will see compilation inconsistencies and surprising type checker behavior.<br />
My main goal is to point out the complexity of what TS is trying to accomplish and share my understanding of it.<br />
On a positive note, I will introduce additional tools for asking TS type questions.<br />
Also, I promise, the next installment will be about good things in TS. It will be about programming with type variables.</p>
<p>Before we discuss the messy bits, let’s briefly talk about some cool type safety features.</p>
<h2 id="interesting-safety">Interesting safety</h2>
<p>TypeScript implements special <a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html" target="_blank">narrowing</a> semantics when processing parts of JS code. These semantic rules provide very surprising and useful type safety features. TS can effectively narrow types used in a number of JS operators such as <code>typeof</code>, <code>===</code>, <code>==</code> and apply this information to <code>if-else</code>, <code>switch</code> statements. This post has already shown a few examples where this, almost magically, prevents placing code in a wrong branch of conditional if-else blocks.</p>
<p>Here are some of my favorites with IMO on their use.</p>
<h3 id="apple-orange-type-safety"><code>apple !== orange</code> type safety</h3>
<p>This JavaScript code (I keep reusing <code>type Person = {firstNm: string, lastNm: string}</code> from the first post):</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co">//Bad code</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">blah</span>(lhs<span class="op">:</span> string<span class="op">,</span> rhs<span class="op">:</span> Person) {</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a>  <span class="cf">if</span> (lhs <span class="op">===</span> rhs) {</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a>    <span class="co">//Do something</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a>  } <span class="cf">else</span> {</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a>    <span class="co">//Do something else</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a>  }</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a>}</span></code></pre></div>
<p>is a programming bug and will not type-check in TypeScript. You can just replace it with:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="co">//Actual equivalent</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">blah</span>(lhs<span class="op">:</span> string<span class="op">,</span> rhs<span class="op">:</span> Person) {</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a>  <span class="co">//Do something else</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>}</span></code></pre></div>
<p>TypeScript prevents from using <code>===</code> if it can guess<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>, by looking at the types, that <code>===</code> will always be <code>false</code>. This is true in general, not just inside <code>if-else</code>, but the <code>if-else</code> use is the killer app IMO.<br />
One cool example of <code>===</code> type safety combines type narrowing with literal types: <code>1 === 2</code> will not compile!</p>
<p>This is a big deal. <code>===</code> is often used to compare things like <code>string</code> or <code>number</code> <em>id</em>-s or <em>hashes</em> and it is not that uncommon to accidentally try to compare something like an <em>id</em> with something completely different.<br />
I have seen analogous issues in many programming languages including even <em>Scala</em>.</p>
<h3 id="switch-exhaustive-check"><code>switch</code> exhaustive check</h3>
<p><code>if-else</code> does not provide any mechanism for the type checker to verify that the program checked all possible conditions.<br />
Interestingly, we can use the <code>switch</code> statement in TS to solve this problem:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="co">//This compiles!</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="kw">const</span> contrived_better <span class="op">=</span> (n<span class="op">:</span> <span class="dv">1</span> <span class="op">|</span> <span class="dv">2</span>)<span class="op">:</span> number <span class="kw">=&gt;</span> {</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a>    <span class="cf">switch</span>(n) {</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a>       <span class="cf">case</span> <span class="dv">1</span><span class="op">:</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a>        <span class="cf">return</span> <span class="dv">1</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a>       <span class="cf">case</span> <span class="dv">2</span><span class="op">:</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a>        <span class="cf">return</span> <span class="dv">2</span> </span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a>    } </span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a>}</span></code></pre></div>
<div class="sourceCode" id="cb4"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">//Compilation error</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="co">//Function lacks ending return statement and return type does not include 'undefined'.ts(</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a>export <span class="dt">const</span> contrived_better_ = (n: <span class="dv">1</span> | <span class="dv">2</span> | <span class="dv">3</span>): number =&gt; {</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a>    <span class="kw">switch</span>(n) {</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a>       <span class="kw">case</span> <span class="dv">1</span>:</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a>        <span class="kw">return</span> n</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a>       <span class="kw">case</span> <span class="dv">2</span>:</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a>        <span class="kw">return</span> n </span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true"></a>    } </span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true"></a>}</span></code></pre></div>
<p>That is another nice example of TS enhancing JS with a nice type safety feature.</p>
<p>IMO an even better solution is provided by the <em>ts-pattern</em> library. See this blog post: <a href="https://dev.to/gvergnaud/bringing-pattern-matching-to-typescript-introducing-ts-pattern-v3-0-o1k" target="_blank">Introducing ts-pattern v3.0</a></p>
<h3 id="null-undefined-safety"><code>null</code> / <code>undefined</code> safety</h3>
<p>We have seen <code>null</code> safety already. There is a semantic difference between <code>null</code> and <code>undefined</code> but most code does not care. My personal preference is to unify these two.</p>
<p>In my very first example in the series, <a href="2021-12-12-ts-types-part1.html#typescript-is-great" target="_blank"><code>getName(p: NullablePerson)</code></a>, was not <code>undefined</code> safe, only <code>null</code> safe. Using it with <code>undefined</code> (e.g. on expressions typed as <code>any</code>) will cause an error.</p>
<p>My coding preference would be to rewrite my first example like this:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="co">//Reusable utility type</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="im">export</span> type Undefined <span class="op">=</span> <span class="kw">null</span> <span class="op">|</span> <span class="kw">undefined</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a><span class="im">export</span> <span class="kw">const</span> isUndefined <span class="op">=</span> (d<span class="op">:</span> unknown)<span class="op">:</span> d is Undefined <span class="kw">=&gt;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a>   (d <span class="op">===</span> <span class="kw">null</span>) <span class="op">||</span> (d <span class="op">===</span> <span class="kw">undefined</span>) <span class="co">//I prefer not to use '=='</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a><span class="kw">const</span> getName2 <span class="op">=</span> (p<span class="op">:</span>Person <span class="op">|</span> Undefined)<span class="op">:</span> string <span class="kw">=&gt;</span> {</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a>    <span class="co">//const tst1 = p.firstNm //will not compile</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a>    <span class="cf">if</span>(<span class="fu">isUndefined</span>(p)){</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a>        <span class="co">//const tst2 = p.firstNm //will not compile</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a>        <span class="cf">return</span> <span class="st">&quot;John Smith&quot;</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true"></a>    } <span class="cf">else</span> {</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true"></a>        <span class="cf">return</span> p<span class="op">.</span><span class="at">firstNm</span> <span class="op">+</span> <span class="st">&quot; &quot;</span> <span class="op">+</span> p<span class="op">.</span><span class="at">lastNm</span> <span class="co">//compiles</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true"></a>    }</span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true"></a>}</span></code></pre></div>
<p>This is just my personal preference, I also use this approach when typing optional <code>?</code> object properties. E.g.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a>type Person2 <span class="op">=</span> {<span class="dt">firstNm</span><span class="op">:</span> string<span class="op">;</span> middleNm<span class="op">?:</span> string <span class="op">|</span> Undefined<span class="op">;</span> <span class="dt">lastNm</span><span class="op">:</span> string}</span></code></pre></div>
<p>The extra safety features are what surprised and excited me about TS. They reminded me of a functional programming language.</p>
<h2 id="complexity-of-ts-types">Complexity of TS types</h2>
<p>Throughout the series, we encountered a few examples where the TS type checker did not work as expected, we will encounter more of TS quirkiness in this section. This note suggests a reason for this: type complexity.</p>
<p>My original plan was to write about TS needing to implement a separate ad-hoc semantics for various JS operators. I was not able to present anything very insightful and I have abandoned that idea, e.g. these <a href="2021-12-12-ts-types-part1.html#type-holes" target="_blank">type hole</a> expressions do not even compile:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="co">//Compiliation errors: Object is of type 'unknown'</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="fu">_</span>() + <span class="fu">_</span>()</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a><span class="fu">_</span>() * <span class="fu">_</span>()</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a><span class="fu">_</span>() / <span class="fu">_</span>()</span></code></pre></div>
<p>Taking the quote from the top of this post to heart, I concluded that TS is about providing support for OO and other idiomatic uses of JS. I decided to narrow the focus of this note to subtyping and the <code>===</code> operator semantics.</p>
<h3 id="semantics-rejected-overlap"><code>===</code> semantics, rejected overlap</h3>
<p>I have picked <code>===</code> because we discussed it already in my previous note about the <a href="2021-12-24-ts-types-part2.html#note-about-the-unknown-type" target="_blank"><code>unknown</code> type</a>. Selecting <code>==</code> would produce a very similar presentation.</p>
<p>Here is an example of safety around the <code>===</code> operator:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="co">//This condition will always return 'false' since the types '&quot;world!&quot;' and '&quot;Dolly!&quot;' have no overlap.ts(2367)</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="st">&quot;world!&quot;</span> === <span class="st">&quot;Dolly!&quot;</span> <span class="co">//does not compile</span></span></code></pre></div>
<p>Let’s try to figure out the semantic rules around <code>===</code>. What does “not having an overlap” mean?<br />
I have not seen a formal (or even a somewhat precise) definition of the semantic rules for the <code>===</code>.<br />
(Please comment in git discussions if you know about any place that defines these.)<br />
The informal definition (from typescriptlang documentation) points to a “common type that both <code>x</code> and <code>y</code> could take on” but this statement clearly has some loose ends.</p>
<p>The first part of the error message “This condition will always return ‘false’” suggests a way to start:</p>
<p><strong>(EQ-SAFETY attempt 1):</strong> <em>TypeScript prevents using <code>===</code> if it can prove, by looking at the types, that the result of <code>===</code> would always be <code>false</code>.</em></p>
<p>This is a very high level and does not explain how TS does it. But is this even true?</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a>function <span class="fu">testEqSemantics</span>(a: {bye: string}, b: {hello: string): <span class="dt">boolean</span> {</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a>   <span class="co">//This condition will always return 'false' since the types '{ bye: string; }' and '{ hello: string; }' have no overlap.</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>   <span class="kw">return</span> a === b <span class="co">//does not compile</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a>}</span></code></pre></div>
<p>Let me temporarily comment the not compiling code:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">testEqSemantics</span>(a<span class="op">:</span> {<span class="dt">bye</span><span class="op">:</span> string}<span class="op">,</span> b<span class="op">:</span> {<span class="dt">hello</span><span class="op">:</span> string})<span class="op">:</span> boolean {</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a>   <span class="co">//This condition will always return 'false' since the types '{ bye: string; }' and '{ hello: string; }' have no overlap.</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a>   <span class="co">//return a === b</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>   <span class="cf">return</span> <span class="kw">true</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a>}</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a><span class="kw">const</span> helloBye <span class="op">=</span> {<span class="dt">bye</span><span class="op">:</span><span class="st">&quot;world!&quot;</span><span class="op">,</span> <span class="dt">hello</span><span class="op">:</span><span class="st">&quot;world!&quot;</span>}</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a><span class="fu">testEqSemantics</span>(helloBye<span class="op">,</span> helloBye)  <span class="co">//compiles, here is the overlap!</span></span></code></pre></div>
<p>TS has effectively prevented me from using <code>===</code> even though there are legitimate cases where the <code>===</code> would have returned <code>true</code>! This seems like a major blooper.</p>
<p><strong><em>We have falsified the error message from TS.</em></strong></p>
<p>OO is complex and type design issues are not uncommon among OO languages, this could be one of them.<br />
On the other hand, preventing <code>{bye: "world!"} === {hello: "world!"}</code> from compiling seems useful from a pragmatic point of view. It is possible that this behavior is intentional.</p>
<p>I see 2 possible conclusions</p>
<ol type="1">
<li>This is a bug caused by a complexity of TS’s semantic rules</li>
<li>This is a feature indicating that the rules are indeed complex</li>
</ol>
<p>This appears to be one of the “Working as Intended” or at least known issues (see <a href="#fn4">footnote 4</a>).</p>
<h3 id="semantics-whats-an-overlap"><code>===</code> semantics, what’s an overlap?</h3>
<p>Let’s focus on this part of the error message: “types … and … have no overlap”.</p>
<p><strong>(EQ-SAFETY attempt 2):</strong> <em><code>x === y</code> compiles if <code>x: X</code> and <code>y: Y</code> and the compiler successfully computes some special non-<code>never</code> <code>Overlap</code> type that widens to both <code>X</code> and <code>Y</code></em></p>
<p><code>X</code> is the computed type for <code>x</code>, <code>Y</code> is the computed type for <code>y</code>, how do we compute <code>Overlap</code> type for both? I think we can assume that <em>widens</em> simply means <code>extends</code>.<br />
The 64K dollar question is how is the <code>Overlap</code> computed? It is clearly not the same as intersection (the type operator <code>&amp;</code>), we have falsified that hypothesis in the previous section. Let’s try to look at some patterns:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="kw">const</span> helloDolly<span class="op">:</span> {<span class="dt">hello</span><span class="op">:</span> string} <span class="op">=</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;Dolly!&quot;</span>}</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a><span class="kw">const</span> datedHello<span class="op">:</span> {<span class="dt">hello</span><span class="op">:</span> string<span class="op">,</span> <span class="dt">since</span><span class="op">:</span> number} <span class="op">=</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;world!&quot;</span><span class="op">,</span> <span class="dt">since</span><span class="op">:</span><span class="dv">2022</span>}</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a><span class="kw">const</span> one <span class="op">=</span> <span class="dv">1</span> <span class="co">//const one: 1</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a><span class="kw">const</span> two <span class="op">=</span> <span class="dv">2</span> <span class="co">//const two: 2</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a><span class="kw">const</span> onenum<span class="op">:</span> number  <span class="op">=</span> <span class="dv">1</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a><span class="kw">const</span> twonum<span class="op">:</span> number  <span class="op">=</span> <span class="dv">2</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a><span class="kw">const</span> world<span class="op">:</span> string <span class="op">=</span> <span class="st">&quot;world&quot;</span></span></code></pre></div>
<div class="sourceCode" id="cb12"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="co">//fails, different literal types do not overlap</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="st">&quot;Dolly!&quot;</span> ===  <span class="st">&quot;world!&quot;</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a><span class="co">//fails, different literal types do not overlap</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a>one === two</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a><span class="co">//fails, string and number do not overlap</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a>one === world</span></code></pre></div>
<div class="sourceCode" id="cb13"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="co">//compilies, note both have the same type</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a>onenum <span class="op">===</span> twonum</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a><span class="co">//compiles, note 'typeof datedHello' extends 'typeof helloDolly' </span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a>helloDolly <span class="op">===</span> datedHello</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true"></a><span class="co">//compiles, the overlap seems to be the 'Person' type</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">tst</span> (x<span class="op">:</span> number <span class="op">|</span> Person<span class="op">,</span> y<span class="op">:</span> string <span class="op">|</span> Person) {</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true"></a>    <span class="cf">return</span> x <span class="op">===</span> y</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true"></a>}</span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true"></a></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true"></a><span class="co">//compiles, the overlap seems to be `{hello: string, since: number}` </span></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">testEqSemantics2</span>(a<span class="op">:</span> {<span class="dt">hello</span><span class="op">:</span> string} <span class="op">|</span> <span class="dv">1</span><span class="op">,</span> b<span class="op">:</span> <span class="st">&quot;boo&quot;</span> <span class="op">|</span> {<span class="dt">hello</span><span class="op">:</span> string<span class="op">,</span> <span class="dt">since</span><span class="op">:</span> number})<span class="op">:</span> boolean {</span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true"></a>    <span class="cf">return</span> a <span class="op">===</span> b</span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true"></a>}</span></code></pre></div>
<p>A possible rule for calculating <code>Overlap</code> could be (this is just a rough, high level heuristics, <em>please comment if you know a better definition</em>):</p>
<ul>
<li>for intersection types <code>X</code> and <code>Y</code>, if <code>X extends Y</code> take <code>X</code> else if <code>Y extends X</code> take <code>Y</code> otherwise reject</li>
<li>for union types <code>X = X1 | X2 | ...</code> and <code>Y = Y1 | Y2 | ...</code> recursively check if any <code>Xi</code> and <code>Yj</code> overlaps (this heuristics ignores performance cost)</li>
<li>for complex combinations of union and intersection types? I DUNNO, I have not tested it enough.</li>
</ul>
<p>I have not played with this assumption for a very long time, but so far these rules seem to hold with these exceptions:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="co">//All compile</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="dv">1</span> <span class="op">===</span> <span class="kw">null</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a><span class="dv">1</span> <span class="op">===</span> <span class="kw">undefined</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">tst2</span> (x<span class="op">:</span> <span class="dv">1</span><span class="op">,</span> y<span class="op">:</span> <span class="kw">null</span>) {</span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true"></a>    <span class="cf">return</span> x <span class="op">===</span> y</span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true"></a>}</span></code></pre></div>
<p>Does <code>1</code> have an overlap with <code>null</code> and <code>undefined</code>? What does that even mean? With the <em>strictNullChecks</em> compiler flag, <code>null</code> should be well separated from other types.<br />
This particular quirkiness is actually useful, it allows for a program to do conservative null checks even if the type indicates that it is not needed.</p>
<p>I hope you agree. This is complicated.<br />
I will hopefully bring this point even closer to home by the end of this post.</p>
<h3 id="hidden-blooper-side-note">Hidden blooper (side note)</h3>
<p>If you remove type annotations from the above definitions, the <code>helloDolly === datedHello</code> still compiles:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="kw">const</span> helloDolly <span class="op">=</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;Dolly!&quot;</span>}</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a><span class="kw">const</span> datedHello <span class="op">=</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;world!&quot;</span><span class="op">,</span> <span class="dt">since</span><span class="op">:</span><span class="dv">2022</span>}</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a>helloDolly <span class="op">===</span> datedHello <span class="co">//still compiles</span></span></code></pre></div>
<p>From a pragmatic standpoint this is very strange. <code>"Dolly!" ===  "world!"</code> is statically rejected, but <code>{hello: "Dolly!"} === {hello: "world!", since:"2022"}</code> is not.</p>
<p>This surprising situation is caused by the type inference widening the types. The types inferred in the expression <code>"world!" === "Dolly!"</code> are the literal types <code>"world!": "world!"</code> and <code>"Dolly!": "Dolly!"</code>, while the <code>helloDolly</code> and <code>datedHello</code> infer a <code>string</code> and <code>number</code> for their properties:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="co">//IntelliSense view of helloDolly</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a><span class="kw">const</span> helloDolly<span class="op">:</span> {</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a>    <span class="dt">hello</span><span class="op">:</span> string<span class="op">;</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a>}</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true"></a><span class="co">//IntelliSense view of datedHello</span></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true"></a><span class="kw">const</span> datedHello<span class="op">:</span> {</span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true"></a>    <span class="dt">hello</span><span class="op">:</span> string<span class="op">;</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true"></a>    <span class="dt">since</span><span class="op">:</span> number<span class="op">;</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true"></a>}</span></code></pre></div>
<p>TS allows to define the above object types using <code>as const</code>, e.g. <code>const helloDolly = {hello: "Dolly!"} as const</code> and <code>const datedHello = {hello: "world!", since:2022} as const</code>. It this is done <code>helloDolly === datedHello</code> will no longer compile but IMO, widening object property types is an arbitrary complexity.</p>
<h3 id="diy-equality">DIY equality</h3>
<p>The question is how far can I get by trying to reproduce safety around the <code>===</code> on my own.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a>declare <span class="kw">function</span> eq<span class="op">&lt;</span>T<span class="op">&gt;</span>(t1<span class="op">:</span> T<span class="op">,</span> t2<span class="op">:</span> T)<span class="op">:</span> boolean</span></code></pre></div>
<p>This generic function (it could be implemented by simply using <code>===</code>) forces both arguments to have the same type. That should give me at least some level of extra safety and prevent from comparing apples and oranges.<br />
Let’s see, starting with these type holes:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="co">//type holes shows a string, not bad, I would prefer the literal &quot;foo&quot;.</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true"></a><span class="fu">eq</span>(<span class="st">&quot;foo&quot;</span><span class="op">,</span> <span class="fu">_</span>())</span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true"></a><span class="co">//type holes shows unknown, Another unexpected 'uknown' widening issue? </span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true"></a><span class="fu">eq</span>(<span class="fu">_</span>()<span class="op">,</span> <span class="st">&quot;foo&quot;</span>)</span></code></pre></div>
<p>Let’s ignore the second type hole disappointing quirkiness and move on.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="co">//These all compile</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true"></a><span class="fu">eq</span>(<span class="dv">1</span> <span class="im">as</span> <span class="dv">1</span><span class="op">,</span> <span class="kw">null</span>)</span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true"></a><span class="fu">eq</span>(<span class="dv">1</span><span class="op">,</span> <span class="dv">2</span>)              <span class="co">//</span><span class="al">NOTE</span><span class="co"> we lost the type safety of ===</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true"></a><span class="fu">eq</span>(<span class="dv">1</span> <span class="im">as</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">2</span> <span class="im">as</span> <span class="dv">2</span>)    <span class="co">//</span><span class="al">NOTE</span><span class="co"> we lost the type safety of ===</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true"></a><span class="fu">eq</span>({<span class="dt">bye</span><span class="op">:</span> <span class="st">&quot;world&quot;</span>}<span class="op">,</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;world&quot;</span>})  <span class="co">//</span><span class="al">NOTE</span><span class="co"> we lost the (possibly erroneous) type safety preventing {bye: &quot;world&quot;} === {hello: &quot;world&quot;}</span></span></code></pre></div>
<p>How come these compile? These are all different types but TS can unify them into a supertype (next section will discuss it). These are all legitimate statements. Unfortunately, the type safety has been lost. This explains why the semantic narrowing around the <code>===</code> operator is needed. It is needed because structural subtyping can unify types even if types are very different.</p>
<p>However, quirkiness alert, these do not compile:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true"></a><span class="co">//Argument of type '&quot;boo&quot;' is not assignable to parameter of type '1'.ts(2345)</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true"></a><span class="fu">eq</span>(<span class="dv">1</span>, <span class="st">&quot;boo&quot;</span>)</span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true"></a><span class="co">//Argument of type '1' is not assignable to parameter of type '&quot;boo&quot;'.ts(2345)</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true"></a><span class="fu">eq</span>(<span class="st">&quot;boo&quot;</span>, <span class="dv">1</span>)</span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true"></a><span class="co">//Argument of type '{ hello: string; }' is not assignable to parameter of type '1'.ts(2345)</span></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true"></a><span class="fu">eq</span>(<span class="dv">1</span>, {hello: <span class="st">&quot;world&quot;</span>})</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true"></a><span class="co">//Argument of type '{ hello: string; }' is not assignable to parameter of type '&quot;boo&quot;'.ts(2345)</span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true"></a><span class="fu">eq</span>(<span class="st">&quot;boo&quot;</span>, {hello: <span class="st">&quot;world&quot;</span>})</span></code></pre></div>
<p><em>This is very unfortunate</em>, you want generic functions to work <em>consistently</em> across types. IMO this is a bug or an arbitrary complexity.<br />
The quirkiness seems to be related to the type inference working inconsistently and failing to widen the types if a string literal type is involved (next section will discussed it).</p>
<p>The narrative has run away from me, but the point should be somewhat clear: Generics provide only limited type safety in TS.<br />
E.g. enhanced safety semantics around <code>===</code> does not transfer to a DIY safety that a library solution could expose.</p>
<h3 id="subtyping">Subtyping</h3>
<p>How come this compiles?</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true"></a><span class="fu">eq</span>(<span class="dv">1</span> <span class="im">as</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">2</span> <span class="im">as</span> <span class="dv">2</span>) </span></code></pre></div>
<p>The type checker widens the types of both arguments to <code>1 | 2</code>. This is because of a subtyping rule that says that <code>1 extends (1 | 2)</code> and <code>2 extends (1 | 2)</code>.<br />
Here is a somewhat clever trick to see that:</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true"></a><span class="im">export</span> declare <span class="kw">function</span> unify<span class="op">&lt;</span>T<span class="op">&gt;</span>(t1<span class="op">:</span> T<span class="op">,</span> t2<span class="op">:</span> T) <span class="op">:</span> T</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true"></a></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true"></a><span class="co">//hovering over unify shows me:</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true"></a><span class="co">//(alias) unify&lt;1 | 2&gt;(t1: 1 | 2, t2: 1 | 2): 1 | 2</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true"></a><span class="fu">unify</span>(<span class="dv">1</span> <span class="im">as</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">2</span> <span class="im">as</span> <span class="dv">2</span>)</span></code></pre></div>
<p>if you do not believe me that <code>1 extends (1 | 2)</code> you can check it for yourself with another trick:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true"></a><span class="im">export</span> <span class="kw">function</span> verifyExtends<span class="op">&lt;</span>T2 <span class="kw">extends</span> T1<span class="op">,</span> T1<span class="op">&gt;</span>() {}</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true"></a></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span><span class="dv">1</span><span class="op">,</span> <span class="dv">1</span> <span class="op">|</span> <span class="dv">2</span><span class="op">&gt;</span>()</span></code></pre></div>
<p>However, TS appears to be not consistently good about inferring these subtyping rules. TS apparently did not notice that <code>1 extends (1 | "boo")</code> and <code>"boo" extends (1 | "boo")</code>. Hence the blooper</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span><span class="dv">1</span><span class="op">,</span> <span class="dv">1</span> <span class="op">|</span> <span class="st">&quot;boo&quot;</span><span class="op">&gt;</span>()</span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span><span class="st">&quot;boo&quot;</span><span class="op">,</span> <span class="dv">1</span> <span class="op">|</span> <span class="st">&quot;boo&quot;</span><span class="op">&gt;</span>()</span></code></pre></div>
<div class="sourceCode" id="cb25"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true"></a><span class="co">//Argument of type '&quot;boo&quot;' is not assignable to parameter of type '1'.ts(2345)</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true"></a><span class="fu">eq</span>(<span class="dv">1</span>, <span class="st">&quot;boo&quot;</span>)</span></code></pre></div>
<p>Let’s try to force TS into compliance by type annotating everything:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true"></a><span class="kw">const</span> booone <span class="op">:</span> <span class="dv">1</span> <span class="op">|</span> <span class="st">&quot;boo&quot;</span> <span class="op">=</span> <span class="st">&quot;boo&quot;</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true"></a><span class="kw">const</span> oneboo <span class="op">:</span> <span class="dv">1</span> <span class="op">|</span> <span class="st">&quot;boo&quot;</span> <span class="op">=</span> <span class="dv">1</span></span></code></pre></div>
<div class="sourceCode" id="cb27"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true"></a><span class="co">//Argument of type '1' is not assignable to parameter of type '&quot;boo&quot;'.ts(2345)</span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true"></a><span class="fu">eq</span>(booone, oneboo) </span></code></pre></div>
<div class="sourceCode" id="cb28"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true"></a><span class="co">//finally compiles with type application on 'eq'</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true"></a>eq<span class="op">&lt;</span>(<span class="dv">1</span> <span class="op">|</span> <span class="st">&quot;boo&quot;</span>)<span class="op">&gt;</span>(booone<span class="op">,</span> oneboo)</span></code></pre></div>
<p>We have seen that <code>===</code> narrowing is partially consistent with the intersection (<code>&amp;</code> operator).<br />
Let’s look at <code>&amp;</code> semantics a little closer.</p>
<p>We can try to double check how the <code>&amp;</code> intersection works by doing this:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true"></a><span class="co">//both compile suggesting that Person is equivalent to the intersection  (number | Person) &amp; (string | Person)</span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span>Person<span class="op">,</span> (number <span class="op">|</span> Person) <span class="op">&amp;</span> (string <span class="op">|</span> Person)<span class="op">&gt;</span>()</span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span>(number <span class="op">|</span> Person) <span class="op">&amp;</span> (string <span class="op">|</span> Person)<span class="op">,</span> Person<span class="op">&gt;</span>()</span></code></pre></div>
<p>However this does not compile, and it does look like a bug (see second line of the error message):</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true"></a><span class="co">//Type '(1 | &quot;boo&quot;) &amp; (&quot;boo&quot; | Person)' does not satisfy the constraint '&quot;boo&quot;'.</span></span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true"></a><span class="co">//  Type '1 &amp; Person' is not assignable to type '&quot;boo&quot;'.ts(2344)</span></span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true"></a>verifyExtends&lt;(<span class="dv">1</span> | <span class="st">&quot;boo&quot;</span>) &amp; (<span class="st">&quot;boo&quot;</span> | Person), <span class="st">&quot;boo&quot;</span>&gt;()</span></code></pre></div>
<p><em>Complexity is a super food for bugs.</em></p>
<p>Here is my quick summary: subtyping is complex and it weakens type safety. TS tries to recover the safety by building complex narrowing semantics around a selected set of JS operators. There are many inconsistencies in both the implementation of subtyping and the implementation of narrowing semantics.</p>
<p> <div class="side-note"><strong>Side Note about Arity</strong>: In TypeScript, functions are also subject to subtyping rules.</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span>() <span class="kw">=&gt;</span> number<span class="op">,</span> (_<span class="op">:</span>string) <span class="kw">=&gt;</span> number<span class="op">&gt;</span>()</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span>(_<span class="op">:</span>string) <span class="kw">=&gt;</span> number<span class="op">,</span> (_1<span class="op">:</span>string<span class="op">,</span>_2<span class="op">:</span>boolean) <span class="kw">=&gt;</span> number<span class="op">&gt;</span>()<span class="op">&gt;</span></span></code></pre></div>
<p>see also <a href="https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-with-fewer-parameters-assignable-to-functions-that-take-more-parameters" target="_blank">functions with fewer parameters are assignable to functions that take more parameters</a>. This is convenient, it can safe a few characters when writing programs.<br />
It is also, IMO, dangerous and complex. We have seen this leading to surprising behavior in <a href="2021-12-12-ts-types-part1.html#compilation-bloopers" target="_blank">Part 1, Compilation bloopers</a> section.  </div></p>
<h3 id="comparative-complexity-rant">Comparative complexity rant</h3>
<p>A “type enthusiast” will associate types with correctness, even formal verification. To me, the words “messy” and “type” are self contradictory. TS “types” support some interesting features but are a mess.</p>
<p>I want to contrast the above <code>===</code> and <code>eq</code> examples against a programming language that has been designed around types from the beginning. An example could be an FP language like Elm, PureScript, or Haskell (I am not that familiar with ReasonML or OCaml)<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>.<br />
These languages have much simpler types. The safety around equality does not require any special narrowing semantics. You get it for free in any DIY function that has 2 arguments sharing the same generic type (only they call it polymorphic not generic).</p>
<p>One underlying reason for this is the lack of complex subtyping and OO features. <code>eq(x,y)</code> will not compile if <code>x</code> and <code>y</code> have different types. There is no way to unify <code>x</code> and <code>y</code> to some supertype because there are no subtypes or supertypes.<br />
But, you may say, JS object polymorphism is very useful. All the 3 languages listed above provide support for polymorphic record types<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a>, only they use much simpler techniques than subtyping to achieve it.<br />
These languages also come with well thought out semantic rules that are often formalized and come with soundness proofs.<br />
The types in these languages are much simpler (not necessarily easier but simpler).</p>
<p>Type complexity translates to a confused type checker and to a confused developer.<br />
<em>Programming in a language in which I do not fully understand the types equates to me writing programs I do not fully understand.</em></p>
<p>I expect that to become a seasoned TS developer, one needs to remember a big dictionary of idiosyncratic compiler behaviors. <a href="https://github.com/Microsoft/TypeScript/wiki/FAQ#common-bugs-that-arent-bugs" target="_blank">Common Bugs that aren’t bugs</a> is, I think, just a warm up reading to achieve such mastery.<br />
Were you surprised about the gotchas we have uncovered in <a href="2021-12-12-ts-types-part1.html" target="_blank">Part 1</a>? Is the above <a href="#semantics-rejected-overlap">overlap issue</a> a well known problem<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>? Call me weird, but I would rather be learning PLT or Type Theory than these gotchas.</p>
<p>It is worth noting that TypeScript has over a million users. FP languages have tens of thousands of users (if combined). TypeScript has more resources to improve. What makes for fewer bugs, lots of dollars or clean types?<br />
I do not think there is a clear answer to this question. However, resources can’t solve all the problems. Programming languages are almost paranoid about backward compatibility and backward compatibility does not like changing things, even if the change is fixing bugs.<br />
So I am afraid, a simple language like Elm will always be cleaner and more robust.</p>
<p>Forgetting about the popularity context, I view it as a trade-off: suffer because of the type complexity and reduced type safety but see a readable JavaScript and trivially integrate with the rest of JS ecosystem <em>vs</em> introduce a language that has nicer types, greater type safety, predictable compiler, but lose generated JS code clarity and suffer when integrating JS libraries.<br />
This trade-off is IMO not trivial and very project dependent. Clean types vs clean JS, I typically select the clean types. The ecosystem compatibility issue is a little harder to ignore and the main reason I am writing code in TS. Projects with a high correctness requirement, IMO, should select an FP language, the optimal choice for other projects is less clear.</p>
<h3 id="variance-problems">Variance problems</h3>
<p>I will finish with some examples that may feel even more surprising.</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true"></a><span class="kw">const</span> bye <span class="op">=</span> {<span class="dt">bye</span><span class="op">:</span> <span class="st">&quot;world&quot;</span>}</span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true"></a><span class="kw">const</span> hello <span class="op">=</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;world&quot;</span>}</span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true"></a></span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true"></a>declare <span class="kw">function</span> eqArrays<span class="op">&lt;</span>T<span class="op">&gt;</span>(t1<span class="op">:</span> T[]<span class="op">,</span> t2<span class="op">:</span> T[])<span class="op">:</span> boolean</span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true"></a></span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true"></a><span class="fu">eqArrays</span>([{<span class="dt">bye</span><span class="op">:</span> <span class="st">&quot;world&quot;</span>}]<span class="op">,</span> [{<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;world&quot;</span>}]) <span class="co">//compiles</span></span></code></pre></div>
<div class="sourceCode" id="cb33"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true"></a><span class="co">//Compilation error</span></span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true"></a><span class="co">//Property 'bye' is missing in type '{ hello: string; }' but required in type '{ bye: string; }'.ts(2741)</span></span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true"></a><span class="fu">eqArrays</span>([bye], [hello])</span></code></pre></div>
<p>Here is another example:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true"></a><span class="kw">interface</span> Payload<span class="op">&lt;</span>T<span class="op">&gt;</span> {<span class="dt">payload</span><span class="op">:</span> T}</span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true"></a></span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true"></a><span class="co">// ... we would see the same behavior for:</span></span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true"></a><span class="co">//type Payload1&lt;T&gt; = {payload: T} </span></span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true"></a></span>
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true"></a>declare <span class="kw">function</span> eqPayloads<span class="op">&lt;</span>T<span class="op">&gt;</span>(t1<span class="op">:</span> Payload<span class="op">&lt;</span>T<span class="op">&gt;,</span> t2<span class="op">:</span> Payload<span class="op">&lt;</span>T<span class="op">&gt;</span>)<span class="op">:</span> boolean</span>
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true"></a></span>
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true"></a><span class="fu">eqPayloads</span>({<span class="dt">payload</span><span class="op">:</span> {<span class="dt">bye</span><span class="op">:</span> <span class="st">&quot;world&quot;</span>}}<span class="op">,</span> {<span class="dt">payload</span><span class="op">:</span> {<span class="dt">hello</span><span class="op">:</span> <span class="st">&quot;world&quot;</span>}})  <span class="co">//compilies</span></span></code></pre></div>
<div class="sourceCode" id="cb35"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true"></a><span class="co">// Compilation error:</span></span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true"></a><span class="co">// Property 'bye' is missing in type '{ hello: string; }' but required in type '{ bye: string; }'.ts(2741)</span></span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true"></a><span class="fu">eqPayloads</span>({payload: bye}, {payload: hello})</span></code></pre></div>
<p>My first instinct was to assume that this weird behavior is caused by TS treating <code>T[]</code> and <code>Payload&lt;T&gt;</code> conservatively as invariant. Unfortunately, this is not the case. The above quirkiness looks to be just another type inference issue and there is a deeper safety problem.</p>
<p>TS implements variance incorrectly and makes both <code>T[]</code> and <code>Payload&lt;T&gt;</code> covariant (e.g. TS assumes that <code>P extends T</code> implies <code>Payload&lt;P&gt; extends Payload&lt;T&gt;</code>). Here is a well known Java language bug reimplemented in TS:</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true"></a><span class="co">//how to put a string into a list of numbers</span></span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true"></a><span class="kw">const</span> intlist<span class="op">:</span> number[] <span class="op">=</span> [<span class="dv">1</span><span class="op">,</span><span class="dv">2</span><span class="op">,</span><span class="dv">3</span>]</span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true"></a><span class="kw">const</span> list<span class="op">:</span> unknown[] <span class="op">=</span> intlist</span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true"></a>list<span class="op">.</span><span class="fu">push</span>(<span class="st">&quot;not a number&quot;</span>) <span class="co">//compiles</span></span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true"></a></span>
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true"></a><span class="co">//array is incorrectly covariant</span></span>
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span><span class="kw">typeof</span> datedHello[]<span class="op">,</span> <span class="kw">typeof</span> helloDolly[]<span class="op">&gt;</span>() <span class="co">//datedHello extends helloDolly type</span></span></code></pre></div>
<p>I see the same incorrect subtyping on the <code>Payload</code> interface:</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true"></a><span class="co">//interface Payload is incorrectly covariant</span></span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span>Payload<span class="op">&lt;</span><span class="kw">typeof</span> datedHello<span class="op">&gt;,</span> Payload<span class="op">&lt;</span><span class="kw">typeof</span> helloDolly<span class="op">&gt;&gt;</span>()</span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true"></a>verifyExtends<span class="op">&lt;</span>Payload<span class="op">&lt;</span><span class="kw">typeof</span> datedHello<span class="op">&gt;,</span> Payload<span class="op">&lt;</span>object<span class="op">&gt;&gt;</span>()</span></code></pre></div>
<p>Implementations of <code>interface Payload&lt;T&gt;</code> do not need to behave in a covariant way.<br />
An example in the linked github repo exploits <code>interface Payload&lt;T&gt;</code> covariance and ends up passing a <code>number</code> to a function that accepts <code>string</code> input.</p>
<p>Invariance would have been a better (a more conservative) choice for both <code>interface Payload&lt;T&gt;</code> and the array.<br />
A careful reader may notice that the structurally typed <code>type Payload1&lt;T&gt; = {payload: T}</code> should also be invariant since the <code>payload</code> property is mutable (getters are covariant, setters are contravariant). TS incorrectly makes it covariant.</p>
<p>I will sound like a broken record now, subtyping is clearly very complex.</p>
<p>I did more digging into it after writing this note. It appears that the intention was to keep TS conceptually easy (<a href="https://github.com/microsoft/TypeScript/issues/1394" target="_blank">issue #1394</a>).<br />
The result may be easy but is definitely not simple.</p>
<p><em>Incorrect is never simple.</em></p>
<p> <div class="side-note"><strong>Observation (Rant Alert)</strong>: There is a tendency to focus on common cases and ignore corner cases. This tendency has a broad scope, broader than TS. What has (typically) a lower cost: resolving a problem that every user observes when opening the app or resolving a problem that affects 1% of users once a month? Are less frequently observed defects assigned a lower priority? Not really.<br />
Common approach to software and language design and the economics of software maintenance are an ill matched couple.  </div></p>
<h3 id="summary">Summary</h3>
<p>This was a very hard note to write. I rewrote it several times. How do I write about complexity and make it simple to read?<br />
Seems like a catch-22 problem.</p>
<blockquote>
<p>“One does not simply explain TS types”</p>
</blockquote>
<p><em>Boromir about TypeScript</em></p>
<p>Again, my main claims are:</p>
<ul>
<li>subtyping adds significant complexity and lowers type safety</li>
<li>ad-hoc semantic narrowing around JS operators partially recovers safety, but is complex by itself and scope limited</li>
</ul>
<p>Languages with simpler and more reliable type systems are not a superset of JS syntax and are idiomatically far from JS<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a>.</p>
<p>We have observed some compilation issues and irregularities. To summarize these:</p>
<ul>
<li>issues inferring literal types widened to a union (<a href="#subtyping"><code>eq(1, "boo")</code></a>)</li>
<li>issues preventing intersecting unions involving literal types (<a href="#subtyping"><code>(1 | "boo") &amp; ("boo" | Person)</code></a>)</li>
<li>unexpected widening of literal object property types (<a href="#hidden-blooper-side-note">hidden blooper</a>)</li>
<li>inconsistent widening of function arguments (top of <a href="#variance-problems">variance problems</a>)</li>
<li>incorrect handling of variance (<a href="#variance-problems">variance problems</a>)</li>
<li><code>===</code> rejects the <code>&amp;</code> overlap of intersection types, while claiming the opposite in the error message (<a href="#semantics-rejected-overlap">rejected overlap</a>)</li>
</ul>
<p>I cannot identify TypeScript documentation or tickets relevant to these bullets. The subset I have checked against <a href="https://github.com/microsoft/TypeScript/issues" target="_blank">TS issue board</a> is either in the known issues and / or “Working as Intended” category. My question about known issues is: known by whom?</p>
<p>Introduced tools</p>
<div class="sourceCode" id="cb38"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true"></a>declare <span class="kw">function</span> unify<span class="op">&lt;</span>T<span class="op">&gt;</span>(t1<span class="op">:</span> T<span class="op">,</span> t2<span class="op">:</span> T) <span class="op">:</span> T</span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true"></a><span class="kw">function</span> verifyExtends<span class="op">&lt;</span>T2 <span class="kw">extends</span> T1<span class="op">,</span> T1<span class="op">&gt;</span>() {}</span></code></pre></div>
<p>can be used to ask TS subtyping questions.</p>
<h2 id="next-chapter">Next Chapter</h2>
<p>This post has been about the “messy” in TS. The next installment will focus on programming with type variables and will present TS in a better light. I decided to split advanced topics into 2 smaller posts. I plan to discuss phantom types, type variable scoping, a pattern emulating existential types, and rank 2 types. I consider these to be quite useful typing approaches. I will also show a trick that prevents <code>unknown</code> and supertype widening.</p>
<p>Here is the link: <a href="2022-01-09-ts-types-part4.html" target="_blank">Part 4</a>.</p>
<p>Happy New Year to all of my readers. Thank you for reading.</p>
<h2 id="summary-of-final-edits">Summary of final edits</h2>
<ul>
<li>Added information about <code>as const</code> in <a href="#hidden-blooper-side-note">Hidden blooper note</a></li>
<li>Added note about tickets relevant to the <a href="#semantics-rejected-overlap">overlap issue</a> (see footnote <a href="#fn4">4</a>)</li>
<li>Added side note about arity in <a href="#subtyping">Subtyping</a>.</li>
</ul>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p><a href="#semantics-rejected-overlap">rejected overlap</a> section explains why I call it a guess.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>All can be used for frontend development and can be compiled to JS.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3" role="doc-endnote"><p>Haskell is still improving on this aspect. IMO, the need for polymorphic access to record fields is overrated. I would trade it for a capable compiler any time.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4" role="doc-endnote"><p>See <a href="https://github.com/microsoft/TypeScript/issues/27910" target="_blank">#27910</a> I created <a href="https://github.com/microsoft/TypeScript/issues/48628" target="_blank">#48628</a> which was originally marked as “Working as Intended” and then moved to “Duplicate” status.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5" role="doc-endnote"><p>I have not used <em>flow</em> recently, and I cannot compare TS to it. However <em>flow</em> has subtyping which I do not consider simple. Indeed, some level of subtyping support is needed to support commonly used JS idioms.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

    </section>
</article>
]]></summary>
</entry>
<entry>
    <title>Type Enthusiast's Notes about TypeScript. Part 2. Typing Honestly</title>
    <link href="https://rpeszek.github.io//posts/2021-12-24-ts-types-part2.html" />
    <id>https://rpeszek.github.io//posts/2021-12-24-ts-types-part2.html</id>
    <published>2021-12-24T00:00:00Z</published>
    <updated>2021-12-24T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on December 24, 2021
        
            by Robert Peszek
        
        
        
        <div class="changelog"> <div>Revision History: <ul> <li> (2022.04.29 - 2022.05.29) Minor edits </li> <li> (2022.05.29) Draft warning removed </li> <li> (2022.08.30) added <a href="../tags/patterns-of-erroneous-code.html">patterns-of-erroneous-code</a> tag </li> </ul> </div></div>
        
        
        <div class="info">Tags: <a title="All pages tagged 'TypeScript-Notes'." href="../tags/TypeScript-Notes.html">TypeScript-Notes</a>, <a title="All pages tagged 'patterns-of-erroneous-code'." href="../tags/patterns-of-erroneous-code.html">patterns-of-erroneous-code</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <div class="toc"><div class="header">Table of Contents</div>
<ul>
<li><a href="#nutshell">Nutshell</a></li>
<li><a href="#can-i-trust-the-types">Can I trust the types?</a></li>
<li><a href="#note-about-the-any-type">Note about the <code>any</code> type</a></li>
<li><a href="#casting-casting-in-a-bad-light">Casting <em>casting</em> in a bad light</a>
<ul>
<li><a href="#improving-office.js-with-type-predicates">Improving <em>office.js</em> with type predicates</a></li>
</ul></li>
<li><a href="#note-about-the-unknown-type">Note about the <code>unknown</code> type</a></li>
<li><a href="#honest-typing-conventions">Honest typing conventions</a>
<ul>
<li><a href="#referential-transparency">Referential Transparency</a></li>
<li><a href="#types-as-documentation">Types as documentation</a></li>
</ul></li>
<li><a href="#next-chapter">Next Chapter</a></li>
</ul>
</div>
<p><em>Please Leave Feedback in: <a href="https://github.com/rpeszek/rpeszek.github.io/discussions/1" target="_blank">git discussions</a></em></p>
<p>Previous post: <a href="2021-12-12-ts-types-part1.html" target="_blank">Part 1. Typing in Anger</a>.</p>
<p><strong>Disclaimers:</strong> (imagine this is a very small font, read it very fast in a half whisper)<br />
<em>I assume strict compiler flags are on, something you get by default with scaffolding, e.g. using <code>create-react-app my-project --template typescript</code> is close enough.<br />
The code examples have been tested with TypeScript v4.4.4 and v4.5.2.<br />
office.js examples are based on https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js and <span class="citation" data-cites="types/office-js">@types/office-js</span><span class="citation" data-cites="1.0.221">@1.0.221</span> (these match the current scaffold for office.js/React).<br />
This post is a pandoc output of a markdown document and code examples are not interactive.<br />
Most of the code examples are published in <a href="https://github.com/rpeszek/ts-experiments/tree/master/ts-notes" target="_blank">ts-notes</a> folder in this github repo: <a href="https://github.com/rpeszek/ts-experiments" target="_blank">ts-experiments</a>.</em></p>
<p><strong>Motivating Quote for the series:</strong></p>
<blockquote>
<p>“TypeScript began its life as an attempt to bring traditional object-oriented types to JavaScript so that the programmers at Microsoft could bring traditional object-oriented programs to the web. As it has developed, TypeScript’s type system has evolved to model code written by native JavaScripters. The resulting system is <em>powerful, interesting and messy.</em>”</p>
</blockquote>
<p><em>From typescriptlang <a href="https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html" target="_blank">TypeScript for Functional Programmers</a></em></p>
<h2 id="nutshell">Nutshell</h2>
<p>This is the second post in the series devoted to types in TypeScript. In this series, I explore type-centric approaches to writing code and often push TS to its limits in doing so. I am writing these posts for like minded developers who are interested in types and either use or consider using TypeScript.</p>
<p>This post will cover TS’s type predicates, the notorious <code>any</code>, and its safer cousin the <code>unknown</code>. These are well known and heavily blogged topics. My goal is provide a little different perspective with a more type-centric view point.<br />
This series uses <em>office.js</em> as a source of code examples. This post examines the correctness of <em>office.js</em> types and fixes them using type predicates.<br />
My main code example is something I am excited about. It demonstrates a case where TS made me completely rethink a previously written JS code.<br />
I will discuss some safety concerns about <code>unknown</code> (no, this is not a typo, I mean the <code>unknown</code> type) and will set the stage for my future note about complexity of TS types.<br />
I will finish in the realm of coding conventions discussing transparent, self documenting type definitions.</p>
<h2 id="can-i-trust-the-types">Can I trust the types?</h2>
<p>I am going to discuss the obvious gotcha in a gradually typed language like TS: runtime values do not satisfy statically defined types.<br />
Despite it being an obvious concern, the issue is something a developer who spends most time in a statically typed language (e.g. me) will not have on his / her mind when working in TS.<br />
The following seem to be the prevalent reasons for why values do not match types: overconfident TS code (e.g. type casting, <code>any</code> type), issues with converted JavaScript (declaration files out of sync or containing otherwise incorrect definitions). I am going to show a real life (or close to real life) example of each.</p>
<p>The series started with an example defining the <code>Person</code> type, to avoid jumping back and forth I will repeat it here</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a>type Person <span class="op">=</span> {<span class="dt">firstNm</span><span class="op">:</span> string<span class="op">,</span> <span class="dt">lastNm</span><span class="op">:</span> string} </span></code></pre></div>
<p>This will be a good conversation starter:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="co">//Questionable JSON parsing example</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="kw">const</span> p<span class="op">:</span> Person <span class="op">=</span> <span class="bu">JSON</span><span class="op">.</span><span class="fu">parse</span>(<span class="st">'&quot;John Smith&quot;'</span>)</span></code></pre></div>
<p>Your experience with consistency of JSON data may be different from mine. I rarely see JSON issues in a frontend - backend conversation. On the other hand, my experience with using 3rd party REST APIs is not exactly stellar. JSON data problems do happen.</p>
<p>The above code illustrates what I used to call ‘fail late’ and now I call ‘a type I cannot trust’ case. It is a nasty situation where runtime errors are nowhere near the actual problem. Looking at the example, <code>JSON.parse</code> function is declared to return the TS’s notorious <code>any</code> type. Using <code>any</code> bypasses type checking and the code assigns the result to <code>Person</code>. The actual run-time value of <code>p</code> will be a <code>string</code>, while the type checker is now convinced it is <code>p:Person</code>.</p>
<p>Now, look at the top rated answer in this stackoverflow: <a href="https://stackoverflow.com/questions/38688822/how-to-parse-json-string-in-typescript" target="_blank">how-to-parse-json-string-in-typescript</a>. It appears that the above code matches the top rated answer. Yes, safer approaches are available (look at less popular answers, we will discuss a much safer way as well).<br />
I am not claiming this to be a prevalent problem in TS code, but it is an interesting issue caused by the coexistence of the typed and the untyped.</p>
<p>Now, since I already may have angered a large part of the TS community (did I? I hope not.), let’s beat a little on <strong><em>office.js</em></strong>.</p>
<p><em>office.js</em> is a source of code examples for my series. Looking into <em>office.js</em> release history suggests that a bond between <em>office.js</em> and TypeScript. That bond developed very early. It looks like these projects grew up together. <em>office.js</em> might have even been one of these Microsoft projects that spearheaded the development of TS.</p>
<p><strong>Short Recap</strong> We are using <em>office.js</em> to interact with Outlook emails. <em>office.js</em> provides us with <code>item: Office.MessageRead</code> allowing us to retrieve data from an email opened for viewing in Outlook. <strong>(Recap End)</strong></p>
<p>I imagine it is not that uncommon for a TS library to have a non-nullable property that is undefined at runtime.<br />
The IntelliSense tells me that <code>item: Office.MessageRead</code> contains an overloaded <code>item.body.getTypeAsync</code> method. I was hoping to use it to retrieve the type (plain text vs html) of the email body.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a>(method) Office<span class="op">.</span><span class="at">Body</span><span class="op">.</span><span class="fu">getTypeAsync</span>(options<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncContextOptions</span><span class="op">,</span> callback<span class="op">?:</span> ((asyncResult<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>) <span class="op">|</span> <span class="kw">undefined</span>)<span class="op">:</span> <span class="kw">void</span> (<span class="op">+</span><span class="dv">1</span> overload)</span></code></pre></div>
<p><code>getTypeAsync</code> is undefined at runtime. It looks to me like the TS declaration files are not in sync with JavaScript. My hypothesis seems to be confirmed by the <a href="https://docs.microsoft.com/en-us/javascript/api/outlook/office.body?view=outlook-js-preview#getTypeAsync_callback_" target="_blank"><code>item.body.getTypeAsync</code></a> documentation suggesting that this method is available when email is open in compose mode (not when using <code>Office.MessageRead</code>). (I am using office online and the latest <em>office.js</em> as of the time of this writing.)<br />
<em>Please message me in git discussions if you think I am misrepresenting it.</em></p>
<p><em>It seems like <em>office.js</em> types are a little off.</em></p>
<p>We should look at the type definition of the <em>office.js</em> <a href="https://docs.microsoft.com/en-us/javascript/api/outlook/office.item?view=outlook-js-preview" target="_blank"><code>Office.context.mailbox.item</code></a> a little closer.<br />
This property is overloaded to be one of the following types (let me call them <em>facets</em>):</p>
<blockquote>
<p><code>Office.AppointmentCompose</code> (composing calendar entry)<br />
<code>Office.AppointmentRead</code> (reading calendar entry)<br />
<code>Office.MessageCompose</code> (composing email)<br />
<code>Office.MessageRead</code> (reading email)</p>
</blockquote>
<p>These <em>facet</em> types are all different. For example, to get email subject you use <code>item.subject:string</code> if you are working with <code>Office.MessageRead</code> or <code>item.subject:Office.Subject</code> if you are working with <code>Office.MessageCompose</code>.<br />
<code>Office.Subject</code> contains <code>getAsync</code>, <code>setAsync</code> methods and is absolutely not a <code>string</code>.</p>
<p>The type of <code>item</code> provided by <em>office.js</em> is not, as I would expect:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">//Type I expected</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a>AppointmentCompose <span class="op">|</span> AppointmentRead <span class="op">|</span> MessageCompose <span class="op">|</span> MessageRead</span></code></pre></div>
<p>Rather it is closer (I have not listed all the <code>&amp;</code>-s) to:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="co">//Actual Type with some &amp; parts removed </span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a>AppointmentCompose <span class="op">&amp;</span> AppointmentRead <span class="op">&amp;</span> MessageCompose <span class="op">&amp;</span> MessageRead</span></code></pre></div>
<p>Basically, the type <em>office.js</em> chose for <code>item</code> mashes all the available properties, methods, overloads into one type. This is simply an incorrect type for the <code>item</code> property. Runtime values do not satisfy the <em>intersection</em> type, they satisfy the <em>union</em> type. Type checked programs will fail at runtime. <em>office.js</em> type declarations are incorrect.</p>
<p><em>office.js types are off for sure.</em></p>
<p>In a weird way, this explains why the undefined <code>item.body.getTypeAsync</code> has not been noticed. Without a corrective reassignment to, say, <code>Office.MessageRead</code> many other methods are <code>undefined</code> at runtime and it is harder to single this particular one out.</p>
<p>Gradual typing over the wild-west JS has to come with maintenance challenges.<br />
Nonetheless this is surprising. What are the types good for if they’re not accurate?</p>
<blockquote>
<p>“You take the blue pill — the story ends, you wake up in your bed and believe whatever you want to believe.<br />
You take the red pill — you stay in Wonderland, and I show you how deep the rabbit hole goes”</p>
</blockquote>
<p><em>Morpheus about not believing types in a gradually typed language</em><br />
<em>… nightmares of JavaScript running on my walls and ceilings make me wake up screaming</em></p>
<h2 id="note-about-the-any-type">Note about the <code>any</code> type</h2>
<p>My first example in this post used the infamous <code>any</code> type. Let’s have a closer look.</p>
<p><code>any</code> type is crazy. It behaves like the <em>top</em> (you can assign any other type to it). It also behaves like the <em>bottom</em> (it can be assigned to any other type, maybe except of <em>never</em>). Ideally, the bottom type is empty, this one clearly is not.</p>
<p><em>As a result, any value can have any type.</em></p>
<p>We should have some fun with this.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="co">//express yourself with _any_ (notice no casting, only assignments)</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="kw">const</span> sad<span class="op">:</span> any <span class="op">=</span> <span class="st">&quot;emptiness and sadness&quot;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a><span class="kw">const</span> sadVoid<span class="op">:</span> <span class="kw">void</span> <span class="op">=</span> sad</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a><span class="kw">const</span> myCallback <span class="op">=</span> (n<span class="op">:</span> number)<span class="op">:</span> <span class="kw">void</span> <span class="kw">=&gt;</span> {</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>    <span class="cf">return</span> sadVoid<span class="op">;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a>}</span></code></pre></div>
<p>You can have your own favorite <code>null</code> that is not <code>null</code> value, you can <em>define</em> your own <code>undefined</code>. Sky and your creativity are the limits. I will spoil this party and say that I do not recommend doing it. Oh, maybe just a little. Well OK, one more:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="kw">const</span> sassy<span class="op">:</span> any <span class="op">=</span> {<span class="dt">netWorth</span><span class="op">:</span> <span class="st">&quot;billion dollars&quot;</span><span class="op">,</span> <span class="dt">popularityLevel</span><span class="op">:</span> <span class="st">&quot;celebrity&quot;</span>}</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="kw">const</span> sassyNull<span class="op">:</span> <span class="kw">null</span> <span class="op">=</span> sassy</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a><span class="kw">const</span> p<span class="op">:</span> Person <span class="op">|</span> <span class="kw">null</span> <span class="op">=</span> sassyNull</span></code></pre></div>
<p>A bottom that is not empty will cause the language to be unsound. Allowing all values in a bottom type, I would call it insane.<br />
However, using an <em>any</em> type similar to TS’s seems to be a common practice in gradually typed languages (e.g. Python does it too).<br />
Using <code>any</code> is like saying “hey, TS, please suspend type checking, I know what I am doing”. This is the antithesis of type safety, but what else can TS do and maintain JS compatibility?</p>
<p>Actually, TS has a very clever solution for this, it is described in the following sections.<br />
I view <code>any</code> as a form of type coercion or casting.</p>
<h2 id="casting-casting-in-a-bad-light">Casting <em>casting</em> in a bad light</h2>
<p>I will use the term casting and type coercion interchangeably. TypeScript documentation also uses the term <em>type assertion</em>. I view the <code>any</code> type to be in the same boat as well (an implicit type coercion).<br />
TS uses the <code>t as T</code> or <code>&lt;T&gt; t</code> syntax to cast expression <code>t</code> into type <code>T</code>, e.g. <code>iAmSureIsString as string</code>.<br />
(IMO, the second notation, <code>&lt;T&gt; t</code>, is somewhat unfortunate as it is very similar to type application and generic function declaration e.g. <code>const f = &lt;T&gt;():T</code> declares, <code>&lt;T&gt;f()</code> casts, <code>f&lt;T&gt;()</code> applies. I recommend the <code>v as T</code> syntax to make casting more explicit and searchable in your code.)</p>
<p> <div class="side-note"><strong>Type enthusiast’s note on casting at large:</strong><br />
Typically (and rightly) casting is considered to be a last resort, <em>only cast if you must</em>.</p>
<p>With more involved types it is often harder to write code that type checks. That increases the appeal of casting or finding some other alternatives for nudging the type checker into agreeing.<br />
Some languages offer the ability to write a program to persuade the type checker about type equality (write actual <em>proof of type equality</em>). This is an advanced feature and is available in only a few languages (e.g. Coq, Idris, Haskell). Writing such programs is often challenging or even impossible. (I consider writing such proofs to be one of the highest level “type games” that a developer can play. It is both a challenge and fun. A great intro is <a href="https://www.manning.com/books/type-driven-development-with-idris" target="_blank">TDD with Idris</a>)</p>
<p>There is an alternative to type coercion that allows programs to type check but will throw an exception when executed.<br />
This can be useful for interacting with the type checker when writing code. We have seen a TS version of this already, function <a href="2021-12-12-ts-types-part1.html#type-holes" target="_blank"><code>_&lt;T&gt;(): T</code></a>, defined in my previous post and stolen from <a href="https://dev.to/gcanti/type-holes-in-typescript-2lck" target="_blank">Type holes in TS</a>. Such programming practice is foreign to most languages but becomes very convenient when working with more involved types. We are using it in this series.  </div></p>
<p>Let’s beat on <em>office.js</em> some more. <a href="https://docs.microsoft.com/en-us/javascript/api/outlook/office.item" target="_blank">Here</a> is a piece <em>office.js</em> documentation about (you guessed it, this post is so very predictable) the <a href="https://docs.microsoft.com/en-us/javascript/api/outlook/office.item" target="_blank"><code>Office.context.mailbox.item</code></a>:</p>
<blockquote>
<p><em>If you want to see IntelliSense for only a specific type or mode, <strong>cast</strong> this item to one of the following:</em><br />
<em><code>AppointmentCompose</code></em><br />
<em><code>AppointmentRead</code> …</em></p>
</blockquote>
<p>TS offers a neat alternative to casting. I will explain it by <em>not</em> following the <em>office.js</em> documentation ;)</p>
<p>As I indicated already, I can interact with outlook email using <code>Office.context.mailbox.item</code>. However, <code>item</code> property is overloaded into several types discussed in the previous section (I called them <em>facets</em>):</p>
<p>The legacy code I am currently re-implementing at work is retrieving the email subject using <code>item.subject</code> and checking what kind of <code>item.subject</code> it is (a string, has asyc methods, etc) and using it accordingly. It does a similar <em>“check before you use”</em> game to retrieve <code>to</code>, <code>from</code>, <code>cc</code> and other email information.<br />
Such an approach is typical, almost idiomatic to JS. It is also hard to maintain as making changes directed at one facet can easily break the other facets. And you can test your heart out on all emails you can think about and your app will still crash and burn if used with an office calendar appointment.</p>
<p>So what is the new TS-idiomatic way to do it?  TS has the <code>is</code> types.</p>
<h3 id="improving-office.js-with-type-predicates">Improving <em>office.js</em> with type predicates</h3>
<div class="sourceCode" id="cb8"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="im">export</span> <span class="kw">const</span> isMessageRead <span class="op">=</span> (item<span class="op">:</span> any)<span class="op">:</span> item is Office<span class="op">.</span><span class="at">MessageRead</span> <span class="kw">=&gt;</span> {</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>    <span class="cf">return</span> (item<span class="op">.</span><span class="at">itemType</span> <span class="op">===</span> Office<span class="op">.</span><span class="at">MailboxEnums</span><span class="op">.</span><span class="at">ItemType</span><span class="op">.</span><span class="at">Message</span>) <span class="op">&amp;&amp;</span> item<span class="op">.</span><span class="at">getAttachmentsAsync</span> <span class="op">===</span> <span class="kw">undefined</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a>} </span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>  </span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a><span class="im">export</span> <span class="kw">const</span> isMessageCompose <span class="op">=</span> (item<span class="op">:</span> any)<span class="op">:</span> item is Office<span class="op">.</span><span class="at">MessageCompose</span> <span class="kw">=&gt;</span> {</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a>    <span class="cf">return</span> (item<span class="op">.</span><span class="at">itemType</span> <span class="op">===</span> Office<span class="op">.</span><span class="at">MailboxEnums</span><span class="op">.</span><span class="at">ItemType</span><span class="op">.</span><span class="at">Message</span>) <span class="op">&amp;&amp;</span> item<span class="op">.</span><span class="at">getAttachmentsAsync</span> <span class="op">!==</span> <span class="kw">undefined</span> </span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a>} </span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">doSomethingWithViewedEmail</span>(item<span class="op">:</span> Office<span class="op">.</span><span class="at">MessageRead</span>)<span class="op">:</span> <span class="kw">void</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">doSomethingWithComposedEmail</span>(item<span class="op">:</span> Office<span class="op">.</span><span class="at">MessageCompose</span>)<span class="op">:</span> <span class="kw">void</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">onlyEmailEntriesAreSupported</span>()<span class="op">:</span> <span class="kw">void</span></span></code></pre></div>
<p>(OK, checking <code>getAttachmentsAsync</code> is ugly, office.js could provide some nicer and more stable way to identify the exact <code>item</code> type. This is still not bad. Let’s move on.)</p>
<p><code>doSomethingWithViewedEmail</code> and <code>doSomethingWithComposedEmail</code> can now be coded with confidence (if I trust <em>office.js</em> types) following the corresponding <code>MessageRead</code> or <code>MessageCompose</code> types. IntelliSense makes writing these a breeze and the code is very clean. E.g., <code>subject</code> is just a <code>string</code> in <code>MessageRead</code>.</p>
<p>I can use these without any casting:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="co">//'unknown' replaces incorrect office.js type (see previous section). </span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="kw">const</span> item<span class="op">:</span> unknown <span class="op">=</span> Office<span class="op">.</span><span class="at">context</span><span class="op">?.</span><span class="at">mailbox</span><span class="op">?.</span><span class="at">item</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a><span class="cf">if</span>(<span class="fu">isMessageRead</span>(item)) {</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a>  <span class="co">//doSomethingWithComposedEmail(item) //this will not type check!</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a>  <span class="fu">doSomethingWithViewedEmail</span>(item)    </span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a>} <span class="cf">else</span> <span class="cf">if</span> (<span class="fu">isMessageCompose</span>(item)) {</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a>  <span class="co">//doSomethingWithViewedEmail(item) //this will not type check!</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a>  <span class="fu">doSomethingWithComposedEmail</span>(item)  </span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a>} <span class="cf">else</span> {   </span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true"></a>  <span class="fu">calendarEntriesAreNotSupported</span>()</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true"></a>}</span></code></pre></div>
<p>This is a really nice, bravo TypeScript! Simple to use, yet very useful.</p>
<p>It is also IMO a very interesting case of TS making a bigger impact on how we actually code. <em>“Check before you use”</em> game becomes type assisted and happens on a coarser scale of <code>item</code> types instead of single (e.g. the email <em>subject</em>, <em>from</em>, <em>cc</em>, etc.) properties.<br />
This adds a lot of clarity to the code. TS types not just check my code, types change how I code!</p>
<p><code>t is T</code> type is one of the TypeScript <a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html" target="_blank">narrowing</a> tools. The documentation refers to it as a <em>type predicate</em> or a <em>type guard</em> (a more general term).<br />
IMO, the idea of a middle ground between type checked safety and unsafe type coercion is brilliant.<br />
It is something that sits a half way between a cast and a type equality proof.<br />
This will probably influence other languages (e.g. here is <a href="https://www.python.org/dev/peps/pep-0647/" target="_blank">enhancement proposal for Python</a>).</p>
<p>The syntax <code>t is T</code> is interesting, it clearly borrows from dependently typed languages. The value <code>t</code> appears next to the type <code>T</code> and comes from the earlier part of the declaration. This also somewhat justifies the existence of otherwise cumbersome parameter names in type definitions (something I complained about in my <a href="2021-12-12-ts-types-part1.html#office.js.-using-ts-in-anger" target="_blank">previous post</a>).</p>
<p>I hope the TS community develops a healthy aversion to casting. Why would you use a type checker if you keep subverting it? I also hope that exporting functions returning type predicates will become a standard practice for APIs.</p>
<p> <div class="side-note"><strong>Use of <code>any</code> in type predicates</strong><br />
Arguably, a safer approach was to define <code>isMessageRead</code> and <code>isMessageCompose</code> using a parameter type that is more restrictive than <code>any</code>.<br />
My goal was to keep this example very simple and avoid introducing a <code>CorrectedOfficeItem</code> type to fix <em>office.js</em> typing. In real code, I would opt in for introducing the corrected type. Linked github repo defines and uses <code>CorrectedOfficeItem</code>.<br />
However, using <code>any</code> in type predicate implementations appears to be a common practice. Implementing a type predicate typically requires checking for existence of object properties and <code>any</code> provides access to these.<br />
My suggestion is to avoid type guards in certain places, e.g. in generics. We want generics to be generic.  </div></p>
<h2 id="note-about-the-unknown-type">Note about the <code>unknown</code> type</h2>
<p>This post started with a use of the unsafe <code>JSON.parse</code>. I am quite sure that if TypeScript could travel back in time <code>JSON.parse</code> would return <code>unknown</code> instead of <code>any</code>.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="im">export</span> <span class="kw">const</span> safeParseJSON <span class="op">:</span> (_<span class="op">:</span> string) <span class="kw">=&gt;</span> unknown <span class="op">=</span> <span class="bu">JSON</span><span class="op">.</span><span class="fu">parse</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a><span class="kw">const</span> isPerson <span class="op">=</span> (p<span class="op">:</span> any)<span class="op">:</span> p is Person <span class="kw">=&gt;</span> </span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>        <span class="kw">typeof</span> p<span class="op">.</span><span class="at">firstNm</span> <span class="op">===</span> <span class="st">'string'</span> <span class="op">&amp;&amp;</span> <span class="kw">typeof</span> p<span class="op">.</span><span class="at">lastNm</span> <span class="op">===</span> <span class="st">'string'</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a><span class="kw">const</span> possiblyPerson <span class="op">=</span> <span class="fu">safeParseJSON</span>(<span class="st">'&quot;John Smith&quot;'</span>) </span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a><span class="cf">if</span> (<span class="fu">isPerson</span>(possiblyPerson)) {</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a>    <span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(possiblyPerson<span class="op">.</span><span class="at">firstNm</span>)</span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a>} <span class="cf">else</span> {</span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a>    <span class="co">// console.log(possiblyPerson.firstNm) //does not compile</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a>}</span></code></pre></div>
<p><code>unknown</code> is a newer and a safer alternative to <code>any</code>.</p>
<p><code>unknown</code> type is (only) the top type (you can assign anything to it but you cannot assign it to anything else, maybe except for <code>any</code>). This is a much better safety than being both the <em>top</em> and the <em>bottom</em>. Compared to <code>any</code> it is more cumbersome to use but significantly safer.</p>
<p>Let’s criticize the <code>unknown</code> a bit. A rough view (IMO) of what type safety is: an ability to <em>separate apples from oranges</em>. If you can assign both an apple to <code>unknown</code> and an orange to <code>unknown</code> then they are no longer separated.<br />
What makes this worse in TS, is its occasional tendency to widen return types to <code>unknown</code>. TS tends to do that if it cannot find a more precise return type, when it tries to apply subtying rules to things like functions, or when it gets confused. We saw two examples of this in the last post:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="co">//compilation bug allows this incorrect code to compile with</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a><span class="co">// emailBody4: unknown</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a><span class="co">//this code will accutally work at runtime because 'crazyConfig' ends up not being used </span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a><span class="kw">const</span> crazyConfig <span class="op">:</span> (_<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span> <span class="op">=</span> x <span class="kw">=&gt;</span> <span class="st">&quot;&quot;</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a><span class="kw">const</span> emailBody4 <span class="op">=</span> <span class="cf">await</span> <span class="fu">officePromise</span> (<span class="fu">curry3</span>(item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="at">getAsync</span>)(Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span>)(crazyConfig)) </span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a><span class="co">//test: (a: unknown) =&gt; (b: unknown) =&gt; unknown</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a><span class="kw">const</span> test <span class="op">=</span> <span class="fu">curry</span>({} <span class="im">as</span> any)</span></code></pre></div>
<p>Also, notice <code>unknown</code> in some of the <a href="2021-12-12-ts-types-part1.html#compilation-bloopers" target="_blank">blooper</a> examples from the previous post:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="co">//these should not compile but they do. Names are consitent with previous post and the linked github repo</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a><span class="co">//const nonsense2: &lt;T1, T2, R&gt;(a: (ax: T1, bx: T2) =&gt; R) =&gt; (b: unknown) =&gt; (a: T1) =&gt; (b: T2) =&gt; R</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a><span class="kw">const</span> nonsense2 <span class="op">=</span> <span class="fu">curry</span>(curry) </span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a><span class="co">//const nonsense3: &lt;T1, T2, T3, R&gt;(a: (ax: T1, bx: T2, cx: T3) =&gt; R) =&gt; (b: unknown) =&gt; (a: T1) =&gt; (b: T2) =&gt; (c: T3) =&gt; R</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a><span class="kw">const</span> nonsense3 <span class="op">=</span> <span class="fu">curry</span>(curry3)</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a><span class="co">//const nonsense4: &lt;T1, T2, R&gt;(a: (ax: T1, bx: T2) =&gt; R) =&gt; (b: unknown) =&gt; (b: unknown) =&gt; (a: T1) =&gt; (b: T2) =&gt; R</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true"></a><span class="kw">const</span> nonsense4 <span class="op">=</span> <span class="fu">curry</span>(<span class="fu">curry</span>(curry))</span></code></pre></div>
<p>and we will encounter more examples of <em>unknown widening</em> in future notes. I would be happier if many of these examples resulted in a compilation error. Current status quo reduces safety of TS code.</p>
<p>Let’s look at how <code>unknown</code> makes things like <code>===</code> more complex. I really love the fact that this code (a contrived example but generalizes easily to real situations) does not compile:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="co">//Compilation error:</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a><span class="co">//This condition will always return 'false' since the types 'string' and 'number' have no overlap.</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a><span class="st">&quot;some email body&quot;</span> === <span class="dv">1</span> </span></code></pre></div>
<p>However, this does compile:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a>(<span class="st">&quot;some email body&quot;</span> <span class="im">as</span> unknown) <span class="op">===</span> <span class="dv">1</span></span></code></pre></div>
<p>and so does this:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a>emailBody4 <span class="op">===</span> <span class="dv">1</span></span></code></pre></div>
<p>Let’s bring in the type hole <a href="2021-12-12-ts-types-part1.html#type-holes" target="_blank"><code>_&lt;T&gt;(): T</code></a> from the last post. The type hole is a convenient way to ask the compiler type questions.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="co">//hovering over res and _ allows me to see the typing of '===`</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a><span class="kw">const</span> res <span class="op">=</span> <span class="fu">_</span>() <span class="op">===</span> <span class="fu">_</span>()</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a>(<span class="dv">1</span> <span class="im">as</span> <span class="dv">1</span>) <span class="op">===</span> <span class="fu">_</span>()</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a><span class="fu">_</span>() <span class="op">===</span> (<span class="dv">1</span> <span class="im">as</span> <span class="dv">1</span>)</span></code></pre></div>
<p>So the “imaginary” type signature of <code>===</code> is:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">eqeqeq</span>(a<span class="op">:</span> unknown<span class="op">,</span> b<span class="op">:</span> unknown)<span class="op">:</span> boolean</span></code></pre></div>
<p>Except</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="fu">eqeqeq</span>(<span class="st">&quot;some text&quot;</span><span class="op">,</span> <span class="dv">1</span>) <span class="co">//compiles</span></span></code></pre></div>
<div class="sourceCode" id="cb19"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="st">&quot;some text&quot;</span> <span class="op">===</span> <span class="dv">1</span> <span class="co">//does not compile</span></span></code></pre></div>
<p>In fact, <code>===</code> <em>does not have a type</em>. It is a built-in JS operator. TS applies semantic narrowing rules to the code that uses it.<br />
This complex approach is needed to provide type safety while maintaining compatibility with JS.<br />
TS’s semantic rules prevent certain types like <code>someText === someNumber</code> from compiling, except, this safety is somewhat fragile and breaks when <code>someText</code> or <code>someNumber</code> are accidentally widened to <code>unknown</code> by the type inference. TS uses a similar approach for other built-in JS operators. (We will discuss the crazy <code>===</code> semantics in a deeper detail in the next post.)</p>
<p><strong>General safety concerns about the top:</strong> Developers, like me, who had spent decades working in languages like Java and then switched to a typed FP language see immediate safety benefits just because there isn’t any top type. The concern about <code>unknown</code> is that it is used with many JS functions and operators. Such use is not type safe, similarly to how Java’s <code>Object</code> methods are not type safe.<br />
From the type safety point of view, these JS functions and operators are not implemented well either. Consider for example <code>JSON.stringify</code> which accepts <code>any</code>. Does this expression (it returns <code>undefined</code>) make much sense to you: <code>JSON.stringify(() =&gt; {})</code>?<br />
Generic functions lose safety too, generics are not <em>generic</em> if a generically typed function parameter can use a <em>specific</em> JS function (like the <code>JSON.stringify</code> function).</p>
<p>Something like <code>unknown</code> is probably the only way for TS to achieve JS compatibility, nonetheless <code>unknown</code> is not ideal.</p>
<p>I will come back to this discussion again, I plan to discuss the complexity of TS types. I will also return to the <code>unknown</code> type itself in the future in a more theoretical setting.</p>
<h2 id="honest-typing-conventions">Honest typing conventions</h2>
<p>These notes will be a little ranty (you’ll probably ask: “Did you read your other notes?”). Any coding convention is effectively a hand waving rant. That is why we use types, so we can rant less!</p>
<p>One of my former colleagues liked to use the phrase “gentlemen’s agreement”. It means an agreement between developers to self impose certain limitations on the code they write. These limitations are not enforced by the compiler, only by developers who agree to abide by the set rules. Coding guidelines, design patterns, you know what I am talking about.</p>
<p>There is a term in Programming Language Theory called <em>parametricity</em>. Roughly speaking, a language that supports <em>parametricity</em> can assure that a generic function cannot discover what is the type behind a type variable. Remove the top and the bottom from the language too. You are left with very precise types. As an example,</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true"></a>declare <span class="kw">function</span> someName<span class="op">&lt;</span>T<span class="op">&gt;</span>(t<span class="op">:</span>T)<span class="op">:</span> T</span></code></pre></div>
<p>could be only implemented as an identity. Incidentally, there are a few languages that support strict parametricity and a few that come very close, for mainstream languages, <em>parametricity</em> is an gentlemen’s agreement.</p>
<p>Can you write a whole single page app in TS and give it that signature? I bet you can.<br />
We would probably not call it a type-lie. Calling it not descriptive would probably be more accurate. Or, maybe just not the best design?<br />
If some type definitions are better than others, which of them are better? Apps are written so the decisions are being made, but based on what?</p>
<p>I will give you my very type centric view of programming:</p>
<ol type="1">
<li>Well written program means well typed. Well typed means the types express what is happening.<br />
</li>
<li>Types are more fundamental than a programming language.</li>
<li>Coding conventions supplement the language in implementing typing concepts.</li>
<li>TS (or any programming language) programming needs a balancing act. My approach for writing TS is to balance principled and safe with approachable and informative. That balance is subjective and project specific, my balance point may differ from yours.</li>
</ol>
<p><strong>Expanding on 2:</strong><br />
TS type checks my code, I type check TS (last post). A library (e.g. <em>office.js</em>) provides types, I type check these types and fix some of them (this post). Developer interventions are needed. Understanding of types does not change with a programming language environment. The cumbersomeness of their use does. TS is, comparatively speaking, not that bad.</p>
<p><strong>Expanding on 3:</strong><br />
In TS, almost any program can have almost any type. I can implement</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true"></a><span class="kw">function</span> <span class="fu">program</span>()<span class="op">:</span> <span class="kw">void</span> {<span class="op">...</span>}</span></code></pre></div>
<p>and do almost anything I want in that code.<br />
It would not be very clear if most of my types looked like this. There needs to be some coding convention that discourages such code.<br />
Enforcing some level of parametricity when implementing generics is another example of a coding convention.</p>
<p><em>The goal is to move from designing programs to designing types.</em><br />
<em>This post suggests that types are used to define coding conventions.</em></p>
<p>So, besides guarding parametricity, what else can we do? Here are some bootstrapping ideas:</p>
<h3 id="referential-transparency">Referential Transparency</h3>
<p>Referential Transparency is an FP topic but is also very relevant to types and crucially important to the discussion of “type honesty”.</p>
<p>A function is referentially transparent if it does the same thing every time it is called. Referential transparency comes with clear type signatures. The output needs to be a function of the inputs and of nothing else. You can do things like curry or partially apply, but you cannot say, retrieve the current time and act on it (that time parameter would need to be provided as input).<br />
For <code>program:() =&gt; void</code> to be referentially transparent would mean that the implementation does not do anything, just returns.<br />
IMO well written programs identify and separate the referentially transparent parts.</p>
<p>In TS, referential transparency is a coding convention. I will use <em>React.js</em> example to demonstrate this. Readers not familiar with React should think about creating a function from some model (<code>Person</code> in this example) to an actual part of the HTML DOM. Here is my example of a vanilla React component type (I like React to be vanilla as much as possible)</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true"></a><span class="kw">const</span> PersonCard<span class="op">:</span> ({ model<span class="op">,</span> onChange }<span class="op">:</span> {</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true"></a>    <span class="dt">model</span><span class="op">:</span> Person<span class="op">;</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true"></a>    <span class="dt">onChange</span><span class="op">:</span> (<span class="dt">_</span><span class="op">:</span> Person) <span class="kw">=&gt;</span> <span class="kw">void</span><span class="op">;</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true"></a>}) <span class="kw">=&gt;</span> JSX<span class="op">.</span><span class="at">Element</span></span></code></pre></div>
<p>Hopefully, the implementation does not use any hooks, it only uses the parameters (I call them setters<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> and getters) to create bits of HTML with event handlers. This would be an example of a referentially transparent React type. It also would be an example of a very explicit type that is very “honest”.</p>
<p> <div class="side-note"><strong>FP side notes:</strong> Such approach is not novel at all (e.g. Elm uses a similar approach, only not as a coding style but as its architecture).<br />
 </div></p>
<p>Many developers will very much disagree with me on this. E.g. many will prefer to encapsulate state handling inside components. I do not intend to argue which approach is better. I will just point out that encapsulation is secretive in the type definition and I am looking for transparency here. Many parts of React code will require some use of hooks, my approach is to do that only when I have to and to keep the hooks outside of my main components. It is not about not using hooks, it is about not having them all over the code base. The goal is to make things very type-explicit. It is an IMO.</p>
<p>Such type is also self documenting.</p>
<p><strong>Expanding on my point 4:</strong> IMO, the best communication tools for developers and the best documenting tools for the code, in that order, are: <em>types and tests</em>. I will only focus on the first.</p>
<h3 id="types-as-documentation">Types as documentation</h3>
<p>When I write TS, I want my types to be very informative. For example, compare these two slightly modified versions of the above React component:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true"></a><span class="kw">const</span> PersonCard<span class="op">:</span> React<span class="op">.</span><span class="at">FC</span><span class="op">&lt;</span>{</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true"></a>    <span class="dt">model</span><span class="op">:</span> Person<span class="op">;</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true"></a>    <span class="dt">onChange</span><span class="op">:</span> (<span class="dt">_</span><span class="op">:</span> Person) <span class="kw">=&gt;</span> <span class="kw">void</span><span class="op">;</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true"></a>}<span class="op">&gt;</span></span></code></pre></div>
<p>vs:</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true"></a><span class="kw">const</span> PersonCard<span class="op">:</span> React<span class="op">.</span><span class="at">FC</span><span class="op">&lt;</span>Props<span class="op">&gt;</span> <span class="co">//Commonly used 'Props' type alias defined next to 'PersonCard'</span></span></code></pre></div>
<p>I like the first one better.<br />
And, I am not suggesting the names for the setters and getters here. I would be equally happy with this:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true"></a><span class="kw">const</span> PersonCard<span class="op">:</span> React<span class="op">.</span><span class="at">FC</span><span class="op">&lt;</span>{</span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true"></a>    <span class="dt">get</span><span class="op">:</span> Person<span class="op">;</span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true"></a>    <span class="dt">set</span><span class="op">:</span> (<span class="dt">_</span><span class="op">:</span> Person) <span class="kw">=&gt;</span> <span class="kw">void</span><span class="op">;</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true"></a>}<span class="op">&gt;</span></span></code></pre></div>
<p>There is no safety benefit in doing this. Communication, documentation and accessibility are the only goals.<br />
I like to think about a modernized definition of the KISS principle: “Simple” is a lot of very transparent types.</p>
<h2 id="next-chapter">Next Chapter</h2>
<p>There are parts of TS that I absolutely adore and I will talk about them. The complexity of TS types is another big topic to discuss. Complexity causes compilation issues (we will encounter some new bloopers) and makes the language hard to use.</p>
<p>Here is the link: <a href="2022-01-03-ts-types-part3.html" target="_blank">Part 3</a>.</p>
<p>I am working on these notes during the 2021 holiday season. <em>Merry Christmas, Happy New Year!</em> Stay happy and healthy!</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>What I am achieving here is not the full referential transparency. Setters are not referentially transparent, referentially transparent function that returns <code>void</code> cannot do anything. However, This construction is still very explicit.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

    </section>
</article>
]]></summary>
</entry>
<entry>
    <title>Type Enthusiast's Notes about TypeScript. Part 1. Typing in Anger</title>
    <link href="https://rpeszek.github.io//posts/2021-12-12-ts-types-part1.html" />
    <id>https://rpeszek.github.io//posts/2021-12-12-ts-types-part1.html</id>
    <published>2021-12-12T00:00:00Z</published>
    <updated>2021-12-12T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article>
    <section class="header">
        Posted on December 12, 2021
        
            by Robert Peszek
        
        
        
        <div class="changelog"> <div>Revision History: <ul> <li> (2021.12.24) modified historical note about office.js. Linked Part 2. Planned future content adjustment.</li> <li> (2021.12.26) <a href="#fn1">footnote [1]</a> <li> (2022.01.03 - 2022.05.29) Changes are documented in <a href="#summary-of-final-edits">Summary of final edits</a>. <li> (2022.05.29) Draft warning removed </li> <li> (2022.08.30) added <a href="../tags/patterns-of-erroneous-code.html">patterns-of-erroneous-code</a> tag</li> </ul> </div></div>
        
        
        <div class="info">Tags: <a title="All pages tagged 'TypeScript-Notes'." href="../tags/TypeScript-Notes.html">TypeScript-Notes</a>, <a title="All pages tagged 'patterns-of-erroneous-code'." href="../tags/patterns-of-erroneous-code.html">patterns-of-erroneous-code</a></div>
        
    </section>
    <section>

    </section>
    <section>
        <div class="toc"><div class="header">Table of Contents</div>
<ul>
<li><a href="#introduction-to-the-series">Introduction to the series</a></li>
<li><a href="#typescript-is-great">TypeScript is great!</a></li>
<li><a href="#office.js.-using-ts-in-anger"><em>office.js</em>. Using TS in anger</a>
<ul>
<li><a href="#happy-path">Happy path</a></li>
<li><a href="#bumps-on-the-path">Bumps on the path</a></li>
<li><a href="#bump-leveling-tools">Bump leveling tools</a></li>
<li><a href="#compilation-bloopers">Compilation bloopers</a></li>
<li><a href="#its-all-worth-it">It’s all worth it</a></li>
</ul></li>
<li><a href="#relevant-typescript-language-tickets">Relevant TypeScript Language tickets</a></li>
<li><a href="#next-chapter">Next Chapter</a></li>
<li><a href="#summary-of-final-edits">Summary of final edits</a></li>
</ul>
</div>
<p><em>Please Leave Feedback in: <a href="https://github.com/rpeszek/rpeszek.github.io/discussions/1" target="_blank">git discussions</a></em></p>
<p><strong>Disclaimers:</strong> (imagine this is a very small font, read it very fast in a half-whisper)<br />
<em>I assume strict compiler flags are on, something you get by default with scaffolding, e.g. using <code>create-react-app my-project --template typescript</code> is close enough.<br />
The code examples have been tested with TypeScript v4.4.4, v4.5.2, and v4.6.3.<br />
office.js examples are based on <a href="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js" target="_blank">https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js</a> and <span class="citation" data-cites="types/office-js">@types/office-js</span><span class="citation" data-cites="1.0.221">@1.0.221</span> (these match the current scaffold for office.js/React).<br />
This post is a pandoc conversion of markdown document and code examples are not interactive.<br />
Most of the code examples are published in <a href="https://github.com/rpeszek/ts-experiments/tree/master/ts-notes" target="_blank">ts-notes</a> folder in this github repo: <a href="https://github.com/rpeszek/ts-experiments" target="_blank">ts-experiments</a>.</em></p>
<h2 id="introduction-to-the-series">Introduction to the series</h2>
<blockquote>
<p>“TypeScript began its life as an attempt to bring traditional object-oriented types to JavaScript so that the programmers at Microsoft could bring traditional object-oriented programs to the web. As it has developed, TypeScript’s type system has evolved to model code written by native JavaScripters. The resulting system is <em>powerful, interesting and messy.</em>”</p>
</blockquote>
<p><em>From typescriptlang <a href="https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html" target="_blank">TypeScript for Functional Programmers</a></em></p>
<p>I wanted to write a short post about my experience with TS types, I ended up with a draft the size of a short book. I decided to split it into digestible installments and publish it as a series of shorter posts. The series will be about the <em>powerful, interesting and messy</em> types in TS. This post is the first in that series.</p>
<p>Here is my plan:</p>
<ul>
<li>Part 1 (this post). Is a warm-up. Part 1 has been motivated by a project at my work that uses TS. I will show code examples that are hard to compile. I will discuss strategies and methods for resolving compilation issues. I will present code examples that compile but really, really should not, and code examples that should compile but surprisingly don’t. I will also summarize my overall experience of working with TS.<br />
This series needed a JS library with TS bindings to draw examples from, I decided to use <em>office.js</em> and Part 1 introduces it.</li>
<li><a href="2021-12-24-ts-types-part2.html" target="_blank">Part 2</a>. Will be about keeping types honest. Are runtime values consistent with the types? We hope they always are but, especially in a gradually typed language like TS, types will sometimes lie. We will see concrete examples of type dishonesty from <em>office.js</em>. Part 2 will cover the notorious <code>any</code> and its safer cousin <code>unknown</code>, the type coercion (casting), and TS’s type guards. I will also discuss (or rather rant about) coding conventions for transparent, self documenting types.</li>
<li><a href="2022-01-03-ts-types-part3.html" target="_blank">Part 3</a>. Will cover some of the TS type safety features that I absolutely love. Throughout the series, we will encounter several examples where TS compiler does not work as expected. This part will discuss questionable (and arguably incorrect) semantics of subtyping variance and of narrowing. It will argue that what TS is and does it quite complex. Complexity is the likely cause of errors in TS programs and in the language itself.</li>
<li><a href="2022-01-09-ts-types-part4.html" target="_blank">Part 4</a>, <a href="2022-02-13-ts-types-part5.html" target="_blank">Part 5</a>. Will be more theoretical. Notes in Parts 4-5 will discuss topics such as TS’s structural, recursive types, subtyping, phantom types, type variable scoping, higher-rank polymorphism (TS supports a version of it!), and type level programming. I will show a trick to increase type safety that prevents widening to <code>unknown</code> or other supertypes.<br />
</li>
<li><a href="2022-03-13-ts-types-part6.html" target="_blank">Part 6</a>. Will be a wrap-up with some final thoughts.</li>
</ul>
<p><strong>Why am I writing these notes?</strong><br />
To be honest, it is because I am really impressed and excited about some of the type safety features in TS.</p>
<p>Despite being a superset of JavaScript, TS stands out among mainstream languages as one that supports some interesting types.<br />
There exist a tiny but important feedback loop: the more developers play with types the more they will end up being used.<br />
So, to be perfectly honest, the goal of these notes is to simply play with some interesting types and see how the compiler reacts.</p>
<p>IMO, to master something is to understand its limitations.<br />
So, to be brutally honest, the goal of these notes is to explore the TS compiler limitations.</p>
<p><strong>Target audience and prerequisites.</strong> I assume that the reader is interested in types and either uses or considers using TypeScript.<br />
Types tend to be related to FP. There will not be much FP in these notes. However, I will use some basic functional programming concepts, like currying, without explaining them.<br />
TypeScript is a superset of JavaScript with type syntax very similar to any other C-like language. These notes will probably be hard to read without some experience with JavaScript or ability to read C-like types.</p>
<p><strong>About the author.</strong> I am spearheading a rewrite of a legacy frontend component at work, the goal is to rewrite it using the new React.js and TypeScript. In recent years I have been spending all of my time in the backend designing, writing, and maintaining Haskell programs. Haskell code has a lot of types. Thus, I use types a lot. Types allow me to code faster, safer, and with much more confidence.<br />
I wear a hat with types on it when writing TS.<br />
I love Programming Language Theory and have some experience and lots of interest in compiler and language design.<br />
I wear a very thin headband embroidered with PLT symbols under my hat (should be mostly invisible in this series).<br />
All of this gives me a different (compared to most typescripters) perspective and a reason to write these posts. For some readers, parts of these posts will feel strange. Established practices like overloading will be considered a bad thing, writing experimental code (that won’t even run) to answer <em>type questions</em> will be a good thing. Strange is a corollary of different.</p>
<p><strong>What is TypeScript for?</strong> Is it just a JavaScript add-on used to prevent typos and trivial code errors?<br />
Or, will TypeScript more fundamentally change the way the code is written?<br />
Please have these questions in mind when reading these notes.</p>
<p>We will cover a lot of topics.</p>
<blockquote>
<p>“And we never say anything unless it is worth taking a long time to say.”</p>
</blockquote>
<p><em>J.R.R Tolkien and Treebeard about discussing types in TypeScript</em></p>
<h2 id="typescript-is-great">TypeScript is great!</h2>
<p>It literally took me less than one minute of playing with TS to get excited about it.<br />
Just look at the union types (using a somewhat contrived example):</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a>type Person <span class="op">=</span> {<span class="dt">firstNm</span><span class="op">:</span> string<span class="op">,</span> <span class="dt">lastNm</span><span class="op">:</span> string} </span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a>type NullablePerson <span class="op">=</span> Person <span class="op">|</span> <span class="kw">null</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="kw">const</span> getName <span class="op">=</span> (p<span class="op">:</span>NullablePerson)<span class="op">:</span> string <span class="kw">=&gt;</span> {</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a>    <span class="co">//const tst1 = p.firstNm //does not compile</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a>    <span class="cf">if</span>(p<span class="op">===</span><span class="kw">null</span>){</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a>        <span class="co">//const tst2 = p.firstNm //does not compile</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a>        <span class="cf">return</span> <span class="st">&quot;John Smith&quot;</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a>    } <span class="cf">else</span> {</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true"></a>        <span class="cf">return</span> p<span class="op">.</span><span class="at">firstNm</span> <span class="op">+</span> <span class="st">&quot; &quot;</span> <span class="op">+</span> p<span class="op">.</span><span class="at">lastNm</span> <span class="co">//compiles</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true"></a>    }</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true"></a>}</span></code></pre></div>
<p>How cool!</p>
<p>Talking about my “literal” excitement, my next play example implements <code>Either</code> (I am not trying to implement my own Either type, only to play with the language):</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a>type Either<span class="op">&lt;</span>A<span class="op">,</span>B<span class="op">&gt;</span> <span class="op">=</span> </span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;left&quot;</span><span class="op">,</span> <span class="dt">content</span><span class="op">:</span> A}</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;right&quot;</span><span class="op">,</span> <span class="dt">content</span><span class="op">:</span> B}</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a><span class="kw">const</span> x1<span class="op">:</span> Either<span class="op">&lt;</span>number<span class="op">,</span> string<span class="op">&gt;</span> <span class="op">=</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;left&quot;</span><span class="op">,</span> <span class="dt">content</span><span class="op">:</span> <span class="dv">1</span>}</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a><span class="kw">const</span> xone<span class="op">:</span> Either<span class="op">&lt;</span>number<span class="op">,</span> string<span class="op">&gt;</span> <span class="op">=</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;right&quot;</span><span class="op">,</span> <span class="dt">content</span><span class="op">:</span> <span class="st">&quot;one&quot;</span>}</span></code></pre></div>
<div class="sourceCode" id="cb3"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="dt">const</span> wrong: Either&lt;number, string&gt; = {type: <span class="st">&quot;left&quot;</span>, content: <span class="st">&quot;one&quot;</span>} <span class="co">// does not compile</span></span></code></pre></div>
<p>it almost looks like dependent types! TS calls these literal types. (In this example, <code>"left"</code> is a type with a single value <code>"left": "left"</code>.)<br />
TypeScript calls this programming pattern <em>Discriminated Unions</em>.</p>
<p>And, TS is serious about string property names too:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="kw">const</span> y<span class="op">:</span> Either<span class="op">&lt;</span>number<span class="op">,</span> string<span class="op">&gt;</span> <span class="op">=</span> {<span class="st">&quot;type&quot;</span><span class="op">:</span> <span class="st">&quot;left&quot;</span><span class="op">,</span> <span class="st">&quot;content&quot;</span><span class="op">:</span> <span class="dv">1</span>}</span></code></pre></div>
<div class="sourceCode" id="cb5"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="dt">const</span> wrong: Either&lt;number, string&gt; = {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;left&quot;</span>, <span class="st">&quot;content&quot;</span>: <span class="st">&quot;one&quot;</span>} <span class="co">// does not compile</span></span></code></pre></div>
<p>TypeScript <a href="https://www.npmjs.com/package/ts-pattern" target="_blank"><em>ts-pattern</em></a> library uses discriminated unions to implement <em>pattern matching</em>. Exhaustive check is part of it.<br />
Again, really cool. All of these are really exciting developments to me.</p>
<p>Continuing with play examples, here is the full JSON grammar defined in TS.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a>type JsonVal <span class="op">=</span> </span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;object&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> <span class="bu">Map</span><span class="op">&lt;</span>string<span class="op">,</span> JsonVal<span class="op">&gt;</span>}</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;array&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> JsonVal[]}</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;string&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> string}</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;number&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> number}</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;bool&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> boolean}</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a><span class="op">|</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;null&quot;</span>}</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a><span class="kw">const</span> tstj<span class="op">:</span> JsonVal <span class="op">=</span> {<span class="dt">type</span><span class="op">:</span><span class="st">&quot;array&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span>[{<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;null&quot;</span>}<span class="op">,</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;number&quot;</span><span class="op">,</span> <span class="dt">val</span><span class="op">:</span> <span class="dv">5</span>}]} <span class="co">//compiles</span></span></code></pre></div>
<div class="sourceCode" id="cb7"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="dt">const</span> wrong: JsonVal = {type: <span class="st">&quot;number&quot;</span>, val: {type: <span class="st">&quot;string&quot;</span>, val: <span class="st">&quot;5&quot;</span>}} <span class="co">//does not compile, number is not JSON object</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="dt">const</span> wrong2: {type: <span class="st">&quot;object&quot;</span>,  val:[{type: <span class="st">&quot;null&quot;</span>}, {type: <span class="st">&quot;number&quot;</span>, val: <span class="dv">5</span>}]} <span class="co">//does not compile, object is not an array</span></span></code></pre></div>
<p>This could have been expressed with OO classes, but it would not be very easy, would it?<br />
I wrote the <code>JsonVal</code> definition without thinking, I have committed <code>Data.Aeson.Value</code> (Haskell’s commonly used type for JSON values) definition to memory and I just mimicked it. Then I looked at it again … holly … TS supports complex recursive definitions! We will discuss recursive types later in this series.</p>
<p>TypeScript has an ability to do type level programming that goes beyond the demonstrated uses of literal types. All of this is oriented toward creating type safety over various kinds of idiomatic JS code and is limited in scope. It is nonetheless interesting. We will return to this topic in the future as well.</p>
<p>As far as mainstream languages go (I consider <em>Scala</em>, <em>Rust</em>, or <em>Reason</em> a border line just outside the mainstream), TypeScript could be the most interesting choice today IMO.</p>
<p>This was my trailer/preview section. If the code that excited me feels interesting to you, you may enjoy reading these notes. There will be some gory details (not a lot violence). You have to decide if type safety is your genre.<br />
Developers are divided into 2 camps: Those who use types because that is the most effective way to write software and those who do not use types because that is the most effective way to write software. Since you are still reading, I assume you are in camp 1.</p>
<h2 id="office.js.-using-ts-in-anger"><em>office.js</em>. Using TS in anger</h2>
<p>I will use <em>office.js</em> library as a source of examples for this series. It is a Microsoft product (like TypeScript). It comes with TypeScript type definitions (this series uses <span class="citation" data-cites="types/office-js">@types/office-js</span><span class="citation" data-cites="1.0.221">@1.0.221</span>).<br />
Looking into the <em>office.js</em> revision history suggests that the bond between <em>office.js</em> and TypeScript developed very early. It almost looks like these projects grew up together. <em>office.js</em> seems like a good ‘comprehensive’ example for examining the benefits (and frustrations) of using TS in anger.<br />
Despite some hardships, TS makes working with office.js much, much easier!</p>
<p>As the name suggests, <em>office.js</em> provides an API for working with <em>Microsoft Office</em>. It allows implementing custom apps that work inside the office suite of products (Microsoft calls these apps add-ins).<br />
This is not an office.js tutorial but, I hope, the code should be clear to follow even if you never used <em>office.js</em>.</p>
<p>As a working example, we will play with code that extracts data from an email opened in Outlook. To start, I want to extract the email body.<br />
To access data, <em>office.js</em> often uses an old style <code>getAsync</code> methods that I will modernize using a custom conversion to a <code>Promise</code>. Node’s <code>util.promisify</code> will not work well for this task. This is how this could be done in TS:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="co">/* Utility to convert office functions to promises */</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="im">export</span> <span class="kw">const</span> officePromise <span class="op">=</span> <span class="op">&lt;</span>T<span class="op">&gt;</span> (getasync<span class="op">:</span> (fx<span class="op">:</span> (r<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>T<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>)<span class="op">:</span> <span class="bu">Promise</span><span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="kw">=&gt;</span> {</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a>    <span class="cf">return</span> <span class="kw">new</span> <span class="bu">Promise</span>((resolve<span class="op">,</span> reject) <span class="kw">=&gt;</span> {</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>      <span class="fu">getasync</span>((<span class="dt">res</span><span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>T<span class="op">&gt;</span>) <span class="kw">=&gt;</span> {</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>        <span class="cf">if</span>(res<span class="op">.</span><span class="at">status</span><span class="op">===</span>Office<span class="op">.</span><span class="at">AsyncResultStatus</span><span class="op">.</span><span class="at">Succeeded</span>){</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a>          <span class="fu">resolve</span>(res<span class="op">.</span><span class="at">value</span>)</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a>      } <span class="cf">else</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a>          <span class="fu">reject</span>(res<span class="op">.</span><span class="at">error</span>)</span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a>      })</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a>   })</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a>  }</span></code></pre></div>
<p> <div class="side-note"><strong>Side Note</strong><a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>: Here is my first criticism of TS. The ergonomics of function type definitions is IMO really poor. These definitions are hard to read and cumbersome to write. This syntax does not scale well to more involved types and makes reasoning about types harder.<br />
E.g. in the above example parameters <code>fx:</code> and <code>r:</code> cannot be used anywhere (are outside of the lexical scope) and serve only a documentation purpose. This simple example needs 6 parentheses. The use of <code>:</code> and <code>=&gt;</code> is confusing. Function form <code>A</code> to <code>B</code> is (depending where in the declaration) either <code>(a: A) =&gt; B</code> or <code>(a: A): B</code>. I admit it took me a long time to figure out how to write these and it still takes me forever to read some of these types.<br />
Later in this post, I will show some work-arounds that simplify type definitions like this one.<br />
I am adding a big fat <strong>IMO</strong> to this side note, readability is in the eye of … well the reader. But seriously…<br />
 </div></p>
<p>Properly initialized office add-in will have access to <code>Office.context.mailbox.item: Office.MessageRead</code>.<br />
This <code>item</code> object allows access to the email data.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> To retrieve the email body I need to use <code>item.body.getAsync</code>. But wait, the type for that version of <code>getAsync</code> accepts not only a callback function but also a “body type” parameter.</p>
<p>I am going to resist the temptation to overload <code>officePromise</code>. Instead I will move in a direction that is more fundamental.</p>
<p>Assume that we want ‘html’ body format, the code can look something like this:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="co">//retrieving email body, 1st attempt</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="kw">const</span> bodyType <span class="op">=</span> Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a> </span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a><span class="kw">const</span> partiallyAppliedBodyFn <span class="op">=</span> (fn<span class="op">:</span> ((res<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>)) <span class="kw">=&gt;</span> </span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a>     item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="fu">getAsync</span>(bodyType<span class="op">,</span> fn) </span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a>  </span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a><span class="kw">const</span> body  <span class="op">=</span> <span class="cf">await</span> officePromise<span class="op">&lt;</span>string<span class="op">&gt;</span> (partiallyAppliedBodyFn) <span class="co">// body: string</span></span></code></pre></div>
<p>I had to fully specify the <code>partiallyAppliedBodyFn</code> type for this to work. That looks like a lot of code to just partially apply <code>item.body.getAsync</code>!</p>
<h3 id="happy-path">Happy path</h3>
<p>There are some libraries that offer a <code>curry</code> function conversion, but these are typically JS not TS. So I wrote it myself (again, note the type signature is somewhat hard to read):</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="im">export</span> <span class="kw">const</span> curry <span class="op">=</span> <span class="op">&lt;</span>T1<span class="op">,</span> T2<span class="op">,</span> R<span class="op">&gt;</span> (fn<span class="op">:</span> (ax<span class="op">:</span> T1<span class="op">,</span> bx<span class="op">:</span> T2) <span class="kw">=&gt;</span> R)<span class="op">:</span> (a<span class="op">:</span> T1) <span class="kw">=&gt;</span> (b<span class="op">:</span> T2) <span class="kw">=&gt;</span> R <span class="kw">=&gt;</span> {</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a>    <span class="kw">const</span> res <span class="op">=</span> (<span class="dt">a</span><span class="op">:</span> T1) <span class="kw">=&gt;</span> (<span class="dt">b</span><span class="op">:</span> T2) <span class="kw">=&gt;</span> <span class="fu">fn</span>(a<span class="op">,</span> b)</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a>    <span class="cf">return</span> res</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a> }</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a><span class="kw">const</span> addtst <span class="op">=</span> (a<span class="op">:</span>number<span class="op">,</span> b<span class="op">:</span> number) <span class="kw">=&gt;</span> a <span class="op">+</span> b</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a><span class="kw">const</span> curriedAdd <span class="op">=</span> <span class="fu">curry</span>(addtst) <span class="co">//const curriedAdd: (a: number) =&gt; (b: number) =&gt; number</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a><span class="kw">const</span> tst1 <span class="op">=</span> <span class="fu">curry</span>(addtst)(<span class="dv">1</span>) <span class="co">//const tst1: (b: number) =&gt; number</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a><span class="kw">const</span> tst12 <span class="op">=</span> <span class="fu">curry</span>(addtst)(<span class="dv">1</span>)(<span class="dv">2</span>) <span class="co">//tst12 = 3</span></span></code></pre></div>
<p>And I have a much simpler code that compiles right off the bat:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="co">//Happy path one liner to get email body</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a><span class="co">//body2: string</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a><span class="kw">const</span> body2 <span class="op">=</span> <span class="cf">await</span> <span class="fu">officePromise</span> (<span class="fu">curry</span>(item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="at">getAsync</span>)(Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span>)) </span></code></pre></div>
<p>This worked out quite well and the type checker was able to infer the types!<br />
This ended up being a happy path.</p>
<h3 id="bumps-on-the-path">Bumps on the path</h3>
<p>In practice, the type checker will often need some help. Even more often, the programmer (me) will need help figuring why the code is not compiling.</p>
<p>I will start by presenting code that should compile but it does not.</p>
<p><code>item.body.getAsync</code> offers a 3 parameter overload which accepts additional <code>Office.AsyncContextOptions</code>. Using it is much harder. (I will not delve into what the extra argument is for, I just want to see if my code will compile with 3 parameters)</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="co">//boilerplate 'curry3' implementation is not shown (available in the linked github project), </span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="co">//it is almost identical to `curry` but accepts a 3 parameter function  </span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a><span class="co">//trying to pass extra parameter to body.getAsync</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a><span class="kw">const</span> emptyConfig<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncContextOptions</span> <span class="op">=</span> {}</span></code></pre></div>
<div class="sourceCode" id="cb13"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="co">//Compilation Error: </span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a><span class="co">//&quot;Argument of type 'AsyncContextOptions' is not assignable to parameter of type '(asyncResult: AsyncResult&lt;string&gt;) =&gt; void'.&quot; </span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a><span class="dt">const</span> body3  = await <span class="fu">officePromise</span> (<span class="fu">curry3</span>(item.<span class="fu">body</span>.<span class="fu">getAsync</span>)(Office.<span class="fu">CoercionType</span>.<span class="fu">Html</span>)(emptyConfig)) </span></code></pre></div>
<p>To understand what is happening, I sometimes need to spend time annotating things, or picking up the exact overload I want. E.g.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="kw">const</span> useThisAsync <span class="op">=</span> (coercionType<span class="op">:</span> Office<span class="op">.</span><span class="at">CoercionType</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a>                     <span class="op">,</span> options<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncContextOptions</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a>                     <span class="op">,</span> callback<span class="op">:</span> (asyncResult<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>)<span class="op">:</span> <span class="kw">void</span> <span class="kw">=&gt;</span> {</span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a>      item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="fu">getAsync</span>(coercionType<span class="op">,</span> options<span class="op">,</span> callback)</span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a>    }</span></code></pre></div>
<p>This can be tedious, but it typically gets the job done. In this particular case, using <code>curry3(useThisAsync)</code> fixes the <code>body3</code> (or the “3 body”, I just had to pun this) problem. So, the issue with <code>body3</code> code appears to be related to overloading.</p>
<p>Looking closer at the types, I notice that not only <code>item.body.getAsync</code> has two overloads, but the one I want is accepting a union type argument and the callback is optional:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="co">//from office.js documentation</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a><span class="co">//2 parameter overload used in happy path</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a><span class="fu">getAsync</span>(coercionType<span class="op">:</span> Office<span class="op">.</span><span class="at">CoercionType</span> <span class="op">|</span> string</span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a>  <span class="op">,</span> callback<span class="op">?:</span> (asyncResult<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>)<span class="op">:</span> <span class="kw">void</span><span class="op">;</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a><span class="co">//3 parameter overload we are trying to use now</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a><span class="fu">getAsync</span>(coercionType<span class="op">:</span> Office<span class="op">.</span><span class="at">CoercionType</span> <span class="op">|</span> string<span class="op">,</span> </span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a>        options<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncContextOptions</span><span class="op">,</span> </span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></a>        callback<span class="op">?:</span> (asyncResult<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>)<span class="op">:</span> <span class="kw">void</span><span class="op">;</span></span></code></pre></div>
<p>So there are sort of <em>overloads on top of overloads</em> and the type checker could get confused. In fact the case here is much simpler, TS inference tends to pick the last defined overload (see <a href="https://github.com/rpeszek/typescript-issues/blob/master/src/RejectingCorrectCode/RejectingOverloads.ts" target="_blank">this code example</a> and (<a href="https://github.com/microsoft/TypeScript/issues/43187" target="_blank">#43187</a>). The compilation error also suggests that the compiler gets stuck on a wrong (the 2 parameter) version of <code>getAsync</code> despite the use of the 3 parameter <code>curry3</code>. I will also confirm this hypothesis using a <em>type hole</em> (we will learn what that is) in the next section.<br />
I expect the type checker to backtrack and try the next overload, but for some reason it does not want to do that on its own.<br />
I do not blame TS, overloading gives me a headache too.<br />
Overloading is known for being not type inference friendly (incidentally, that is the reason why Haskell does not overload names).</p>
<p>There is something worryingly asymmetric about a 2 parameter overload compiling without additional help and a 3 parameter overload needing a developer intervention. Should I worry<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> that the 2 parameter overload will stop compiling in the future? How stable is this arbitrary complexity?</p>
<p><em>If you are an API owner, my advice is to not overload. IntelliSense works better, type inference works better, developer head hurts less without overloads.</em></p>
<p>One type that is notorious for needing annotations is the TypeScript’s <em>tuple</em>. Typescript overloads array syntax <code>[]</code> to define tuples (some readers may prefer the term heterogeneous lists). This is an example of a tuple: <code>[2,"two"]: [number, string]</code>. The syntax overloading probably does not help TS in inferring the type and the type checker often gives up or infers the array type.</p>
<p>I am concerned that <strong>many developers will give up</strong> trying to write this type of code. My concern is also that developers will resort to unsafe type coercion / type casting. There will be a lot of <code>myvar as IWantIt</code>, or a lot of the <code>any</code> type.</p>
<p> <div class="side-note"><strong>Side note:</strong> I can push this code to a ridiculous limit and <strong>demonstrate the first example of code compiles but I would not expect it to</strong>:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="co">//this compiles by using a wrong input parameter type and returns 'body4: unknown'</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a><span class="kw">const</span> crazyConfig <span class="op">:</span> (_<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span> <span class="op">=</span> x <span class="kw">=&gt;</span> <span class="st">&quot;&quot;</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a><span class="kw">const</span> body4 <span class="op">=</span> <span class="cf">await</span> <span class="fu">officePromise</span> (<span class="fu">curry3</span>(item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="at">getAsync</span>)(Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span>)(crazyConfig)) </span></code></pre></div>
<p>Accepting invalid <code>unknown</code> appears to be a common pattern to how TS sometimes works. We will come back to this example later in this post and we will discuss the <code>unknown</code> problem more in future notes.<br />
 </div></p>
<p>Was this enough gore for you? You say it was not? I say you did not see the content of that email!</p>
<h3 id="bump-leveling-tools">Bump leveling tools</h3>
<h4 id="readable-type-definitions">Readable Type Definitions</h4>
<p>Cumbersome type annotations are not a good excuse to give up! There is a way to simplify function type definitions. For example, I can define a helper alias:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a><span class="co">//DIY reusable type for Office getAsync callbacks</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true"></a><span class="im">export</span> type OfficeCallack<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">=</span> (_<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>T<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span></span></code></pre></div>
<p>Here is how this simplifies the previously defined <code>partiallyAppliedBodyFn</code>:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="co">//before:</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true"></a><span class="kw">const</span> partiallyAppliedBodyFn1 <span class="op">=</span> (fn<span class="op">:</span> ((res<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>)) <span class="kw">=&gt;</span> item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="fu">getAsync</span>(Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span><span class="op">,</span> fn) </span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true"></a><span class="co">//after:</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true"></a><span class="kw">const</span> partiallyAppliedBodyFn2 <span class="op">=</span> (fn<span class="op">:</span> OfficeCallack<span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="fu">getAsync</span>(Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span><span class="op">,</span> fn)</span></code></pre></div>
<p>Notice <strong>no more redundant parameter definitions</strong> in the type signature and a much easier to read syntax.<br />
The next version is <strong>my personal preference</strong> (it nicely separates the type and the implementation)<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="kw">const</span> partiallyAppliedBodyFn3<span class="op">:</span> (_<span class="op">:</span> OfficeCallack<span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span> <span class="op">=</span> </span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true"></a>  fn <span class="kw">=&gt;</span> item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="fu">getAsync</span>(Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span><span class="op">,</span> fn)</span></code></pre></div>
<h4 id="type-application">Type Application</h4>
<p>Returning to my failed <code>body3</code> example, instead of trying to type annotate with full type signatures, it is sometimes more convenient to apply the types. Here, I have the “generic” (or polymorphic) <code>curry3</code> function that I can apply the types <code>CoercionType</code>, <code>AsyncContextOptions</code>, <code>OfficeCallack&lt;string&gt;</code>, and <code>void</code> to:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true"></a><span class="co">//type applied version, it just compiles!</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true"></a><span class="kw">const</span> emptyConfig<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncContextOptions</span> <span class="op">=</span> {}</span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true"></a><span class="kw">const</span> body3  <span class="op">=</span> <span class="cf">await</span> officePromise<span class="op">&lt;</span>string<span class="op">&gt;</span> (</span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true"></a>  curry3<span class="op">&lt;</span>Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">,</span> Office<span class="op">.</span><span class="at">AsyncContextOptions</span><span class="op">,</span> OfficeCallack<span class="op">&lt;</span>string<span class="op">&gt;,</span> <span class="kw">void</span><span class="op">&gt;</span> <span class="co">//explicity specified type parameters</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true"></a>     (item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="at">getAsync</span>)</span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true"></a>     (Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span>)</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true"></a>     (emptyConfig)</span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true"></a>  ) </span></code></pre></div>
<p>That is so much easier than specifying the exact <code>useThisAsync</code> overload!</p>
<h4 id="type-holes">Type Holes</h4>
<p>A DIY type hole technique is sometimes useful to help figure out stubborn types (see <a href="https://dev.to/gcanti/type-holes-in-typescript-2lck" target="_blank">Type holes in TS</a>).</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true"></a><span class="co">//genric (why not say polymorphic) bottom function will allow me to ask type questions</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true"></a><span class="im">export</span> <span class="kw">const</span> _ <span class="op">=</span> <span class="op">&lt;</span>T<span class="op">&gt;</span>()<span class="op">:</span> T <span class="kw">=&gt;</span> {</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true"></a>    <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">Error</span>(<span class="st">&quot;hole&quot;</span>)<span class="op">;</span> </span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true"></a>}</span></code></pre></div>
<p>A type hole allows me to ask the compiler <em>type questions</em>.<br />
You can learn a lot about how the type checker works using it. E.g. using my <code>Either</code> type as an example:</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true"></a><span class="kw">const</span> tstnum<span class="op">:</span> Either<span class="op">&lt;</span>number<span class="op">,</span> string<span class="op">&gt;</span> <span class="op">=</span> {<span class="dt">type</span><span class="op">:</span> <span class="st">&quot;left&quot;</span><span class="op">,</span> <span class="dt">content</span><span class="op">:</span> <span class="fu">_</span>()}</span></code></pre></div>
<p>if you hover over <code>_</code> you will see</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true"></a>(alias) _<span class="op">&lt;</span>number<span class="op">&gt;</span>()<span class="op">:</span> number</span></code></pre></div>
<p>Nice! If you hover over <code>_</code> in this expression</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true"></a><span class="kw">const</span> str <span class="op">=</span> <span class="st">&quot;Hello &quot;</span> <span class="op">+</span> <span class="fu">_</span>()</span></code></pre></div>
<p>you will see</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true"></a>(alias) _<span class="op">&lt;</span>unknown<span class="op">&gt;</span>()<span class="op">:</span> unknown</span></code></pre></div>
<p>This can provide a lot of insight into types and how TS uses them!</p>
<p>I have not been very lucky in using type holes to figure out why TS is confused. Returning to my failed <code>body3</code> example:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true"></a><span class="co">//body3 inferred type is 'unknown'</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true"></a><span class="kw">const</span> body3  <span class="op">=</span> <span class="cf">await</span> <span class="fu">officePromise</span> (<span class="fu">curry3</span> (item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="at">getAsync</span>)(Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span>)(<span class="fu">_</span>())) </span></code></pre></div>
<p>if I hover over the <code>_</code> function, the IntelliSense suggests this completely wrong type:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true"></a>(alias) _<span class="op">&lt;</span>((asyncResult<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>) <span class="op">|</span> <span class="kw">undefined</span><span class="op">&gt;</span>()<span class="op">:</span> ((asyncResult<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>) <span class="op">|</span> <span class="kw">undefined</span></span></code></pre></div>
<p>The type hole confirms that the compiler is trying to match against the two parameter overload of <code>item.body.getAsync</code>. This confirms my hypothesis from the last section that what made TS confused here was the overloading. There are a few things to note here:</p>
<ul>
<li>We are asking TS “why are you confused?” and that is a funny question.</li>
<li>This type hole did not tell us more than the compilation error message itself. However, the type hole is more targeted so it could reveal more specific information in some cases.</li>
<li>Type holes may tell us something useful in situations where the code compiles but we do not understand why.</li>
</ul>
<p>If, as before, I add the type application (<code>&lt;Office.CoercionType, Office.AsyncContextOptions, OfficeCallack&lt;string&gt;, void&gt;</code>) to <code>curry3</code> the <code>_()</code> will show the type correctly:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true"></a>(alias) _<span class="op">&lt;</span>Office<span class="op">.</span><span class="at">AsyncContextOptions</span><span class="op">&gt;</span>()<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncContextOptions</span></span></code></pre></div>
<p><strong>About some limitations</strong><br />
Sadly, the <code>_&lt;T&gt;(): T</code> is not universally useful, e.g. this will not compile:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true"></a><span class="co">//compilation error: </span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true"></a><span class="co">//       Argument of type '(ax: never, bx: never) =&gt; never' is not assignable </span></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true"></a><span class="co">//       to parameter of type '(ax: unknown, bx: unknown) =&gt; unknown'.</span></span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true"></a><span class="dt">const</span> testfn = <span class="fu">curry</span>(_()) </span></code></pre></div>
<div class="sourceCode" id="cb30"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true"></a><span class="co">//interestingly the following compiles as curry&lt;unknown, unknown, unknown&gt;</span></span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true"></a><span class="kw">const</span> testfn <span class="op">=</span> <span class="fu">curry</span>({} <span class="im">as</span> any)</span></code></pre></div>
<p>TS type checker does not work with type variables at the top level. That makes testing expressions like <code>curry(_())</code> rather pointless. The following compiles just fine, and would be a good (not very useful but good) choice for the inferred type of <code>_()</code>:</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true"></a>type GenFn2Type <span class="op">=</span> (ax<span class="op">:</span> never<span class="op">,</span> bx<span class="op">:</span> never) <span class="kw">=&gt;</span> unknown</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true"></a></span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true"></a><span class="kw">const</span> compiles <span class="op">=</span> <span class="fu">curry</span>(_<span class="op">&lt;</span>GenFn2Type<span class="op">&gt;</span>()) </span></code></pre></div>
<p>but TS fails instead of inferring that type.</p>
<p>There is an interesting relationship between the <code>never</code> type and <code>_&lt;T&gt;(): T</code>. There will be a future note about it.<br />
The type hole <code>_</code> function is a useful tool and we will keep using it in future type explorations.</p>
<p>Using types requires some experience, knowledge, and patience. More advanced types come with more misleading error messages, it takes experience to find the underlying cause of a misleading compilation error, and that is true in any language. Eventually, I (and you) will look at a TS compilation error and will say “ah, you really meant this: …”.</p>
<p>I am mostly left to my own devices when working with more involved types in TS. Hopefully the future will bring us mainstream grade interactive tools that allow asking type questions, browsing types, and help solving type puzzles. For now it is mostly the programmer who connects the dots.<br />
The good news is that this gets easier and easier with practice. I have been working in TS for only about 2 months now and I already see a difference.</p>
<p><em>Good code requires two type checkers: TypeScript and You</em></p>
<h3 id="compilation-bloopers">Compilation bloopers</h3>
<p>We already saw “correct” programs that should have compiled but did not (e.g. <code>curry(_())</code>, <code>body3</code> example) and we will see more in the future notes. Our <code>body4</code> example compiled but it was a bug.<br />
This note shows other, less contrived, examples that compile and are clearly bugs.</p>
<p>All of these type check:</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true"></a><span class="co">//annotated correct code added for reference, this code compiles</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true"></a><span class="kw">const</span> good<span class="op">:</span> (a<span class="op">:</span> Office<span class="op">.</span><span class="at">CoercionType</span>) </span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true"></a>          <span class="kw">=&gt;</span> (b<span class="op">:</span> ((asyncResult<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>)) </span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true"></a>          <span class="kw">=&gt;</span> <span class="kw">void</span></span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true"></a>    <span class="op">=</span> <span class="fu">curry</span> (item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="at">getAsync</span>)</span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true"></a></span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true"></a><span class="co">//compiles but it should not, compiles even with type annotation</span></span>
<span id="cb32-8"><a href="#cb32-8" aria-hidden="true"></a><span class="kw">const</span> nonsense1<span class="op">:</span> (a<span class="op">:</span> Office<span class="op">.</span><span class="at">CoercionType</span>) </span>
<span id="cb32-9"><a href="#cb32-9" aria-hidden="true"></a>          <span class="kw">=&gt;</span> (b<span class="op">:</span> ((asyncResult<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span>)) </span>
<span id="cb32-10"><a href="#cb32-10" aria-hidden="true"></a>          <span class="kw">=&gt;</span> <span class="kw">void</span></span>
<span id="cb32-11"><a href="#cb32-11" aria-hidden="true"></a>    <span class="op">=</span> <span class="fu">curry</span> (<span class="fu">curry</span> (item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="at">getAsync</span>)) </span>
<span id="cb32-12"><a href="#cb32-12" aria-hidden="true"></a></span>
<span id="cb32-13"><a href="#cb32-13" aria-hidden="true"></a><span class="co">//compiles but it should not</span></span>
<span id="cb32-14"><a href="#cb32-14" aria-hidden="true"></a><span class="kw">const</span> nonsense2 <span class="op">=</span> <span class="fu">curry</span>(curry)</span>
<span id="cb32-15"><a href="#cb32-15" aria-hidden="true"></a></span>
<span id="cb32-16"><a href="#cb32-16" aria-hidden="true"></a><span class="co">//... more examples in the linked github project</span></span></code></pre></div>
<p>and all, except the first one, are bugs.<br />
One pattern is clearly visible: <code>unknown</code> somewhere in the type<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a>.<br />
The underlying reason seems to be much simpler: TS does not exactly match the types of parameters that are functions. The underlying reason is that <a href="https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-with-fewer-parameters-assignable-to-functions-that-take-more-parameters" target="_blank">functions with fewer parameters are assignable to functions that take more parameters</a><a href="#fn6" class="footnote-ref" id="fnref6" role="doc-noteref"><sup>6</sup></a>:</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true"></a>declare <span class="kw">function</span> <span class="fu">testfn</span>(fn<span class="op">:</span> (str<span class="op">:</span>string) <span class="kw">=&gt;</span> number)<span class="op">:</span>number</span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true"></a></span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true"></a><span class="co">//compiles, calculated type is: const num: number</span></span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true"></a><span class="kw">const</span> num <span class="op">=</span> <span class="fu">testfn</span>(() <span class="kw">=&gt;</span> <span class="dv">1</span>)</span></code></pre></div>
<p>IMO this language design decision can lead to very confusing escaped bugs and it smells like subtyping<a href="#fn7" class="footnote-ref" id="fnref7" role="doc-noteref"><sup>7</sup></a>. Higher order functions are not uncommon in JavaScript. The <code>nonsense1</code> example is a piece of code I accidentally wrote in my project.<br />
This is very concerning since errors like these are likely to remain uncaught and become escaped bugs.<br />
Careful reader will notice that my <code>body4</code> example is a perfect storm. Here it is again:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true"></a><span class="co">//this compiles by using a wrong input parameter type and returns 'body4: unknown'</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true"></a><span class="kw">const</span> crazyConfig <span class="op">:</span> (_<span class="op">:</span> Office<span class="op">.</span><span class="at">AsyncResult</span><span class="op">&lt;</span>string<span class="op">&gt;</span>) <span class="kw">=&gt;</span> <span class="kw">void</span> <span class="op">=</span> x <span class="kw">=&gt;</span> <span class="st">&quot;&quot;</span></span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true"></a><span class="kw">const</span> body4 <span class="op">=</span> <span class="cf">await</span> <span class="fu">officePromise</span> (<span class="fu">curry3</span>(item<span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="at">getAsync</span>)(Office<span class="op">.</span><span class="at">CoercionType</span><span class="op">.</span><span class="at">Html</span>)(crazyConfig)) </span></code></pre></div>
<p>TS picks a (wrong) 2 parameter overload of <code>item.body.getAsync</code> because it was defined last by <em>office.js</em>. It assigns it to <code>curry3</code> because <code>curry3</code> expects a 3 parameter function and 2 &lt; 3 is OK.<br />
Sadly, accepting <code>body4</code> code is TypeScript “Working as Intended” (<a href="https://github.com/microsoft/TypeScript/issues/43187" target="_blank">#43187</a>, <a href="https://github.com/microsoft/TypeScript/issues/48624" target="_blank">#48624</a>).</p>
<p>Compared to other programming languages I use, TS’s rate of compiler issues I encounter is much higher, the issues are more dangerous, and are likely to happen on more commonly used vanilla code (well… at least commonly used by me).<br />
I can see two general reasons for this: gradual typing on top of JS is not easy, subtyping is not easy. I plan to write a note about the complexity of TS types in a future post.</p>
<h3 id="its-all-worth-it">It’s all worth it</h3>
<p>One common concern related to using types (especially more advanced types) is a slowdown in the development speed.<br />
There is some truth to this in general because of things like compilation times in some language environments. I cannot comment on TS compilation times for large projects, so far it is not a problem for me. In my experience, having a type checker is a huge productivity bust. In my experience, the more types the faster the development speed. That is true even with compilation bloopers.<br />
Efficiency considerations are somewhat personal so your experience may vary.</p>
<p>I rewrote some legacy code using the techniques in this section. That effort resulted in significant size reduction and an overall big improvement in readability and correctness when compared to the code I was replacing or to code in the <em>office.js</em> documentation.<br />
A lot of the improvement comes from using <code>await</code> <code>async</code> syntax sugar but converting functions to their curried form and figuring out more terse ways to type annotate also results in added clarity and significant syntactic simplification.</p>
<p>In my book, there is just no comparing TS to JS, TS is the clear winner.<br />
How does TS compare to statically type checked frontend languages that compile to JS and have capable type checkers and solid types (e.g. Reason, Elm, PureScript, even Haskell)? I am not in a good position to discuss this yet.<br />
Lots of projects need to stay close to JS, my project at work falls into this group. For such projects TS is the right choice IMO.</p>
<h2 id="relevant-typescript-language-tickets">Relevant TypeScript Language tickets</h2>
<ul>
<li><a href="https://github.com/microsoft/TypeScript/issues/43187" target="_blank">#43187</a> the overloading issue (type inference considers the last overload only) has been known and has been marked as “Docs”.</li>
<li><a href="https://github.com/microsoft/TypeScript/issues/48624" target="_blank">#48624</a> (I entered it) about my blooper examples has been marked as “Working as Intended”</li>
<li><a href="https://github.com/microsoft/TypeScript/issues/48625" target="_blank">#48625</a> <code>curry(_())</code> not compiling issue (I entered it) has been marked as “Working as Intended”</li>
</ul>
<h2 id="next-chapter">Next Chapter</h2>
<p>We are not done with <em>office.js</em>. I will use it in future notes.</p>
<p>Do statically defined types reflect the actual runtime values? How to assure that they do?<br />
We will discuss these questions in the next installment. Here is the link: <a href="2021-12-24-ts-types-part2.html" target="_blank">Part 2. Typing Honestly</a></p>
<h2 id="summary-of-final-edits">Summary of final edits</h2>
<p>Added context to <a href="#bumps-on-the-path">bumps on the path</a> section about type inference not working well with overloaded methods.</p>
<p>Added context to why <code>curry(_())</code> is not compiling.</p>
<p>Added context to <a href="#compilation-bloopers">compilation bloopers</a> section explaining the underlying reason for TS accepting my blooper examples: TS allows to assign a function with fewer parameters to a function type with more parameters. The arity does not need to match.</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>I rewrote the side note and improved <code>officePromise</code> type definition based on a <a href="https://www.reddit.com/r/typescript/comments/rnougu/comment/hptsnvg/?utm_source=share&amp;utm_medium=web2x&amp;context=3" target="_blank">comment</a> from <em>u/Tubthumper8</em> on reddit. Thanks!<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>The situation is just slightly more complicated since the <code>item</code> property is overloaded but that is not important for now.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3" role="doc-endnote"><p>As the previously linked <a href="https://github.com/rpeszek/typescript-issues/blob/master/src/RejectingCorrectCode/RejectingOverloads.ts" target="_blank">example</a> shows, I indeed should worry.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4" role="doc-endnote"><p>It should be noted that with this syntax type variables will be defined in the <em>lhs</em> of the definition and will be outside of the lexical scope in the <em>rhs</em>. You would have to re-declare them which makes this approach much less usable in the presence of type variables.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5" role="doc-endnote"><p><code>nonsense1</code> will will show <code>unknown</code> if you remove the type annotation.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn6" role="doc-endnote"><p>This took me a long time to figure out and was added late.<a href="#fnref6" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn7" role="doc-endnote"><p>In fact it is subtyping. In TS <code>() =&gt; number</code> extends <code>(_:string) =&gt; number</code>. One can argue that this TS design decision follows from how JS works and is often used.<a href="#fnref7" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

    </section>
</article>
]]></summary>
</entry>

</feed>
