Allanderek's bloghttps://blog.poleprediction.com2022-07-25T16:18:24ZThe thoughts of a programmer, heavily slanted towards development in Elm, but occasionally more general programming.allanderekLaziness would be good for Time.Extra.posixToParts2022-10-27T15:45:04Zhttps://blog.poleprediction.com/time-extra-posix-to-parts-great-laxy-example<p>In this post I'm going to show a good example of where laziness would work well. This is of course not an argument
that lazy programming languages are somehow better than strictly evaluated programming languages. Rather what I wish to
do here is answer the question, what is laziness good for?</p>
<p>My example here comes from the <a href="https://package.elm-lang.org/packages/justinmimbs/time-extra/latest/" rel="nofollow">justinmimbs/time-extra Elm package</a>.
The main purpose of this library is to provide a means for working with the standard library's <a href="https://package.elm-lang.org/packages/elm/time/latest/Time#Posix" rel="nofollow">Time.Posix</a>
values. Functions are provided to calculate the difference between two time values, and also to add/minus a given interval from a
given time value. So for example it provides a convenient way to take a given time value and add one day, or two hours, or six months.</p>
<p>However, as an additional utility it provides the <a href="https://package.elm-lang.org/packages/justinmimbs/time-extra/latest/Time-Extra#posixToParts" rel="nofollow">posixToParts</a>
which takes in a <code>Time.Posix</code> value (and a zone) and gives back a record:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Parts</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="p">{</span> <span class="nv">year</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="p">,</span> <span class="nv">month</span> <span class="nf">:</span> <span class="kt">Month</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"> <span class="p">,</span> <span class="nv">day</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"> <span class="p">,</span> <span class="nv">hour</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"> <span class="p">,</span> <span class="nv">minute</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"> <span class="p">,</span> <span class="nv">second</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"> <span class="p">,</span> <span class="nv">millisecond</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"> <span class="p">}</span>
</span></span></code></pre><p>So a common way you might use this, is to get the 'date' part of a time value. That is get the <code>year</code>, <code>month</code> and <code>day</code> values.
So suppose you have a bunch of events, all that have start-times stored as a <code>Time.Posix</code>. Now, what you might want to do is show all of
those events which are on today. Assuming that you have the current time as a <code>Time.Posix</code>, you can do something like this:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Event</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="p">{</span> <span class="nv">start</span> <span class="nf">:</span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Posix</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="p">,</span> <span class="c1">-- Presumably other relevant data about an event.</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nv">getTodaysEvents</span> <span class="nf">:</span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Zone</span> <span class="nf">-></span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Posix</span> <span class="nf">-></span> <span class="kt">List</span> <span class="kt">Event</span> <span class="nf">-></span> <span class="kt">List</span> <span class="kt">Event</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nv">getTodaysEvents</span> <span class="nv">zone</span> <span class="nv">now</span> <span class="nv">events</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="nv">todaysParts</span> <span class="nf">:</span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Extra</span><span class="nf">.</span><span class="kt">Parts</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="nv">todaysParts</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Extra</span><span class="nf">.</span><span class="nv">posixToParts</span> <span class="nv">zone</span> <span class="nv">now</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="nv">isToday</span> <span class="nf">:</span> <span class="kt">Event</span> <span class="nf">-></span> <span class="kt">Bool</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="nv">isToday</span> <span class="nv">event</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="nv">eventParts</span> <span class="nf">:</span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Extra</span><span class="nf">.</span><span class="kt">Parts</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="nv">eventParts</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Extra</span><span class="nf">.</span><span class="nv">posixToParts</span> <span class="nv">zone</span> <span class="nv">event</span><span class="nf">.</span><span class="nv">start</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="nv">eventParts</span><span class="nf">.</span><span class="nv">year</span> <span class="nf">==</span> <span class="nv">todaysParts</span><span class="nf">.</span><span class="nv">year</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"> <span class="nf">&&</span> <span class="nv">eventParts</span><span class="nf">.</span><span class="nv">month</span> <span class="nf">==</span> <span class="nv">todaysParts</span><span class="nf">.</span><span class="nv">month</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"> <span class="nf">&&</span> <span class="nv">eventParts</span><span class="nf">.</span><span class="nv">day</span> <span class="nf">==</span> <span class="nv">todaysParts</span><span class="nf">.</span><span class="nv">day</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"> <span class="kt">List</span><span class="nf">.</span><span class="nv">filter</span> <span class="nv">isToday</span> <span class="nv">events</span>
</span></span></code></pre><p>This will work perfectly well, but it's doing quite a lot of work that is unnecessary. For each event it is calculating not just the,
year, month, and day associated with the start <code>Time.Posix</code>, but also the hour, minute, second, and millisecond. These values are
calculated, but just thrown-away. If there are a lot of events, then it might be desirable to write our own version of <code>Time.Extra.posixToParts</code>:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Date</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="p">{</span> <span class="nv">year</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="p">,</span> <span class="nv">month</span> <span class="nf">:</span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Month</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="p">,</span> <span class="nv">day</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nv">posixToDate</span> <span class="nf">:</span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Zone</span> <span class="nf">-></span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Posix</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nv">posixToDate</span> <span class="nv">zone</span> <span class="nv">time</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="p">{</span> <span class="nv">year</span> <span class="nf">=</span> <span class="kt">Time</span><span class="nf">.</span><span class="nv">getYear</span> <span class="nv">zone</span> <span class="nv">time</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="p">,</span> <span class="nv">month</span> <span class="nf">=</span> <span class="kt">Time</span><span class="nf">.</span><span class="nv">getMonth</span> <span class="nv">zone</span> <span class="nv">month</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="p">,</span> <span class="nv">day</span> <span class="nf">=</span> <span class="kt">Time</span><span class="nf">.</span><span class="nv">getDay</span> <span class="nv">zone</span> <span class="nv">month</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nv">getTodaysEvents</span> <span class="nf">:</span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Zone</span> <span class="nf">-></span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Posix</span> <span class="nf">-></span> <span class="kt">List</span> <span class="kt">Event</span> <span class="nf">-></span> <span class="kt">List</span> <span class="kt">Event</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nv">getTodaysEvents</span> <span class="nv">zone</span> <span class="nv">now</span> <span class="nv">events</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="nv">today</span> <span class="nf">:</span> <span class="kt">Date</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="nv">today</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="nv">posixToDate</span> <span class="nv">zone</span> <span class="nv">now</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"> <span class="nv">isToday</span> <span class="nf">:</span> <span class="kt">Event</span> <span class="nf">-></span> <span class="kt">Bool</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"> <span class="nv">isToday</span> <span class="nv">event</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="p">(</span><span class="nv">posixToDate</span> <span class="nv">event</span><span class="nf">.</span><span class="nv">start</span> <span class="nf">==</span> <span class="nv">today</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"> <span class="kt">List</span><span class="nf">.</span><span class="nv">filter</span> <span class="nv">isToday</span> <span class="nv">events</span>
</span></span></code></pre><p>This works well enough, but it's slightly unsatisfying that I've had to re-implement a library function just because the
library function did too <strong>much</strong> work.</p>
<p>But note, that even this is doing potentially too much work, if the year is not correct, we needn't check the month nor day.</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">getTodaysEvents</span> <span class="nf">:</span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Zone</span> <span class="nf">-></span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Posix</span> <span class="nf">-></span> <span class="kt">List</span> <span class="kt">Event</span> <span class="nf">-></span> <span class="kt">List</span> <span class="kt">Event</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">getTodaysEvents</span> <span class="nv">zone</span> <span class="nv">now</span> <span class="nv">events</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="nv">todayDay</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="nv">todayDay</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kt">Time</span><span class="nf">.</span><span class="nv">getDay</span> <span class="nv">zone</span> <span class="nv">now</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="nv">todayMonth</span> <span class="nf">:</span> <span class="kt">Time</span><span class="nf">.</span><span class="kt">Month</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="nv">todayMonth</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kt">Time</span><span class="nf">.</span><span class="nv">getMonth</span> <span class="nv">zone</span> <span class="nv">now</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="nv">todayYear</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="nv">todayYear</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="kt">Time</span><span class="nf">.</span><span class="nv">getYear</span> <span class="nv">zone</span> <span class="nv">now</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="nv">isToday</span> <span class="nf">:</span> <span class="kt">Event</span> <span class="nf">-></span> <span class="kt">Bool</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="nv">isToday</span> <span class="nv">event</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kt">Time</span><span class="nf">.</span><span class="nv">getYear</span> <span class="nv">zone</span> <span class="nv">event</span><span class="nf">.</span><span class="nv">start</span> <span class="nf">==</span> <span class="nv">todayYear</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="nf">&&</span> <span class="kt">Time</span><span class="nf">.</span><span class="nv">getMonth</span> <span class="nv">zone</span> <span class="nv">event</span><span class="nf">.</span><span class="nv">start</span> <span class="nf">==</span> <span class="nv">todayMonth</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="nf">&&</span> <span class="kt">Time</span><span class="nf">.</span><span class="nv">getDay</span> <span class="nv">zone</span> <span class="nv">event</span><span class="nf">.</span><span class="nv">start</span> <span class="nf">==</span> <span class="nv">todayDay</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"> <span class="kt">List</span><span class="nf">.</span><span class="nv">filter</span> <span class="nv">isToday</span> <span class="nv">events</span>
</span></span></code></pre><p>Because <code>&&</code> does not evaluate the right-hand side if the left-hand side is <code>False</code> we avoid calculating the month and day of an
event's start time if the year is not the same as the current one.</p>
<p>Even this version potentially does a small amount of work that it needn't. If <strong>none</strong> of the event start times are in the correct year
then we will needlessly calculate today's month and day. Of course in this case, that's a minor extra calculation (and probably faster than
the lazy version if laziness is done via thunks). However, note that it's non-trivial to see when you might be doing unnecessary work, and
remember, that this is pretty simple exmaple.</p>
<p>It's not difficult to find such cases in your own code, where either you're potentially doing more work than is necessary, or you're
crafting conditional evaluation in order to avoid unnecessary calculation. These conditionals will make your code more complex and
hence more diffcult to maintain.</p>
<p>A thought experiment, how could the <code>justinmimbs/time-extra</code> Elm package achieve this configurability? First of all it could attempt
having multiple functions which get different 'parts', just as our <code>posixToDate</code> function got the parts we needed. Note though that
that was still insufficiently lazy, and the number of different functions is combinatorial, though in this particular case you could
probably guess at a few common ones, such as 'date', or 'time of day'.</p>
<p>Of course it could return a lambda for each field of the record, but then you might evaluate that lambda more than once. In any case
if it did this it would be re-implementing laziness.</p>
<p>The main point I'm making here is not that laziness is more efficient, it's that it frees you from having to consider unnecessary work
and as such can lead to simplier code structure, that is easier to maintain.</p>
A motivation for why laziness is a good programming language feature.No more imports2022-10-06T13:50:13Zhttps://blog.poleprediction.com/no-more-imports<p>Imports in most languages always seem like a bit of an add-on, a separate language to the actual language.
Today's post is a thought experiment regarding removing imports from Elm/Gren. Ultimately I think it's probably worthwhile
having the import statements there, but this is an interesting (and quick) thought experiment.</p>
<p>If we remove <code>import</code> statements from the head of a module file the first thing to note is that a file then becaomes one <code>module</code> declaration line followed by simply a list of top level declarations.
However, we would obviously need someway to refer to values and types defined in another module.</p>
<p>Suppose we just say that <strong>all</strong> modules that <strong>could</strong> be referenced are "in scope". I'll define "in scope" shortly
but first we need need to decide what modules could be referenced. Fortunately this is defined pretty strictly
in the elm/gren.json. It's just any module that appears in a source-directory or in package dependency.</p>
<p>To actually use a value from another module, the module has to be "in scope". All that means is that the fully
qualified module name is available to the programmer. It is equivalent to taking all modules that could be referenced
and generating an import statement with no <code>as</code> or <code>exposing</code> clause. Assuming you have <code>elm/core</code> or <code>gren/core</code> in
your list of dependencies you will have an implied set of import statements that looks like:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kn">import </span><span class="nc">Array</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">import </span><span class="nc">Basics</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kn">import </span><span class="nc">Bitwise</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="kn">import </span><span class="nc">Char</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="kn">import </span><span class="nc">Debug</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nv">etc</span><span class="nf">.</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="kn">import </span><span class="nc">Platform.Cmd</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="kn">import </span><span class="nc">Platform.Sub</span>
</span></span></code></pre><p>This is already a perfectly usable language. It's a little clunky, particulary with respect to infix operators.
Everything needs to be qualified, and even module names cannot be aliased. So no more referring to <code>Json.Encode.Value</code> as <code>Encode.Value</code>.</p>
<p>Let's deal with the first of these, everything must be qualified. First of all, most types and functions I <a href="/my-import-scheme" rel="nofollow">qualify already</a>.
But suppose you're one of those people who do not like to write <code>Html.div</code> everywhere. That's fine, you can alias that yourself:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">div</span> <span class="nf">:</span> <span class="kt">List</span><span class="nf">.</span><span class="kt">List</span> <span class="p">(</span><span class="kt">Html</span><span class="nf">.</span><span class="kt">Attribute</span> <span class="nv">msg</span><span class="p">)</span> <span class="nf">-></span> <span class="kt">List</span><span class="nf">.</span><span class="kt">List</span> <span class="p">(</span><span class="kt">Html</span><span class="nf">.</span><span class="kt">Html</span> <span class="nv">msg</span><span class="p">)</span> <span class="nf">-></span> <span class="kt">Html</span><span class="nf">.</span><span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">div</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="kt">Html</span><span class="nf">.</span><span class="nv">div</span>
</span></span></code></pre><p>Okay I'll agree that this is not terribly beautiful, mostly because of all the qualified types. You could get around this by not giving the signature:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">div</span> <span class="nf">=</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">div</span>
</span></span></code></pre><p>This is now exactly equivalent to having an <code>exposing</code> clause when importing the <code>Html</code> module.
Except that <code>elm-review</code> might complain about it because it is a top-level definition without a type-signature, but the <code>elm-review</code> rules for this could be easily ammended to not warn
about alias definition unadorned with a type signature. If you leave the type-signature on you will of course have to update the alias if the aliased value changes in type (more likely for values defined in your own modules).
In any case, I feel that such aliases, at least for values, should be used sparingly.</p>
<p>But let's worry about those type definitions, you can of course do the same thing and just alias those:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Html</span> <span class="nf">=</span> <span class="kt">Html</span><span class="nf">.</span><span class="kt">Html</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">List</span> <span class="nf">=</span> <span class="kt">List</span><span class="nf">.</span><span class="kt">List</span>
</span></span></code></pre><p>You cannot simulate an <code>exposing (..)</code> clause, but I definitely feel that those are dubious anyway. I could certainly live without them.</p>
<p>That means we can simulate any <code>exposing</code> clause of a module import, other than <code>(..)</code>.
I tend to qualify even constructor names, but we would also lack a means of opening a custom union type.</p>
<p>The last thing that would not be possible is simulating an <code>as</code> clause. If we allowed a module as a value, then we could do:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kt">Html</span><span class="nf">.</span><span class="kt">Attributes</span> <span class="nf">=</span> <span class="kt">Attributes</span>
</span></span></code></pre><p>With this, we would have no need for import statements.</p>
<p>The fact that you would need to provide some way to alias a module name to a shorter one, might open up some nice possibilities, most obvious of which is providing a very short alias to a module that is only used within on top-level definition (presumably because it uses that module extensively)
So for example you might generally wish to refer to it as <code>Html.Attributes</code>, but may when defining some element with a lot of attributes you could do:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">let</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="kt">A</span> <span class="nf">=</span> <span class="kt">Html</span><span class="nf">.</span><span class="kt">Attributes</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kr">in</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="kt">Html</span><span class="nf">.</span><span class="nv">button</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"> <span class="p">[</span> <span class="kt">A</span><span class="nf">.</span><span class="nv">id</span> <span class="nf">...</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"> <span class="p">,</span> <span class="kt">A</span><span class="nf">.</span><span class="nv">class</span> <span class="nf">...</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"> <span class="p">,</span> <span class="kt">A</span><span class="nf">.</span><span class="nv">disabled</span> <span class="nf">..</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"> <span class="nf">..</span> <span class="nv">etc</span><span class="nf">.</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"> <span class="p">]</span>
</span></span></code></pre><h3 id="advantages"><a class="anchor" href="#advantages" rel="nofollow">#</a> Advantages</h3>
<p>You can have any of these aliases at any point in the module, including within a <code>let-in</code> expression thereby constricting its scope.</p>
<p>Module imports don't need to be kept up to date, and you can just use a value from another module without going to the top of the file and adding an <code>import</code> statement (or checking if one already exists).</p>
<p>I like that you would be forced into making module aliases a little more expressive.</p>
<h3 id="disadvantages"><a class="anchor" href="#disadvantages" rel="nofollow">#</a> Disadvantages</h3>
<p>It's more difficult to see at a glance which modules are imported by a module.</p>
<p>You cannot, as easily, expose all the constructors of an imported custom union datatype.</p>
<h2 id="conclusion"><a class="anchor" href="#conclusion" rel="nofollow">#</a> Conclusion</h2>
<p>Mostly imports then are a set of syntactic sugar forms for declarations.
On balance I think it's probably worthwhile having those syntactic sugar forms, but not by much.</p>
Do we really need imports?Underscore type params2022-07-26T14:44:51Zhttps://blog.poleprediction.com/underscore-type-param<p>In a case expression when you want to match <strong>anything</strong> you can use the special pattern <code>_</code> which avoids giving a name to something you aren't going to use. However, you cannot do the same thing for type parameters. If you want to say "I accept any type here and I don't return anything containing that type" you just have to use an unused ttype variable name. I think you should be allowed to "say what you mean" and use underscore here for that situation. I'm going to give a motivating circumstance in which this would actually be useful.</p>
<p>To give a concrete example, suppose you wish to define the <code>length</code> function for lists:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">length</span> <span class="nf">:</span> <span class="kt">List</span> <span class="nv">a</span> <span class="nf">-></span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">length</span> <span class="nv">l</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="kr">case</span> <span class="nv">l</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"> <span class="p">[]</span> <span class="nf">-></span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"> <span class="nv">_</span> <span class="nf">::</span> <span class="nv">rest</span> <span class="nf">-></span> <span class="mi">1</span> <span class="nf">+</span> <span class="nv">length</span> <span class="nv">rest</span>
</span></span></code></pre><p>I think you should be able to write the type signature here as <code>List _ -> Int</code>. Note that this only works because we're not returning any of the elements, we could <strong>not</strong> do: <code>reverse : List _ -> List _</code>. In just the same what that you cannot use <code>_</code> as a variable name. So it is crucial that underscore does not simply introduce a new type variable name into scope which must be unified.</p>
<p>A particularly common place where you don't care about type parameter is when used within an extensible record. Suppose you have the following function:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">viewSideBar</span> <span class="nf">:</span> <span class="p">{</span> <span class="nv">a</span> <span class="nf">|</span> <span class="nv">now</span> <span class="nf">:</span> <span class="kt">Time</span><span class="p">}</span> <span class="nf">-></span> <span class="kt">User</span> <span class="nf">-></span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">viewSideBar</span> <span class="nv">model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="nv">showTime</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="nf">...</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="nv">showUser</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="nf">...</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">Html</span><span class="nf">.</span><span class="nv">div</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="p">[</span> <span class="kt">Attributes</span><span class="nf">.</span><span class="nv">class</span> <span class="s">"sidebar"</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="p">[</span> <span class="nv">showTime</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">,</span> <span class="nv">showUser</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span></code></pre><p>Now suppose the user may have a list of pets, as well as a list of siblings, ignoring the possibility that either of these two lists is empty:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">viewSideBar</span> <span class="nf">:</span> <span class="p">{</span> <span class="nv">a</span> <span class="nf">|</span> <span class="nv">now</span> <span class="nf">:</span> <span class="kt">Time</span><span class="p">}</span> <span class="nf">-></span> <span class="kt">User</span> <span class="nf">-></span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">viewSideBar</span> <span class="nv">model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="nv">showTime</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="nf">...</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="nv">showUser</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="nf">...</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="nv">showPets</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="nv">showPets</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">Html</span><span class="nf">.</span><span class="nv">ul</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="p">[</span> <span class="kt">Attributes</span><span class="nf">.</span><span class="nv">class</span> <span class="s">"user-pets"</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">[</span> <span class="kt">List</span><span class="nf">.</span><span class="nv">map</span> <span class="p">(</span><span class="nf">\</span><span class="nv">p</span> <span class="nf">-></span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">li</span> <span class="p">[]</span> <span class="p">[</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">text</span> <span class="nv">p</span><span class="nf">.</span><span class="nv">name</span><span class="p">])</span> <span class="nv">user</span><span class="nf">.</span><span class="nv">pets</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="nv">showSiblings</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="nv">showSiblings</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kt">Html</span><span class="nf">.</span><span class="nv">ul</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="p">[</span> <span class="kt">Attributes</span><span class="nf">.</span><span class="nv">class</span> <span class="s">"user-siblings"</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="p">[</span> <span class="kt">List</span><span class="nf">.</span><span class="nv">map</span> <span class="p">(</span><span class="nf">\</span><span class="nv">s</span> <span class="nf">-></span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">li</span> <span class="p">[]</span> <span class="p">[</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">text</span> <span class="nv">s</span><span class="nf">.</span><span class="nv">name</span><span class="p">])</span> <span class="nv">user</span><span class="nf">.</span><span class="nv">siblings</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="kt">Html</span><span class="nf">.</span><span class="nv">div</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"> <span class="p">[</span> <span class="kt">Attributes</span><span class="nf">.</span><span class="nv">class</span> <span class="s">"sidebar"</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"> <span class="p">[</span> <span class="nv">showTime</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"> <span class="p">,</span> <span class="nv">showUser</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"> <span class="p">,</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">h</span><span class="mi">2</span> <span class="p">[]</span> <span class="p">[</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">text</span> <span class="s">"Pets"</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"> <span class="p">,</span> <span class="nv">showPets</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"> <span class="p">,</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">h</span><span class="mi">2</span> <span class="p">[]</span> <span class="p">[</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">text</span> <span class="s">"Siblings"</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"> <span class="p">,</span> <span class="nv">showSiblings</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"> <span class="p">]</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span></code></pre><p>Now, a <code>Pet</code> is a different type from a <code>Sibling</code> but you realise that both are rendered the same, as both have a <code>name</code> field and that's all that is used here, so you factor that out:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">viewSideBar</span> <span class="nf">:</span> <span class="p">{</span> <span class="nv">a</span> <span class="nf">|</span> <span class="nv">now</span> <span class="nf">:</span> <span class="kt">Time</span><span class="p">}</span> <span class="nf">-></span> <span class="kt">User</span> <span class="nf">-></span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">viewSideBar</span> <span class="nv">model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="nv">showTime</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="nf">...</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="nv">showUser</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="nf">...</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="nv">showNamedList</span> <span class="nf">:</span> <span class="kt">String</span> <span class="nf">-></span> <span class="kt">List</span> <span class="p">{</span> <span class="nv">a</span> <span class="nf">|</span> <span class="nv">name</span> <span class="nf">:</span> <span class="kt">String</span> <span class="p">}</span> <span class="nf">-></span> <span class="kt">Html</span> <span class="nv">list</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="nv">showNamedList</span> <span class="nv">className</span> <span class="nv">nameables</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">Html</span><span class="nf">.</span><span class="nv">ul</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="p">[</span> <span class="kt">Attributes</span><span class="nf">.</span><span class="nv">class</span> <span class="nv">className</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">[</span> <span class="kt">List</span><span class="nf">.</span><span class="nv">map</span> <span class="p">(</span><span class="nf">\</span><span class="nv">n</span> <span class="nf">-></span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">li</span> <span class="p">[]</span> <span class="p">[</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">text</span> <span class="nv">n</span><span class="nf">.</span><span class="nv">name</span><span class="p">])</span> <span class="nv">nameables</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="nv">showPets</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="nv">showPets</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="nv">showNamedList</span> <span class="s">"user-pets"</span> <span class="nv">user</span><span class="nf">.</span><span class="nv">pets</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"> <span class="nv">showSiblings</span> <span class="nf">:</span> <span class="kt">Html</span> <span class="nv">msg</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"> <span class="nv">showSiblings</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="nv">showNamedList</span> <span class="s">"user-siblings"</span> <span class="nv">user</span><span class="nf">.</span><span class="nv">siblings</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"> <span class="kt">Html</span><span class="nf">.</span><span class="nv">div</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"> <span class="p">[</span> <span class="kt">Attributes</span><span class="nf">.</span><span class="nv">class</span> <span class="s">"sidebar"</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"> <span class="p">[</span> <span class="nv">showTime</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"> <span class="p">,</span> <span class="nv">showUser</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"> <span class="p">,</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">h</span><span class="mi">2</span> <span class="p">[]</span> <span class="p">[</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">text</span> <span class="s">"Pets"</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"> <span class="p">,</span> <span class="nv">showPets</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"> <span class="p">,</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">h</span><span class="mi">2</span> <span class="p">[]</span> <span class="p">[</span> <span class="kt">Html</span><span class="nf">.</span><span class="nv">text</span> <span class="s">"Siblings"</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"> <span class="p">,</span> <span class="nv">showSiblings</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"> <span class="p">]</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span></code></pre><p>Can you spot the error? I've re-used the <code>a</code> as a type parameter, but that now means the type checker will insist that the uses of <code>showNamedList</code> are unified to the type in the main signature. It won't be able to do that because presumably the other fields of <code>Pet</code>s and <code>Sibling</code>s are different. Even if they are the same the <code>{a | now : Time}</code> would now be too lenient.</p>
<p>What you really intended here was <code>{ anything | now : Time}</code>, and I think a reasonable way to write that is <code>{ _ | now : Time}</code>.</p>
A very small feature proposal for Elm's type parameters with a motivating situation.Structural Custom Types2022-07-28T16:18:21Zhttps://blog.poleprediction.com/structural-custom-types<p>There have been a few proposals for extensible custom types in Elm, the idea is that they are somewhat analogous to extensible record types. In this post I wish to give a proposal for how to make custom types 'extensible' that could play well with opaque types. The key point is that we need not focus on extensibility so much as the distinction between structural and nominal types.</p>
<p>The slightly longer summary is to say that we can make custom union types structural types. This means that custom union type declarations are actually type aliases, just as record type definitions are in Elm. Two record types can share the same field, and thus it is trivial to make two record types one of which is a sub-type of the other. Similarly if custom union types are <strong>structural</strong>, and their associated definitions type aliases, two such custom union types can share a constructor, and again it is trivial to create two custom union type definitions one of which is a sub-type of the other. With this apparatus we can implement opaque types by choosing to expose a type either nominally or structurally, this gives us opaque types in which the underlying type can be a primitive type, a function type, a record type or a custom union type.</p>
<h2 id="structural-types"><a class="anchor" href="#structural-types" rel="nofollow">#</a> Structural types</h2>
<p>Structural types refers to types in which the type checker looks at the structure of two types to determine whether they are compatible. For example we might say:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Email</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="kt">String</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nv">emailLength</span> <span class="nf">:</span> <span class="kt">Email</span> <span class="nf">-></span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nv">emailLength</span> <span class="nv">email</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"> <span class="kt">String</span><span class="nf">.</span><span class="nv">length</span> <span class="nv">email</span>
</span></span></code></pre><p>This works, because a <code>type alias</code> in Elm doesn't define a new type, it's just a way to refer to the original type. So the Elm type checker can see the structure of the type of the value <code>email</code> is <code>String</code> even though its type is given as <code>Email</code>. Because it can see the structure of this type, it knows that it is type-safe to pass <code>email</code> in as the first argument to <code>String.length : String -> Int</code>.</p>
<p>This works with record types as well, because those are also type aliases. What matters is the structure of the type, not the name that you have given to it. So we can do:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Child</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="p">{</span> <span class="nv">name</span> <span class="nf">:</span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="p">,</span> <span class="nv">age</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Parent</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="p">{</span> <span class="nv">name</span> <span class="nf">:</span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="p">,</span> <span class="nv">age</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nv">areTheyTheSame</span> <span class="nf">:</span> <span class="kt">Parent</span> <span class="nf">-></span> <span class="kt">Child</span> <span class="nf">-></span> <span class="kt">Bool</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nv">areTheyTheSame</span> <span class="nv">parent</span> <span class="nv">child</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="nv">parent</span> <span class="nf">==</span> <span class="nv">child</span>
</span></span></code></pre><p>Again this works, because although we have given different named types to the two arguments, they are structurally the same.</p>
<h2 id="nominal-types"><a class="anchor" href="#nominal-types" rel="nofollow">#</a> Nominal types</h2>
<p>In Elm, custom union types are nominal. Creating a custom union type essentially gives you constructors to create a new type with the given name. There is no other way to create that newly named type. Hence when we wish to check if a value of a given custom union type is compatible, we need only check the name of the type, not its structure.</p>
<p>In Elm you cannot do the following, because you would be defining two <strong>distinct</strong> constructors with the same name:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kt">Handedness</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="nf">=</span> <span class="kt">Left</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="nf">|</span> <span class="kt">Right</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="kr">type</span> <span class="kt">Correctness</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"> <span class="nf">=</span> <span class="kt">Right</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"> <span class="nf">|</span> <span class="kt">Wrong</span>
</span></span></code></pre><p>The problem here is that if you used the <code>Right</code> constructor, the type-checker would have to guess whether you meant to create a value of type <code>Handedness</code> or a value of type <code>Correctness</code>.</p>
<h2 id="making-custom-types-structural"><a class="anchor" href="#making-custom-types-structural" rel="nofollow">#</a> Making Custom types structural</h2>
<p>The key difference between record and custom types in Elm is that record types are structural, whereas custom types are nominal. Instead of a custom <code>type</code> declaration defining constructors, which are essentially functions from the constructor arguments to the nominal custom type, we can instead represent the return type of a constructor to be exactly the singleton custom type of the constructor used. This would mean we could then define multiple functions that could accept such a constructed value.</p>
<p>So, we can construct a value without defining a custom <code>type</code>. Here we need some extra syntax to refer to a custom union structural type. I'm going to use square brackets here, this is analogous to using curly brackets for record types. So just as a record type is written as <code>{ field-name : field-type (, field-name : field-type)* }</code> we will write a structural custom type using <code>[ constructor-name (type-argument)* (| constructor-name (type-argument)*)*]</code> if you are comfortable with pseudo-parser syntax if not the general patterns are:</p>
<pre><code>{ field-name1 : field-type1
, field-name2 : field-type2
, ...
}
[ constructor-name1 : constructor-type1a constructor-type1b ...
| constructor-name2 : constructor-type2a constructor-type2b ...
]
</code></pre>
<p>With that syntax we can define a value with a structural custom union type, the simplest being:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">value</span> <span class="nf">:</span> <span class="p">[</span> <span class="kt">Duck</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">value</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="kt">Duck</span>
</span></span></code></pre><p>Now we can define a function that accepts some set of constructors:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">isDuck</span> <span class="nf">:</span> <span class="p">[</span> <span class="kt">Duck</span> <span class="nf">|</span> <span class="kt">Swan</span> <span class="nf">|</span> <span class="kt">Goose</span> <span class="p">]</span> <span class="nf">-></span> <span class="kt">Bool</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">isDuck</span> <span class="nv">bird</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">case</span> <span class="nv">bird</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="kt">Duck</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kt">Swan</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="kt">False</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="kt">Goose</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kt">False</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nv">isDuck</span> <span class="kt">Duck</span> <span class="c1">-- type checks fine.</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nv">isDuck</span> <span class="kt">Chicken</span> <span class="c1">-- type error</span>
</span></span></code></pre><p>If we prefer, we can give a <code>type alias</code> to the <code>[ Duck | Swan | Goose ]</code> type:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">PondBird</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="p">[</span> <span class="kt">Duck</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="nf">|</span> <span class="kt">Swan</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"> <span class="nf">|</span> <span class="kt">Goose</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"> <span class="p">]</span>
</span></span></code></pre><p>But this has the same effect as a <code>type alias</code> for a record type, namely that it is just a convenient way to refer to this type.</p>
<p>In the same way that you can constrain a polymorphic variant to a more restrictive type than would otherwise be inferred, you could do the same with structural custom union type:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">noIntegers</span> <span class="nf">:</span> <span class="kt">List</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">noIntegers</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nv">duck</span> <span class="nf">:</span> <span class="kt">PondBird</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nv">duck</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"> <span class="kt">Duck</span>
</span></span></code></pre><p>Here, <code>PondBird</code> is more restrictive than <code>[Duck]</code> in the sense that you could pass that value into fewer functions. There are some functions that would accept a value of type <code>[Duck]</code> that would not accept a value of type <code>PondBird</code>, for example:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">isDuck</span> <span class="nf">:</span> <span class="p">[</span> <span class="kt">Duck</span> <span class="nf">|</span> <span class="kt">Swan</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">isDuck</span> <span class="nv">bird</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="kr">case</span> <span class="nv">bird</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"> <span class="kt">Duck</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"> <span class="kt">Swan</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"> <span class="kt">False</span>
</span></span></code></pre><h3 id="sub-typing-with-structural-types"><a class="anchor" href="#sub-typing-with-structural-types" rel="nofollow">#</a> Sub-typing with structural types</h3>
<p>An important difference between structural record types and structural custom union types is the way sub-typing works. A record type <code>A</code> is a sub-type of a record-typ <code>B</code>, if, for all fields in <code>B, f : C</code> there is a field in <code>A, f : D</code> such that <code>D</code> is a sub-type of <code>C</code>. What this means in practice is that a function that accepts type <code>B</code> is safe to accept type <code>A</code>, because all values of <code>A</code> have at least the same shape as <code>B</code>, perhaps more which is ignored by the function. A simple concrete example helps to make this a little less abstract:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">sayQuack</span> <span class="nf">:</span> <span class="p">{</span> <span class="nv">a</span> <span class="nf">|</span> <span class="nv">quack</span> <span class="nf">:</span> <span class="kt">String</span> <span class="p">}</span> <span class="nf">-></span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">sayQuack</span> <span class="nv">duck</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="nv">duck</span><span class="nf">.</span><span class="nv">quack</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nv">sayQuack</span> <span class="p">{</span> <span class="nv">quack</span> <span class="nf">=</span> <span class="s">"ACK"</span><span class="p">,</span> <span class="nv">age</span> <span class="nf">=</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">colour</span> <span class="nf">=</span> <span class="kt">Brown</span> <span class="p">}</span>
</span></span></code></pre><p>The value <code>{ quack = "ACK", age = 2, colour = Brown }</code> has additional fields, but that's okay because <code>sayQuack</code> doesn't care what <strong>additional</strong> fields the argument has, only that it has the field <code>quack : String</code>. It's a slight quirk of Elm that you must explicitly declar the argument type of <code>sayQuack</code> as extensible, but that won't concern us in this post.</p>
<p>In contrast, a custom union type <code>A</code> is a sub-type of <code>B</code>, if for all constructors in <code>A, C : D</code> there is an equivalent one in <code>B</code>. Again a simple example helps.</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">speak</span> <span class="nf">:</span> <span class="p">[</span> <span class="kt">Dog</span> <span class="nf">|</span> <span class="kt">Cat</span> <span class="nf">|</span> <span class="kt">Budgie</span> <span class="p">]</span> <span class="nf">-></span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">speak</span> <span class="nv">animal</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">case</span> <span class="nv">animal</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="kt">Dog</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="s">"woof"</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kt">Cat</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="s">"miaow"</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="kt">Budgie</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="s">"tweet"</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nv">getPet</span> <span class="nf">:</span> <span class="kt">Bool</span> <span class="nf">-></span> <span class="p">[</span> <span class="kt">Dog</span> <span class="nf">|</span> <span class="kt">Cat</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nv">getPet</span> <span class="nv">isDogLover</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="kr">case</span> <span class="nv">isDogLover</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="kt">True</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="kt">Dog</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kt">False</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kt">Cat</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nv">firstPet</span> <span class="nf">:</span> <span class="p">[</span> <span class="kt">Dog</span> <span class="nf">|</span> <span class="kt">Cat</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="nv">firstPet</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"> <span class="nv">getPet</span> <span class="kt">True</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="nv">speak</span> <span class="nv">firstPet</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span></code></pre><p>The function <code>speak</code> is called with a value of type <code>[ Dog | Cat ]</code> which is a sub-type of <code>[Dog | Cat | Budgie]</code>. This is perfectly type-safe because the type checker knows each possibility will be handled correctly, but I could not call this with a value of type <code>[ Dog | Cat | Mouse]</code> or <code>[ Dog | Cat | Budgie | Mouse]</code> or even simply <code>[ Mouse ]</code>. Because the case of <code>Mouse</code> would not be handled by the function <code>speak</code>.</p>
<h2 id="exporting-importing-and-opaque-types"><a class="anchor" href="#exporting-importing-and-opaque-types" rel="nofollow">#</a> Exporting, Importing, and opaque types</h2>
<p>If we have both record and custom union types represented structurally what does this mean for opaque types? Currently in Elm a custom union type can be exposed using the <code>(..)</code> suffix to mean "and all the constructors are exposed". Exporting a type <code>A</code> with <code>A(..)</code> means that outside the defining module, a value of type <code>A</code> can be constructed or deconstructed/inpsected. Sometimes you want only the defining module to be able to create or inspect (ie. constructor or deconstruct/pattern match), in this way you can ensure some property/properties hold true of all values of <code>A</code>. For example you might define a value of type <code>Email</code> and then only allow the construction of such values via a function that checks whether the given string is indeed a valid email address. Once you do that you know all values of type <code>Email</code> do indeed contain a valid email address. This pattern is known as <em>opaque</em> types.</p>
<p>I would like to propose that we can expose a type <strong>nominally</strong> or <strong>structurally</strong>. If a type is exposed <strong>structurally</strong> then any code outside of that module can create or inspect a value of that type. In particular the type checker will 'know' the structure of that type outside of its defining module, and can type check code knowing the structure of that type. However, if a type is exposed <strong>nominally</strong> then it's not possible for code outside of that module to know the structure of that type at all. Therefore the only way to create or inspect a value of that type is via the module in which it is defined.</p>
<p>This means that we gain opaque types, not just for custom union types, but for record types and primitive types as well. For example, you could have a module:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">module </span><span class="nc">Email</span> <span class="nv">exposing</span> <span class="p">(</span><span class="kt">Email</span><span class="p">,</span> <span class="nv">create</span><span class="p">,</span> <span class="nv">format</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Email</span> <span class="nf">=</span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nv">create</span> <span class="nf">:</span> <span class="kt">String</span> <span class="nf">-></span> <span class="kt">Maybe</span> <span class="kt">Email</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nv">create</span> <span class="nv">s</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="c1">-- This works inside this module, because inside this module, </span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="c1">-- the Email type is **structural**. </span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="c1">-- But you couldn't do this outide this module, </span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="c1">-- because there the Email type is **nominal** </span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="c1">-- and so the type-checker has no way of unifying the String type of 's' </span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="c1">-- with the 'Email' in the return type.</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="c1">-- Obviously we could do stronger validation</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="kr">case</span> <span class="kt">String</span><span class="nf">.</span><span class="nv">contains</span> <span class="s">"@"</span> <span class="nv">s</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="kt">True</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kt">Just</span> <span class="nv">s</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kt">False</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="kt">Nothing</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="nv">format</span> <span class="nf">:</span> <span class="kt">Email</span> <span class="nf">-></span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nv">format</span> <span class="nv">email</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="c1">-- Similarly this works inside this module but not outside it.</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"> <span class="nv">s</span>
</span></span></code></pre><p>Now the nice thing is that we could change the representation of <code>Email</code>, to be a record type:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">module </span><span class="nc">Email</span> <span class="nv">exposing</span> <span class="p">(</span><span class="kt">Email</span><span class="p">,</span> <span class="nv">create</span><span class="p">,</span> <span class="nv">format</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Email</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="p">{</span> <span class="nv">username</span> <span class="nf">:</span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="p">,</span> <span class="nv">domain</span> <span class="nf">:</span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nv">create</span> <span class="nf">:</span> <span class="kt">String</span> <span class="nf">-></span> <span class="kt">Maybe</span> <span class="kt">Email</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nv">create</span> <span class="nv">s</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kr">case</span> <span class="kt">String</span><span class="nf">.</span><span class="nv">split</span> <span class="s">"@"</span> <span class="nv">s</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="p">[</span> <span class="nv">left</span><span class="p">,</span> <span class="nv">right</span> <span class="p">]</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="kt">Just</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">{</span> <span class="nv">username</span> <span class="nf">=</span> <span class="nv">left</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="p">,</span> <span class="nv">domain</span> <span class="nf">=</span> <span class="nv">right</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="nv">_</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kt">Nothing</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="nv">format</span> <span class="nf">:</span> <span class="kt">Email</span> <span class="nf">-></span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nv">format</span> <span class="nv">email</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="kt">String</span><span class="nf">.</span><span class="nv">concat</span> <span class="p">[</span> <span class="nv">email</span><span class="nf">.</span><span class="nv">username</span><span class="p">,</span> <span class="s">"@"</span><span class="p">,</span> <span class="nv">email</span><span class="nf">.</span><span class="nv">domain</span> <span class="p">]</span>
</span></span></code></pre><p>The great thing about this, in contrast with an Elm module, would be that outside of this module, a value of
type <code>Email</code> could not be created or inspected, because outside this module the <code>Email</code> type is <strong>nominal</strong>. That is true whether the underyling type is a <code>String</code> or a record type, because we are choosing to export the <strong>nominally</strong>.</p>
<p>Finally we can do the same with a custom union type:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">module </span><span class="nc">Email</span> <span class="nv">exposing</span> <span class="p">(</span><span class="kt">Email</span><span class="p">,</span> <span class="nv">create</span><span class="p">,</span> <span class="nv">format</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Email</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="p">[</span> <span class="kt">Email</span> <span class="nv">username</span> <span class="nv">domain</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nv">create</span> <span class="nf">:</span> <span class="kt">String</span> <span class="nf">-></span> <span class="kt">Maybe</span> <span class="kt">Email</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nv">create</span> <span class="nv">s</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="kr">case</span> <span class="kt">String</span><span class="nf">.</span><span class="nv">split</span> <span class="s">"@"</span> <span class="nv">s</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="p">[</span> <span class="nv">left</span><span class="p">,</span> <span class="nv">right</span> <span class="p">]</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">Just</span> <span class="p">(</span><span class="kt">Email</span> <span class="nv">username</span> <span class="nv">domain</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="nv">_</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="kt">Nothing</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nv">format</span> <span class="nf">:</span> <span class="kt">Email</span> <span class="nf">-></span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nv">format</span> <span class="nv">email</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kr">case</span> <span class="nv">email</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="kt">Email</span> <span class="nv">username</span> <span class="nv">domain</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="kt">String</span><span class="nf">.</span><span class="nv">concat</span> <span class="p">[</span> <span class="nv">username</span><span class="p">,</span> <span class="s">"@"</span><span class="p">,</span> <span class="nv">domain</span> <span class="p">]</span>
</span></span></code></pre><p>Again, because the <code>Email</code> type is exposed nominally, there is no way to create or inspect a value of type <code>Email</code> outside of this module.</p>
<p>We can represent exporting/importing nominally/structurally using the existing Elm syntax, or come up with new keywords or syntax, for example we could use:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kn">module </span><span class="nc">Email</span> <span class="p">(</span><span class="kt">Email</span><span class="p">)</span> <span class="c1">-- means expose 'Email' nominally</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">module </span><span class="nc">Email</span> <span class="p">(</span><span class="kt">Email</span><span class="nf">(..)</span><span class="p">)</span> <span class="c1">-- means exposing 'Email' structurally</span>
</span></span></code></pre><p>Or we could simply use two new keywords</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kn">module </span><span class="nc">Email</span> <span class="p">(</span><span class="kt">Email</span> <span class="nv">nominally</span><span class="p">)</span> <span class="c1">-- means expose 'Email' nominally</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">module </span><span class="nc">Email</span> <span class="p">(</span><span class="kt">Email</span> <span class="nv">structurally</span><span class="p">)</span> <span class="c1">-- means exposing 'Email' structurally</span>
</span></span></code></pre><p>Or a single new keyword with a default:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kn">module </span><span class="nc">Email</span> <span class="p">(</span><span class="kt">Email</span><span class="p">)</span> <span class="c1">-- means expose 'Email' nominally</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kn">module </span><span class="nc">Email</span> <span class="p">(</span><span class="kt">Email</span> <span class="nv">structurally</span><span class="p">)</span> <span class="c1">-- means exposing 'Email' structurally</span>
</span></span></code></pre><p>Many other possible syntaxes are available, this would be strong bike-shed material.</p>
<h2 id="is-this-enough-for-extensibility"><a class="anchor" href="#is-this-enough-for-extensibility" rel="nofollow">#</a> Is this enough for extensibility</h2>
<p>Note that we haven't defined any syntax for extending a custom union type. But just as structural record types can contain the same fields, so could structural custom union types contain the same constructors.</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">BusinessDay</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="p">[</span> <span class="kt">Monday</span> <span class="nf">|</span> <span class="kt">Tuesday</span> <span class="nf">|</span> <span class="kt">Wednesday</span> <span class="nf">|</span> <span class="kt">Thursday</span> <span class="nf">|</span> <span class="kt">Friday</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">WeekDay</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"> <span class="p">[</span> <span class="kt">Monday</span> <span class="nf">|</span> <span class="kt">Tuesday</span> <span class="nf">|</span> <span class="kt">Wednesday</span> <span class="nf">|</span> <span class="kt">Thursday</span> <span class="nf">|</span> <span class="kt">Friday</span> <span class="nf">|</span> <span class="kt">Saturday</span> <span class="nf">|</span> <span class="kt">Sunday</span> <span class="p">]</span>
</span></span></code></pre><p>Any function defined over <code>Weekday</code> would accept a value of type <code>BusinessDay</code> since <code>BusinessDay</code> is a sub-type of <code>Weekday</code>.</p>
<p>It isn't required, but we could of course imagine some kind of extension syntax:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl">
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">BusinessDay</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="p">[</span> <span class="kt">Monday</span> <span class="nf">|</span> <span class="kt">Tuesday</span> <span class="nf">|</span> <span class="kt">Wednesday</span> <span class="nf">|</span> <span class="kt">Thursday</span> <span class="nf">|</span> <span class="kt">Friday</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">WeekDay</span> <span class="nv">extends</span> <span class="kt">BusinessDay</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"> <span class="p">[</span> <span class="kt">Saturday</span> <span class="nf">|</span> <span class="kt">Sunday</span> <span class="p">]</span>
</span></span></code></pre><h2 id="catch-all-patterns"><a class="anchor" href="#catch-all-patterns" rel="nofollow">#</a> Catch-all patterns</h2>
<p>There is a slight issue with the catch-all pattern <code>_ -></code> related to the way in which a case expression would be typed.</p>
<p>A case expression such as:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">available</span> <span class="nv">day</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="kr">case</span> <span class="nv">day</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kt">Monday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="kt">Tuesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="kt">Wednesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kt">Thursday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">Friday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="kt">True</span>
</span></span></code></pre><p>Cannot have the <strong>inferred</strong> type of <code>BusinessDay -> Bool</code>, because those five constructors could be defined in multiple custom union types, and in any case custom union types would only be aliases. Instead this would type check as: <code>[ Monday | Tuesday | Wednesday | Thursday | Friday] -> Bool</code>. You can of course give a type signature.</p>
<p>Now, what about if we used the catch-all pattern?</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">available</span> <span class="nv">day</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="kr">case</span> <span class="nv">day</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kt">Monday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="kt">Tuesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="kt">Wednesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="nv">_</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">False</span>
</span></span></code></pre><p>Now we need some new type syntax to write down this type. This is something like:
<code>[ Monday | Tuesday | Wednesday | a ] -> Bool</code> where <code>a</code> here stands for "any other set of constructors that don't contain 'Monday', 'Tuesday', or 'Wednesday'". Note that we cannot write this as simply <code>a -> Bool</code> because the value must be a custom union type, <strong>and</strong> it won't accept a value of say <code>Wednesday Int</code>. Note though that here we can simply add a type signature to get the type that perhaps is really wanted:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">available</span> <span class="nf">:</span> <span class="kt">BusinessDay</span> <span class="nf">-></span> <span class="kt">Bool</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">available</span> <span class="nv">day</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">case</span> <span class="nv">day</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="kt">Monday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kt">Tuesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="kt">Wednesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="nv">_</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">False</span>
</span></span></code></pre><p>This because <code>BusinessDay</code> is a synonym for <code>[ Monday | Tuesday | Wednesday | Thursday | Friday]</code> and that is a more restrictive type than <code>[ Monday | Tuesday | Wednesday | a]</code>.</p>
<h2 id="exhaustive-pattern-testing"><a class="anchor" href="#exhaustive-pattern-testing" rel="nofollow">#</a> Exhaustive pattern testing</h2>
<p>Treating custom union types in this way means that we do not get exhaustive pattern testing. Because the case expression itself is always exhaustive for some custom union type.</p>
<p>Of course exhaustive pattern testing remains unaffected for primitive types, such as String and Int.
So for example:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">available</span> <span class="nv">day</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="kr">case</span> <span class="nv">day</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="kt">Monday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"> <span class="kt">Tuesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"> <span class="kt">Wednesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"> <span class="kt">True</span>
</span></span></code></pre><p>Is a perfectly valid function. It has type <code>[ Monday | Tuesday | Wednesday ] -> Bool</code>. This is all fine, if we want to make sure that our function can accept any <code>BusinessDay</code> value, then we should add a type signature:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">available</span> <span class="nf">:</span> <span class="kt">BusinessDay</span> <span class="nf">-></span> <span class="kt">Bool</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">available</span> <span class="nv">day</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="kr">case</span> <span class="nv">day</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"> <span class="kt">Monday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"> <span class="kt">Tuesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"> <span class="kt">True</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"> <span class="kt">Wednesday</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"> <span class="kt">True</span>
</span></span></code></pre><p>In which case we would not get a non-exhaustive pattern match failure <em>per se</em>, but we would get a type error stating that the type of the expression <code>[ Monday | Tuesday | Wednesay ] -> Bool</code> is not compatible with the signature type <code>BusinessDay -> Bool</code>. The error message could explain why.</p>
<p>Alternatively the signature could be omitted in which case it is a perfectly valid function, but we could get an error if we ever attempted to call this function with a value that might be of a <code>Thursday</code> or <code>Friday</code> constructed value. If we never do that, then there is nothing wrong.</p>
<h2 id="recursive-extensive-types"><a class="anchor" href="#recursive-extensive-types" rel="nofollow">#</a> Recursive Extensive Types</h2>
<p>This is perfectly enough for recursive extensive types. We can define a simple expression syntax for doing integer arithmetic. The <code>Apply</code> constructor takes the name of a function, such as <code>sin</code> or <code>add</code> and applies that to the list of argument expressions given.</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kt">Expr</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="nf">=</span> <span class="kt">IntLiteral</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="nf">|</span> <span class="kt">Apply</span> <span class="kt">String</span> <span class="p">(</span><span class="kt">List</span> <span class="kt">Expr</span><span class="p">)</span>
</span></span></code></pre><p>Now we could define a slightly more complicated expression language to parse into:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kt">ParseExpr</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="nf">=</span> <span class="kt">IntLiteral</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="nf">|</span> <span class="kt">Apply</span> <span class="kt">String</span> <span class="p">(</span><span class="kt">List</span> <span class="kt">ParseExpr</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"> <span class="nf">|</span> <span class="kt">Binop</span> <span class="kt">ParseExpr</span> <span class="kt">String</span> <span class="kt">ParseExpr</span>
</span></span></code></pre><p>Now we can defined a <code>simplify</code> function which converts a parsed expression into the simplier expression kind:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">simplify</span> <span class="nf">:</span> <span class="kt">ParseExpr</span> <span class="nf">-></span> <span class="kt">Expr</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">simplify</span> <span class="nv">parsed</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">case</span> <span class="nv">parsed</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="kt">IntLiteral</span> <span class="nv">i</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="kt">IntLiteral</span> <span class="nv">i</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kt">Apply</span> <span class="nv">name</span> <span class="nv">args</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="kt">List</span><span class="nf">.</span><span class="nv">map</span> <span class="nv">simplify</span> <span class="nv">args</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="nf">|></span> <span class="kt">Apply</span> <span class="nv">name</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kt">Binop</span> <span class="nv">left</span> <span class="nv">name</span> <span class="nv">right</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">Apply</span> <span class="nv">name</span> <span class="p">[</span> <span class="nv">simplify</span> <span class="nv">left</span><span class="p">,</span> <span class="nv">simplify</span> <span class="nv">right</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span></code></pre><p>Side note, it would be <strong>possible</strong> but <strong>difficult</strong> for the type-checker to allow <code>IntLiteral _ -> parsed</code> here.</p>
<p>You could already do this with normal nominal custom types in Elm. You would have to give the two expression types different constructors which you could do either by giving them explicitly different names, or by defining each type in a different module. However with this approach you can define a function that operates over both expression types, such as:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">usedFunctions</span> <span class="nf">:</span> <span class="kt">ParseExpr</span> <span class="nf">-></span> <span class="kt">Set</span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">usedFunctions</span> <span class="nv">expr</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">case</span> <span class="nv">expr</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="kt">IntLiteral</span> <span class="nv">_</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="kt">Set</span><span class="nf">.</span><span class="nv">empty</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kt">Apply</span> <span class="nv">name</span> <span class="nv">args</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="kt">List</span><span class="nf">.</span><span class="nv">map</span> <span class="nv">usedFunctions</span> <span class="nv">args</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="nf">|></span> <span class="kt">List</span><span class="nf">.</span><span class="nv">foldl</span> <span class="kt">Set</span><span class="nf">.</span><span class="nv">union</span> <span class="kt">Set</span><span class="nf">.</span><span class="nv">empty</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="nf">|></span> <span class="kt">Set</span><span class="nf">.</span><span class="nv">insert</span> <span class="nv">name</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">Binop</span> <span class="nv">left</span> <span class="nv">name</span> <span class="nv">right</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">Set</span><span class="nf">.</span><span class="nv">singleton</span> <span class="nv">name</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="nf">|></span> <span class="kt">Set</span><span class="nf">.</span><span class="nv">union</span> <span class="p">(</span><span class="nv">usedFunctions</span> <span class="nv">left</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="nf">|></span> <span class="kt">Set</span><span class="nf">.</span><span class="nv">union</span> <span class="p">(</span><span class="nv">usedFunction</span> <span class="nv">right</span><span class="p">)</span>
</span></span></code></pre><p>Because <code>Expr</code> is a sub-type of <code>ParseExpr</code> you can call <code>usedFunctions</code> with a value of type <code>Expr</code>.</p>
<h2 id="possible-drawbacks"><a class="anchor" href="#possible-drawbacks" rel="nofollow">#</a> Possible drawbacks</h2>
<h3 id="same-constructor-in-two-types"><a class="anchor" href="#same-constructor-in-two-types" rel="nofollow">#</a> Same constructor in two types</h3>
<p>One could imagine defining two types as such:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Handedness</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="p">[</span> <span class="kt">Left</span> <span class="nf">|</span> <span class="kt">Right</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Correctness</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"> <span class="p">[</span> <span class="kt">Right</span> <span class="nf">|</span> <span class="kt">Wrong</span> <span class="p">]</span>
</span></span></code></pre><p>This could lead to a situation in which you incorrectly passed a value that you intended to be of type <code>Handedness</code> into a function that expects a value of type <code>Correctness</code>. Note however, that although you intended the type to be of <code>Handedness</code> it would only pass type checking if it was really of type <code>[Right]</code>.</p>
<p>The implications of this would need to be considered quite thoroughly.</p>
<h3 id="compilation"><a class="anchor" href="#compilation" rel="nofollow">#</a> Compilation</h3>
<p>At the moment custom union types can be compiled using integers to represent the tags, and the compilation can still happen locally. If you wish to use integers for tags, when a tag can be used in many different types (including the strutural argument types to functions), then there would have to be a whole program optimisation/transformation, which gives a unique integer to each tag.</p>
SimulatedHttp, functors and sed2022-07-28T16:18:18Zhttps://blog.poleprediction.com/poor-persons-functors<p>In this post I'm going to describe an awkwardness encountered when using <a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest/" rel="nofollow">elm-program-test</a> to simulate HTTP events in the program being tested. I will then describe ML functors, a feature of the SML/Ocaml module system and show how these would solve the awkwardness. I'll then show how it's pretty simple to hack together a "poor-person's-functor" and use that to solve the aforementioned awkwardness.</p>
<h3 id="an-awkwardness-when-simulating-http-for-testing"><a class="anchor" href="#an-awkwardness-when-simulating-http-for-testing" rel="nofollow">#</a> An awkwardness when simulating HTTP for testing</h3>
<p>If you haven't used <a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest/" rel="nofollow">elm-program-test</a> to test an entire Elm program I recommend trying it out. I've found that it not only does the obvious part of helping to build robust tests for Elm programs, but also helps me structure the Elm program in a way that is better for testing, but also just generally better.</p>
<p>In order to use <a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest/" rel="nofollow">elm-program-test</a> you have to write an auxiliary <code>update</code> function for your program that returns a <code>ProgramTest.SimulatedEffect</code> rather than a <code>Cmd</code>. In order to do this without completely re-writing the program (and thus more or less negating the point of the testing), <a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest/" rel="nofollow">elm-program-test</a> advises you to re-write your <code>update</code> function so that it returns a custom <code>Effect</code> type. Then in the real program you translate that <code>Effect</code> into a <code>Cmd Msg</code>, and for the tests you translate it into a <code>ProgramTest.SimulatedEffect</code>. This is best described with some Elm types:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">type</span> <span class="kt">Effect</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"> <span class="nf">=</span> <span class="kt">GetPosts</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="nf">|</span> <span class="kt">GetPostComments</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nv">update</span> <span class="nf">:</span> <span class="kt">Msg</span> <span class="nf">-></span> <span class="kt">Model</span> <span class="nf">-></span> <span class="p">(</span><span class="kt">Model</span><span class="p">,</span> <span class="kt">Effect</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nv">perform</span> <span class="nf">:</span> <span class="kt">Model</span> <span class="nf">-></span> <span class="kt">Effect</span> <span class="nf">-></span> <span class="kt">Cmd</span> <span class="kt">Msg</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="nv">simulate</span> <span class="nf">:</span> <span class="kt">Model</span> <span class="nf">-></span> <span class="kt">Effect</span> <span class="nf">-></span> <span class="kt">SimulatedEffect</span> <span class="kt">Msg</span>
</span></span></code></pre><p>Now, helpfully, the modules in <a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest/" rel="nofollow">elm-program-test</a> for creating simulated effects, tend to have the same API as their equivalent modules for real commands (in some cases they are incomplete as yet). For example <code>SimulatedEffect.Http</code> has the same API as <code>Http</code> from <code>elm/http</code>. This means it's relatively <strong>trivial</strong> to write the <code>perform</code> and <code>simulate</code> modules. In fact, they <strong>can</strong> be, essentially identical:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">perform</span> <span class="nf">:</span> <span class="kt">Model</span> <span class="nf">-></span> <span class="kt">Effect</span> <span class="nf">-></span> <span class="kt">Cmd</span> <span class="kt">Msg</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">perform</span> <span class="nv">model</span> <span class="nv">effect</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">case</span> <span class="nv">effect</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="kt">GetPosts</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="kt">Http</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">Http</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">decoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kt">GetPostComments</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">Http</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">Http</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostCommentsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">commentDecoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nv">simulate</span> <span class="nf">:</span> <span class="kt">Model</span> <span class="nf">-></span> <span class="kt">Effect</span> <span class="nf">-></span> <span class="kt">SimulatedEffect</span> <span class="kt">Msg</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nv">simulate</span> <span class="nv">model</span> <span class="nv">effect</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kr">case</span> <span class="nv">effect</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="kt">GetPosts</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="kt">SimulatedHttp</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">SimulatedHttp</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">decoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"> <span class="kt">GetPostComments</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"> <span class="kt">SimulatedHttp</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">SimulatedHttp</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostCommentsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">commentDecoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"> <span class="p">}</span>
</span></span></code></pre><p>In this very simple example the two functions are essentially identical aside from using the <code>Http</code> and <code>SimulatedEffect.Http</code> modules. This is generally true, even although the logic might be much more complicated. For example, you may have to check if the user is logged in, and if so send an authenticated request. Additionally, <strong>some</strong> effects, might not be simulated at all, for example Dom effects such as focusing are not yet simulated (though you could fake with a simulated port call). Anyway the point is, there ends up being quite a bit of duplicated code.</p>
<h3 id="ml-functors"><a class="anchor" href="#ml-functors" rel="nofollow">#</a> ML functors</h3>
<p>ML has a pretty powerful module system. In truth, although it is powerful, even when using O'caml to develop a compiler, I still very rarely found that I needed to reach for the full power of the module system. Functors just didn't come up very often. What are functors? They are essentially the equivalent to a module, that a function is to a value. So you can think of them as functions over modules. So you can write a module <code>A</code>, which takes another module <code>B</code> as an argument. The argument is specified as a module signature. This means that <code>A</code> is now a functor. You can apply the functor <code>A</code> to more than one other module as long as you have multiple modules that satisfy the signature <code>B</code>. The normal use cases for functors are pretty similar to the use cases for Haskell's type classes.</p>
<p>A common example is a dictionary/set, here is such an example written in a fantasy version of Elm with functors (and multiple modules within a single file):</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">signature</span> <span class="kt">Compare</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="kr">type</span> <span class="kt">Item</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="nv">compare</span> <span class="nf">:</span> <span class="kt">Item</span> <span class="nf">-></span> <span class="kt">Item</span> <span class="nf">-></span> <span class="kt">Order</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nv">functor</span> <span class="kt">Set</span> <span class="p">(</span><span class="kt">Item</span> <span class="nf">:</span> <span class="kt">Compare</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kr">type</span> <span class="kt">Set</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="nf">=</span> <span class="kt">Empty</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="nf">|</span> <span class="kt">Node</span> <span class="kt">Set</span> <span class="kt">Item</span> <span class="kt">Set</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="nv">empty</span> <span class="nf">:</span> <span class="kt">Set</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="nv">empty</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">Empty</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="nv">add</span> <span class="nf">:</span> <span class="kt">Item</span><span class="nf">.</span><span class="kt">Item</span> <span class="nf">-></span> <span class="kt">Set</span> <span class="nf">-></span> <span class="kt">Set</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="nv">add</span> <span class="nv">item</span> <span class="nv">currentSet</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="kr">case</span> <span class="nv">currentSet</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="kt">Empty</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kt">Node</span> <span class="kt">Empty</span> <span class="nv">item</span> <span class="kt">Empty</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kt">Node</span> <span class="nv">left</span> <span class="nv">nodeItem</span> <span class="nv">right</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="kr">case</span> <span class="kt">Item</span><span class="nf">.</span><span class="nv">compare</span> <span class="nv">item</span> <span class="nv">nodeItem</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="kt">LT</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"> <span class="kt">Node</span> <span class="p">(</span><span class="nv">add</span> <span class="nv">item</span> <span class="nv">left</span><span class="p">)</span> <span class="nv">nodeItem</span> <span class="nv">right</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"> <span class="kt">GT</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="kt">Node</span> <span class="nv">left</span> <span class="nv">nodeItem</span> <span class="p">(</span><span class="nv">add</span> <span class="nv">item</span> <span class="nv">right</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"> <span class="kt">EQ</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"> <span class="nv">currentSet</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"> <span class="nf">...</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="kn">module </span><span class="nc">IntCompare</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"> <span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Item</span> <span class="nf">=</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"> <span class="nv">compare</span> <span class="nf">:</span> <span class="kt">Item</span> <span class="nf">-></span> <span class="kt">Item</span> <span class="nf">-></span> <span class="kt">Order</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"> <span class="nv">compare</span> <span class="nf">=</span> <span class="kt">Core</span><span class="nf">.</span><span class="nv">compare</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="kn">module </span><span class="nc">IntSet</span> <span class="nf">=</span> <span class="kt">Set</span><span class="p">(</span><span class="kt">IntCompare</span><span class="p">)</span>
</span></span></code></pre><p>Obviously a real implementation would have the other common <code>Set</code> functions, and you could use this to make <code>Set</code>s of things that aren't in Elm's <code>comparable</code> type class, by actually writing your own <code>compare</code> function.
You can read the documentation for <a href="https://ocaml.org/manual/moduleexamples.html" rel="nofollow">O'caml's module system here</a></p>
<h3 id="effects-simulatedeffects-and-functors"><a class="anchor" href="#effects-simulatedeffects-and-functors" rel="nofollow">#</a> Effects, SimulatedEffects, and Functors</h3>
<p>Hopefully it is pretty obvious how this solves our awkwardness with requests and simulated requests. Because the SimulatedHttp module from <a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest/" rel="nofollow">elm-program-test</a> has the same API (or signature) as the standard Http module, you can easily write a <strong>functor</strong> that, given either of those two modules, produces a <code>Perform</code> module that has the correct type. Again using our fantasy version of Elm with functors:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">type</span> <span class="kt">Effect</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="nf">=</span> <span class="kt">GetPosts</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="nf">|</span> <span class="kt">GetPostComments</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nv">update</span> <span class="nf">:</span> <span class="kt">Msg</span> <span class="nf">-></span> <span class="kt">Model</span> <span class="nf">-></span> <span class="p">(</span><span class="kt">Model</span><span class="p">,</span> <span class="kt">Effect</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="kn">module </span><span class="nc">InterpretEffects</span><span class="p">(</span><span class="kt">Http</span> <span class="nf">:</span> <span class="nf"><</span><span class="nv">suitable</span><span class="nf">-</span><span class="nv">signature</span><span class="nf">></span><span class="p">,</span> <span class="kt">Result</span> <span class="nf">:</span> <span class="nf"><</span><span class="nv">signature</span> <span class="nv">with</span> <span class="nv">return</span> <span class="kr">type</span><span class="nf">></span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="nv">perform</span> <span class="nf">:</span> <span class="kt">Model</span> <span class="nf">-></span> <span class="kt">Effect</span> <span class="nf">-></span> <span class="kt">Return</span><span class="nf">.</span><span class="kt">Cmd</span> <span class="kt">Msg</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="nv">perform</span> <span class="nv">model</span> <span class="nv">effect</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kr">case</span> <span class="nv">effect</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">GetPosts</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">Http</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">Http</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">decoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="kt">GetPostComments</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kt">Http</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">Http</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostCommentsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">commentDecoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="kn">module </span><span class="nc">Perform</span> <span class="nf">=</span> <span class="kt">InterpretEffects</span><span class="p">(</span><span class="kt">Http</span><span class="p">,</span> <span class="p">(</span> <span class="kt">Cmd</span> <span class="p">)</span> <span class="p">)</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="kn">module </span><span class="nc">Simulate</span> <span class="nf">=</span> <span class="kt">InterpretEffects</span><span class="p">(</span><span class="kt">SimulatedEffect</span><span class="nf">.</span><span class="kt">Http</span><span class="p">,</span> <span class="p">(</span> <span class="kt">SimulatedEffect</span> <span class="p">))</span>
</span></span></code></pre><p>I've had to fudge this a bit because the return types of both modules (<code>Cmd</code> and <code>SimulatedEffect</code>) are not defined in the respective <code>Http</code> module, but you get the idea.</p>
<h3 id="elm-doesnt-have-functors"><a class="anchor" href="#elm-doesnt-have-functors" rel="nofollow">#</a> Elm doesn't have functors</h3>
<p>However, it's pretty simple to fake them with the use of the unix program <code>sed</code>. Just write the <code>Perform</code> module as you would, and then copy into a Simulate module, whilst modifying only the parts that change. The use of an <code>import as</code> can make this especially doable. First the <code>Perform</code> module, remember, this is translating our <code>Effect</code> custom type into the Elm's standard library <code>Cmd</code> type:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">module </span><span class="nc">Perform</span> <span class="nv">exposing</span> <span class="p">(</span><span class="nv">perform</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">import </span><span class="nc">Model</span> <span class="nv">exposing</span> <span class="p">(</span><span class="kt">Model</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kn">import </span><span class="nc">Model</span> <span class="nv">exposing</span> <span class="p">(</span><span class="kt">Msg</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="kn">import </span><span class="nc">Http</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nv">perform</span> <span class="nf">:</span> <span class="kt">Model</span> <span class="nf">-></span> <span class="kt">Effect</span> <span class="nf">-></span> <span class="kt">Cmd</span> <span class="kt">Msg</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nv">perform</span> <span class="nv">model</span> <span class="nv">effect</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kr">case</span> <span class="nv">effect</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">GetPosts</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">Http</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">Http</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">decoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="kt">GetPostComments</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kt">Http</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">Http</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostCommentsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">commentDecoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="p">}</span>
</span></span></code></pre><p>Now the <code>Simulate</code> module that we will produce with <code>sed</code>:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">module </span><span class="nc">Simulate</span> <span class="nv">exposing</span> <span class="p">(</span><span class="nv">perform</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">import </span><span class="nc">Model</span> <span class="nv">exposing</span> <span class="p">(</span><span class="kt">Model</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kn">import </span><span class="nc">Model</span> <span class="nv">exposing</span> <span class="p">(</span><span class="kt">Msg</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="kn">import </span><span class="nc">SimulatedEffect.Http</span> <span class="kr">as</span> <span class="kt">Http</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="kn">import </span><span class="nc">ProgramTest</span> <span class="nv">exposing</span> <span class="p">(</span><span class="kt">SimulatedEffect</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nv">perform</span> <span class="nf">:</span> <span class="kt">Model</span> <span class="nf">-></span> <span class="kt">Effect</span> <span class="nf">-></span> <span class="kt">SimulatedEffect</span> <span class="kt">Msg</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nv">perform</span> <span class="nv">model</span> <span class="nv">effect</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kr">case</span> <span class="nv">effect</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">GetPosts</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="kt">Http</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">Http</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">decoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kt">GetPostComments</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kt">Http</span><span class="nf">.</span><span class="nv">get</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="p">{</span> <span class="nv">url</span> <span class="nf">=</span> <span class="s">"/api/posts"</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="p">,</span> <span class="nv">expect</span> <span class="nf">=</span> <span class="kt">Http</span><span class="nf">.</span><span class="nv">expectJson</span> <span class="kt">PostCommentsReceived</span> <span class="p">(</span><span class="kt">Decode</span><span class="nf">.</span><span class="nv">list</span> <span class="kt">Post</span><span class="nf">.</span><span class="nv">commentDecoder</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"> <span class="p">}</span>
</span></span></code></pre><p>So the only changes are:</p>
<ol>
<li>The <code>module</code> line at the top</li>
<li>We have to add an import so that we can use the <code>SimulatedEffect</code> type.</li>
<li>We have to change the <code>Http</code> import to <code>SimulatedEffect.Http</code>, because we alias that we don't need to change any of accesses to that module.</li>
<li>Finally any uses of <code>Cmd Msg</code> we have to change to <code>SimulatedEffect Msg</code> or we could have added a type alias.</li>
</ol>
<p>And that's it. We could also change the type of the function from <code>perform</code> to <code>simulate</code> if we really wanted ot.
As promised, this is easily achieveable with a sed script:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">IMPORT_HTTP</span><span class="o">=</span><span class="s2">"s/import Http/import SimulatedEffect.Http as Http/g"</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">IMPORT_SIMEFFECT</span><span class="o">=</span><span class="s2">"0,/^</span>$<span class="s2">/ s/^</span>$<span class="s2">/\nimport ProgramTest exposing (SimulatedEffect)/"</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nv">REPLACE_CMD</span><span class="o">=</span><span class="s2">"s/Cmd/SimulatedEffect/g"</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">sed <span class="s2">"s/module Perform exposing (perform)/module Simulate exposing (perform)/g;
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="s2"></span><span class="si">${</span><span class="nv">IMPORT_HTTP</span><span class="si">}</span><span class="s2">;
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="s2"></span><span class="si">${</span><span class="nv">IMPORT_SIMEFFECT</span><span class="si">}</span><span class="s2">;
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="s2"></span><span class="si">${</span><span class="nv">REPLACE_CMD</span><span class="si">}</span><span class="s2">"</span> src/Perform.elm > src/Simulate.elm
</span></span></code></pre><p>I put this in a file <code>run-test.sh</code> and then also call actually run the tests, so that this module is generated before every run of the tests. It's a simple <code>sed</code> script and adds negligible time to the test run time.
I think all the parts are fairly self explanatory the <code>0,/^$/ s/^$</code> foo at the start of the <code>IMPORT_SIMEEFFECT</code> is basically saying "Replace the first occurrence of a blank line with the follow", because what I replace it with starts with <code>\n</code> we retain the blank line.</p>
<p>Of course in a real application, including where I actually use this, the <code>Perform</code> module is perhaps broken up into smaller modules. That's okay, you can have a <code>Requests</code> module that is translated into a <code>SimulatedRequests</code> module, and then do the same import translation in your main <code>Peform -> Simulate</code> translation. I even translate a <code>ports</code> module into one that isn't a <code>ports</code> module but uses <code>SimulatedEffect.Ports</code> to created simulated versions of the ports.</p>
Let signatures2022-07-28T16:18:15Zhttps://blog.poleprediction.com/let-signatures<p>A throw-away comment on the elm-radio episode <a href="https://elm-radio.com/episode/debugging-in-elm" rel="nofollow">debugging in elm</a> led me to re-evaluate one of my Elm practices. The comment concerned writing a type signature for values/names defined in a <code>let-in</code> scope. I've always done this in what seemed to be a <em>traditional</em> accepted practice. That is omitting pretty much any and all signatures on names defined within a <code>let-in</code> scope. If you look at <strong>most</strong> Elm code you can find on the web, in particular in the elm-lang guide, and in documentation for elm libraries, you will find the same style.</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">hellowWorld</span> <span class="nf">:</span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">hellowWorld</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="nv">hello</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="s">"Hello"</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="nv">world</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="s">"World"</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="nv">combineWords</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">String</span><span class="nf">.</span><span class="nv">join</span> <span class="s">" "</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">[</span> <span class="nv">hello</span><span class="p">,</span> <span class="nv">world</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="nf">|></span> <span class="nv">combineWords</span>
</span></span></code></pre><p>The throw away comment suggested that you should put type signatures on definitions within let blocks. This had honestly never really occurred to me. However, thinking about it briefly I realised that there is no good reason to have type signatures on top level definitions that doesn't also apply to inner let-defined definitions.</p>
<p>There is the possibility that you're exporting a top-level definition, but the advice/general practice has never seemed to be to put a signature on all <strong>exported</strong> top level definitions, but put type signatures on all top-level definitions.</p>
<p>So for the past couple of weeks I've generally been putting type signatures on most let-defined names. I've found several things, mostly that I like doing this and will continue to do so:</p>
<ol>
<li>This actually speeds up finding type errors. Previously the reported error would often been in the <code>in</code> expression where a name was used because it's the wrong type. However, the real error is in the actual definition of the name, having the type signature there means I get told about the error where it really is.</li>
<li>Particularly for functions this just helps comprehension in the same way that it does for top-level definitions.</li>
<li>I'm generally leaving off the signature if the name is a pretty triivally defined name, so the above I might re-write as:</li>
</ol>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">hellowWorld</span> <span class="nf">:</span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">hellowWorld</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="nv">hello</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="s">"Hello"</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="nv">world</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="s">"World"</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="nv">combineWords</span> <span class="nf">:</span> <span class="kt">List</span> <span class="kt">String</span> <span class="nf">-></span> <span class="kt">String</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="nv">combineWords</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">String</span><span class="nf">.</span><span class="nv">join</span> <span class="s">" "</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="p">[</span> <span class="nv">hello</span><span class="p">,</span> <span class="nv">world</span> <span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="nf">|></span> <span class="nv">combineWords</span>
</span></span></code></pre><p>So I'm still leaving off the type signatures from <code>hello</code> and <code>world</code>. But it has to be pretty trivial, in partiuclar for <em>record</em> types I'm putting the signature, as a common issue is when you add/remove something from the definition of a record type alias, and have to update all the value sites. This is a common situation in which you get the error on the <em>use</em> of a defined name, but it's actually the definition that is wrong, and putting the signature on the let-defined name just means that the compiler reports it in the more appropriate place.</p>
<p>So I'm going to continue to do this, thanks again elm-radio.</p>
100 days of blogging2022-07-28T16:18:17Zhttps://blog.poleprediction.com/one-hundred-days-of-blogging<p>So I started this blog on the first of January 2021, and have blogged every single day since then. This is the 100th day of the year. That was the target I set myself, blog something every single day for 100 days. It seems impossible when you start out. At first you have a few topics you've always wanted to write down but never gotten around to it, but after that you think there is no way you will make it to 100 days.</p>
<p>But it's surprising, once you're pretty much always looking out for things to blog about, topics mostly just "come up" in day-to-day discourse. The hardest days were mostly the weekends, when I wasn't necessarily doing any programming anyway.</p>
<p>There are only a handful of posts that I'm not very proud of, I think my worse one was a dumb idea about <a href="/posts/2021-02-18-mutation-typing" rel="nofollow">mutation typing</a>, that came from having nothing to write about and just trying to <em>write</em> something. However, there are a few much better posts that came out of the same process.</p>
<p>I'm going to <em>try</em> to keep blogging, but not every day. I would like to keep posts, short, but I think I will try to make them more measured. So I think what I'll try to do going forward is to <em>write</em> everyday, but not necessarily <em>publish</em> a post everyday. Some posts will take 2-3 days to write, whilst others I will more readily abandon. So I think I'm aiming for about one post per week. However, I can definitely see that I might just give up entirely, when I don't have a strict goal. We will see.</p>
<p>In any case, I'm happy to have reached this milestone. If one or two of the posts have been found and are useful to someone, then I'm pretty happy with that.</p>
Copy from tmux2022-07-28T16:18:07Zhttps://blog.poleprediction.com/copy-from-tmux<p>This is mostly a post for myself to remind myself how to do this. I typically program on a remote server. I ssh in via the terminal and use tmux to manage sessions. I then edit code in neovim. This setup has a bunch of advantages and a bunch of disadvantages as well. One of the disadvantages is that I found it difficult to copy and paste from the tmux session in my terminal, to a completely different program running on my local machine, such as say my web-browser, or a chat client.</p>
<p>Today I came across <a href="https://ianthehenry.com/posts/tmux-psa/" rel="nofollow">this blog post</a> explaining how to copy and paste from within tmux. Fortunately that also works for me, and copies it to my <strong>local</strong> machine's clipboard which is great because I can now use this to copy-and-paste lines of code elsewhere.</p>
<p>So, how to do this, without any additional configuration. First you enter 'navigation' mode in tmux, I'm told you can do this using <code>Alt-Space</code> but that is bound to something in my window manager, so you can also do <code>prefix-[</code>, where <code>prefix</code> is by default bound to <code>Ctrl-b</code> but almost everyone sets it to something else (<code>Ctrl-a</code> for me).</p>
<p>When in navigation mode you can use the normal vim keys to move up and down etc. Then, to start selecting you press <code>Space</code>, to copy the selection you press <code>Enter</code> and that's it. Selection is done in a block manner, but that can be quite useful, it's at least more useful than copying across splits.</p>
<p>One definitely slightly bad thing I still have is that it copies 'terminal' wise, therefore it doesn't know anything about your vim setup. For example I have visual whitespace which I find really helpful for seeing the (relative) line numbers of indented code. But that means when I copy I get the 'visual space' characters. So for example instead of <code>x = 10</code> I get <code>x␣=␣10</code>. Not great. However, I can instead just 'cat' or 'less' the file in the terminal directly and copy-and-paste from there, rather than from within Vim.</p>
<p>Anyway I doubt this post is helpful to anyone but me, but there you have it.</p>
Dry can cost you2022-07-28T16:18:08Zhttps://blog.poleprediction.com/dry-can-cost-you<p>I had an issue with a computation taking much longer than might have been expected. It turned out to be caused by a use of the function <code>greedyGroupsOf</code> from the <a href="https://package.elm-lang.org/packages/elm-community/list-extra/latest/List-Extra" rel="nofollow">list-extra library</a>, and it was solved by writing my own, naive, implementation.</p>
<p>I haven't quite gotten to the bottom of <strong>why</strong> the library version of <code>greedyGroupsOf</code> of is so much slower than my naive version, but the reason it is slower is mostly to do with <em>DRY</em> or <em>don't repeat yourself</em>.</p>
<p>To see why, let's look first at my naive version:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">greedyGroupsOf</span> <span class="nf">:</span> <span class="kt">Int</span> <span class="nf">-></span> <span class="kt">List</span> <span class="nv">a</span> <span class="nf">-></span> <span class="kt">List</span> <span class="p">(</span><span class="kt">List</span> <span class="nv">a</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">greedyGroupsOf</span> <span class="nv">size</span> <span class="nv">elements</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">case</span> <span class="nv">elements</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="p">[]</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="p">[]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="nv">_</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="p">(</span> <span class="nv">year</span><span class="p">,</span> <span class="nv">remainder</span> <span class="p">)</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">List</span><span class="nf">.</span><span class="nv">splitAt</span> <span class="nv">size</span> <span class="nv">elements</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="nv">year</span> <span class="nf">::</span> <span class="nv">splitIntoYears</span> <span class="nv">size</span> <span class="nv">remainder</span>
</span></span></code></pre><p>This is perfectly simple. Now, let's look at the <a href="https://github.com/elm-community/list-extra/blob/8.3.0/src/List/Extra.elm#L1927" rel="nofollow">library version</a>:</p>
<pre class="chroma"><code><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">greedyGroupsOf</span> <span class="nf">:</span> <span class="kt">Int</span> <span class="nf">-></span> <span class="kt">List</span> <span class="nv">a</span> <span class="nf">-></span> <span class="kt">List</span> <span class="p">(</span><span class="kt">List</span> <span class="nv">a</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">greedyGroupsOf</span> <span class="nv">size</span> <span class="nv">xs</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"> <span class="nv">greedyGroupsOfWithStep</span> <span class="nv">size</span> <span class="nv">size</span> <span class="nv">xs</span>
</span></span></code></pre><p>Ah, okay, so it's just calling a more general function. This is why it is <em>DRY</em>, I do this kind of thing, almost habitually. Here is the implementation of the <a href="https://github.com/elm-community/list-extra/blob/8.3.0/src/List/Extra.elm#L1951" rel="nofollow">more general function</a>:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">greedyGroupsOfWithStep</span> <span class="nf">:</span> <span class="kt">Int</span> <span class="nf">-></span> <span class="kt">Int</span> <span class="nf">-></span> <span class="kt">List</span> <span class="nv">a</span> <span class="nf">-></span> <span class="kt">List</span> <span class="p">(</span><span class="kt">List</span> <span class="nv">a</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">greedyGroupsOfWithStep</span> <span class="nv">size</span> <span class="nv">step</span> <span class="nv">xs</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="nv">xs_</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="kt">List</span><span class="nf">.</span><span class="nv">drop</span> <span class="nv">step</span> <span class="nv">xs</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="nv">okayArgs</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="nv">size</span> <span class="nf">></span> <span class="mi">0</span> <span class="nf">&&</span> <span class="nv">step</span> <span class="nf">></span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="nv">okayXs</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">List</span><span class="nf">.</span><span class="nv">length</span> <span class="nv">xs</span> <span class="nf">></span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="kr">if</span> <span class="nv">okayArgs</span> <span class="nf">&&</span> <span class="nv">okayXs</span> <span class="kr">then</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="kt">List</span><span class="nf">.</span><span class="nv">take</span> <span class="nv">size</span> <span class="nv">xs</span> <span class="nf">::</span> <span class="nv">greedyGroupsOfWithStep</span> <span class="nv">size</span> <span class="nv">step</span> <span class="nv">xs_</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kr">else</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="p">[]</span>
</span></span></code></pre><p>As I've mentioned I'm not entirely sure why this is so much slower than the more specific function. There is one thing it does. Because the step and the size might be different it cannot use <code>List.splitAt</code> and has to do two separate <code>List.drop</code> and <code>List.take</code> operations. This is because in the more general function you can have a different step size to that of the group size, a lower step size will mean that some elements are in more than one group, whilst a larger step size will mean some elements are omitted entirely.</p>
<p>Anyway, this means that <code>greedyGroupsOf</code> is slower than it needs to be, because it is a simple invocation of the more general function. In this case, it might be worth just using the specialised implementation of the function. It means repeating some code, or logic, but in this case I doubt that's so bad, these implementations are unlikely to change, and they are probably correct.</p>
Immutability bugs again2022-07-28T16:18:13Zhttps://blog.poleprediction.com/immutability-bugs-again<p>Previously I've written about <a href="/posts/2021-01-23-immutabilit-bugs/" rel="nofollow">immutability bugs</a> which are bugs that are more likely in an immutable language than a mutable one. I think these are relatively rare, but they do exist. A <a href="https://discourse.elm-lang.org/t/best-practice-for-updating-an-incremented-id-in-the-model/7208" rel="nofollow">good example</a> has come up on the Elm discourse.</p>
<p>The person asking the question wants to create new unique identifiers for items in their model. To do this you can simply keep a count of the number of identifiers you have thus far created. So you can do something like the following:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Id</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="p">{</span> <span class="nf">....</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="p">,</span> <span class="nv">idsSoFar</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nv">createNewId</span> <span class="nf">:</span> <span class="kt">Model</span> <span class="nf">-></span> <span class="p">(</span><span class="kt">Id</span><span class="p">,</span> <span class="kt">Model</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nv">createNewId</span> <span class="nv">model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="p">(</span> <span class="kt">String</span><span class="nf">.</span><span class="nv">fromInt</span> <span class="nv">model</span><span class="nf">.</span><span class="nv">idsSoFar</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="nf">|></span> <span class="kt">String</span><span class="nf">.</span><span class="nv">append</span> <span class="s">"id-number-"</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="p">,</span> <span class="p">{</span> <span class="nv">model</span> <span class="nf">|</span> <span class="nv">idsSoFar</span> <span class="nf">=</span> <span class="nv">model</span><span class="nf">.</span><span class="nv">idsSoFar</span> <span class="nf">+</span> <span class="mi">1</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">)</span>
</span></span></code></pre><p>All good, however, the possibility for a bug is <em>relatively</em> high here. In your update function, if you use the <code>createNewId</code> you must make sure that you remember to store the new model. Here's a potential bug:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">update</span> <span class="nf">:</span> <span class="kt">Msg</span> <span class="nf">-></span> <span class="kt">Model</span> <span class="nf">-></span> <span class="p">(</span><span class="kt">Model</span><span class="p">,</span> <span class="kt">Cmd</span> <span class="kt">Msg</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">update</span> <span class="nv">message</span> <span class="nv">model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kr">case</span> <span class="nv">message</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="nf">...</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="kt">NewThing</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="p">(</span><span class="nv">newId</span><span class="p">,</span> <span class="nv">newModel</span><span class="p">)</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="nv">createNewId</span> <span class="nv">model</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="nv">newThing</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="kt">Thing</span><span class="nf">.</span><span class="nv">empty</span> <span class="nv">newId</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="p">(</span> <span class="p">{</span> <span class="nv">model</span> <span class="nf">|</span> <span class="nv">things</span> <span class="nf">=</span> <span class="nv">newThing</span> <span class="nf">::</span> <span class="nv">model</span><span class="nf">.</span><span class="nv">things</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">,</span> <span class="kt">Cmd</span><span class="nf">.</span><span class="nv">none</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="p">)</span>
</span></span></code></pre><p>You see the bug, I've accidentally updated the original model rather than <code>newModel</code>. This is one reason why using static analysis tools such as <code>elm-review</code> is important. Such tools will warn you about the defined-but-unused name <code>newModel</code> and hopefully you can correct the error.</p>
<p>Could we find a way to make <strong>sure</strong> this bug doesn't happen? Yes we could, but it's not pretty. One way to do this is to define your <code>Model</code> type as an <em>opaque</em> type (this just means making it a custom tagged union type but not exporting the constructors). So, you can do the following in <code>Model.elm</code>:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">module </span><span class="nc">Module</span> <span class="nv">exposing</span> <span class="p">(</span><span class="kt">State</span><span class="p">,</span> <span class="kt">Id</span><span class="p">,</span> <span class="kt">Model</span><span class="p">,</span> <span class="nv">update</span><span class="p">,</span> <span class="nv">updateWithNewId</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">Id</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="kt">String</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="kr">type</span> <span class="kr">alias</span> <span class="kt">State</span> <span class="nv">a</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="p">{</span> <span class="nv">things</span> <span class="nf">:</span> <span class="kt">List</span> <span class="kt">Thing</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="p">,</span> <span class="nf">...</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="kr">type</span> <span class="kt">Model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="kt">Model</span> <span class="p">(</span><span class="kt">State</span> <span class="p">{</span> <span class="nv">idsSoFar</span> <span class="nf">:</span> <span class="kt">Int</span><span class="p">}</span> <span class="p">)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nv">update</span> <span class="nf">:</span> <span class="p">(</span><span class="kt">State</span> <span class="nv">a</span> <span class="nf">-></span> <span class="kt">State</span> <span class="nv">a</span><span class="p">)</span> <span class="nf">-></span> <span class="kt">Model</span> <span class="nf">-></span> <span class="kt">Model</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nv">update</span> <span class="nv">updateState</span> <span class="nv">model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="kr">case</span> <span class="nv">model</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="kt">Model</span> <span class="nv">state</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"> <span class="kt">Model</span> <span class="p">(</span><span class="nv">updateState</span> <span class="nv">state</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nv">updateWithNewId</span> <span class="nf">:</span> <span class="p">(</span><span class="kt">Id</span> <span class="nf">-></span> <span class="kt">State</span> <span class="nv">a</span> <span class="nf">-></span> <span class="kt">State</span> <span class="nv">a</span><span class="p">)</span> <span class="nf">-></span> <span class="kt">Model</span> <span class="nf">-></span> <span class="kt">Model</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="nv">updateWithNewId</span> <span class="nv">updateState</span> <span class="nv">model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"> <span class="kr">case</span> <span class="nv">model</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="kt">Model</span> <span class="nv">state</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"> <span class="nv">newId</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"> <span class="kt">String</span><span class="nf">.</span><span class="nv">fromInt</span> <span class="nv">state</span><span class="nf">.</span><span class="nv">idsSoFar</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"> <span class="nf">|></span> <span class="kt">String</span><span class="nf">.</span><span class="nv">append</span> <span class="s">"id-number-"</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"> <span class="nv">newState</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"> <span class="p">{</span> <span class="nv">state</span> <span class="nf">|</span> <span class="nv">idsSoFar</span> <span class="nf">=</span> <span class="nv">state</span><span class="nf">.</span><span class="nv">idsSoFar</span> <span class="nf">+</span> <span class="mi">1</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"> <span class="kt">Model</span> <span class="p">(</span><span class="nv">updateState</span> <span class="nv">newId</span> <span class="nv">newState</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span></code></pre><p>You could also make the <code>Id</code> type opaque so that it is impossible to create one without using this module.
I think this basically solves the issue, but it's pretty far from pretty. Your update function looks like this:</p>
<pre class="chroma"><code><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">update</span> <span class="nv">message</span> <span class="nv">model</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"> <span class="kr">case</span> <span class="nv">message</span> <span class="kr">of</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"> <span class="kt">Tick</span> <span class="nv">now</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="p">(</span> <span class="kt">Model</span><span class="nf">.</span><span class="nv">update</span> <span class="p">(</span><span class="nf">\</span><span class="nv">s</span> <span class="nf">-></span> <span class="p">{</span> <span class="nv">s</span> <span class="nf">|</span> <span class="nv">now</span> <span class="nf">=</span> <span class="nv">now</span> <span class="p">})</span> <span class="nv">model</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="p">,</span> <span class="kt">Cmd</span><span class="nf">.</span><span class="nv">none</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="nf">....</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="kt">NewThing</span> <span class="nf">-></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"> <span class="kr">let</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="nv">updateFun</span> <span class="nv">newId</span> <span class="nv">state</span> <span class="nf">=</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="p">{</span> <span class="nv">state</span> <span class="nf">|</span> <span class="nv">things</span> <span class="nf">=</span> <span class="kt">Thing</span><span class="nf">.</span><span class="nv">empty</span> <span class="nv">newId</span> <span class="nf">::</span> <span class="nv">model</span><span class="nf">.</span><span class="nv">things</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="kr">in</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="p">(</span> <span class="kt">Model</span><span class="nf">.</span><span class="nv">updateWithNewId</span> <span class="nv">updateFun</span> <span class="nv">model</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="p">,</span> <span class="kt">Cmd</span><span class="nf">.</span><span class="nv">none</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="nf">...</span>
</span></span></code></pre><p>You could probably make this a little more palatable by separating out your messages into those that require a new Id and those that do not and then just matching within those.</p>