{"version": "https://jsonfeed.org/version/1.1", "title": "Mimehole Arch", "home_page_url": "https://michael.homer.nz/notes/", "feed_url": "https://michael.homer.nz/notes/feed.json", "description": "Research notes from Michael Homer", "user_comment": "", "authors": [{"name": "Michael Homer", "url": "https://orcid.org/0000-0003-0280-6748"}], "language": "en-NZ", "items": [{"title": "Tinted Windows: Reconstructing Historic Dithering", "language": "en-US", "url": "https://michael.homer.nz/notes/dither-windows-3/", "date_published": "2026-09-25T00:00:00", "id": "https://doi.org/10.59350/wa3f9-pmq48", "authors": [{"name": "Michael Homer", "url": "https://orcid.org/0000-0003-0280-6748"}], "content_html": "<p>With the capacity only to display a very limited palette at higher resolutions, Windows 3.x used dithering to create the effect of a wider range of solid colours. There are a number of standard dithering algorithms, but these don&#x27;t match what Windows actually did. The specific approach is documented in U.S. Patent 5485558, but there are a number of errors in the description given there. This is the result of my reverse engineering to produce behaviour correct to the real system, and a practical implementation illustrating it. The algorithm takes three colour coordinates 0\u2013255 for each of the red, green, and blue channels, and produces an 8x8 dither pattern containing up to four palette colours spread across it.</p>\n<h2 id=\"Background\">Background</h2>\n<p>Windows 3 was intended for use with VGA graphics cards, which supported 640x480 resolution with 16 distinct colours chosen from an 18-bit colour space. The standard Windows colour palette used white, black, and darker and lighter shades of red, green, yellow, blue, magenta, cyan, and grey.</p>\n<figure><a href=\"https://michael.homer.nz/notes/dither-windows-3/both.png\"><img src=\"https://michael.homer.nz/notes/dither-windows-3/both.png\" alt=\"Full 16-colour palette in two rows, dark above bright version of each colour.\" /></a><figcaption>Full 16-colour palette in two rows, dark above bright version of each colour.</figcaption></figure>\n<p>To create the illusion of a broader range of colours, <em>dithering</em> would be used to mix pixels of different palette colours together to approximate the real colour.</p>\n<figure><a href=\"https://michael.homer.nz/notes/dither-windows-3/comparison2.png\"><img src=\"https://michael.homer.nz/notes/dither-windows-3/comparison2.png\" alt=\"Side-by-side swatches of a solid purple colour and a dithered version of the same colour.\" /></a><figcaption>Composite image of, left, a dithered version of a colour captured in Windows for Workgroups 3.11&#x27;s colour picker running under DOSBox-Staging and, right, a solid block of the colour <sup>^1</sup>.</figcaption></figure>\n<p>Windows combined a selection of up to four colours drawn from 15 palette colours (light grey was excluded) to create any approximation, using an 8x8 pattern.</p>\n<figure><a href=\"https://michael.homer.nz/notes/dither-windows-3/big.png\"><img src=\"https://michael.homer.nz/notes/dither-windows-3/big.png\" alt=\"Zoomed-in 8x8 dither pattern using dark and bright magenta, bright blue, and grey.\" /></a><figcaption>Zoomed-in 8x8 dither pattern using dark and bright magenta, bright blue, and grey.</figcaption></figure>\n<p>There are a number of standard dithering algorithms, but these give different (and generally <em>worse</em>) results than what Windows did, even with the same palette.\nThere is a (now-expired) patent that describes the approach taken in Windows, but following the code given there doesn&#x27;t produce matching results to the real system: wrong colours used, wrong proportions, and totally invalid for many colours.</p>\n<figure><a href=\"https://michael.homer.nz/notes/dither-windows-3/patent-mismatch.png\"><img src=\"https://michael.homer.nz/notes/dither-windows-3/patent-mismatch.png\" alt=\"A swatch of a specific light blue colour, next to the dither pattern produced by the patent code, which is green.\" /></a><figcaption>The result of the code in the patent for a specific colour, next to a swatch of the very different true colour.</figcaption></figure>\n<figure><a href=\"https://michael.homer.nz/notes/dither-windows-3/corrected.png\"><img src=\"https://michael.homer.nz/notes/dither-windows-3/corrected.png\" alt=\"The accurate Windows 3 dither result next to the same colour.\" /></a><figcaption>A solid swatch of the same colour, next to a much more similar dither pattern produced by the accurate algorithm.</figcaption></figure>\n<p>The next section explores my reconstruction of the true algorithm and correction of the errors in the patent description to replicate actual behaviour.</p>\n<h2 id=\"The-Algorithm\">The Algorithm</h2>\n<div class=\"biglink\"><a href=\"https://patentimages.storage.googleapis.com/0d/51/6a/42404fa95f7558/US5485558.pdf\"><div><h3>U.S. Patent 548555810</h3><span class=\"h-cite\"><span class=\"h-author h-card\">Weise, David N.</span> and <span class=\"h-author\">H-Gunter Zieber</span>. <time class=\"dt-published\">1990</time>. \u201c<cite class=\"h-name\">Method and system for displaying color on a computer output device using dithering techniques</cite>\u201d. United States Patent 5485558. <a href=\"https://patentimages.storage.googleapis.com/0d/51/6a/42404fa95f7558/US5485558.pdf\" class=\"u-url\">Online</a>.</span></div></a></div>\n<p>The patent was filed May 22, 1990, the release date of Windows 3.0. It&#x27;s long expired and worth diving into. The whole thing is relevant but I&#x27;m going to be referring to Table 14 in particular a lot, which is a C code listing of (purportedly) the algorithm in use. The code isn&#x27;t the easiest to follow because it relies almost entirely on global variables for passing arguments and return values, and most of it&#x27;s unnecessarily structured in terms of matrices. It&#x27;s also just wrong in several places, some of which are simple syntax errors, but others are quite annoying to trace; for the patent&#x27;s own running example, the code produces a pure-grey dither pattern instead of the purplish colour it is meant to replicate. It uses &quot;pel&quot; for pixels and &quot;super-pel&quot; for the 8x8 dither pattern.</p>\n<p>The dither pattern uses 15 colours corresponding to every combination of half, full, and zero strength in each dimension, expressed as 4-bit RGBI (the 8 bit, I, makes the colour brighter). These are numbered 0-15 with 8 unused and the bits correspond directly to channels and intensity, in increasing order of significance RGBI. The patent gives these names with 8-bit values for each channel, but these would have been squashed to 6-bit for display, and on modern displays the dark colours in particular are darker than they would have appeared on contemporary monitors; the table below shows an <em>approximation</em> of how these shades would have looked at the time as well as swatches of the provided RGB values for all colours.</p>\n<table><tr><th>R</th>\n<th>G</th>\n<th>B</th>\n<th>IBGR</th>\n<th>Decimal</th>\n<th>Name</th></tr>\n<tr><td>0 </td>\n<td>   0 </td>\n<td>   0 </td>\n<td> 0000 </td>\n<td>    0    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/0.png\" alt=\"\" /> Black</td></tr>\n<tr><td>128 </td>\n<td>   0 </td>\n<td>   0 </td>\n<td> 0001 </td>\n<td>    1    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/1.png\" alt=\"\" /><img src=\"https://michael.homer.nz/notes/dither-windows-3/1b.png\" alt=\"\" /> Dark red</td></tr>\n<tr><td>0 </td>\n<td> 128 </td>\n<td>   0 </td>\n<td> 0010 </td>\n<td>    2    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/2.png\" alt=\"\" /><img src=\"https://michael.homer.nz/notes/dither-windows-3/2b.png\" alt=\"\" /> Dark green</td></tr>\n<tr><td>128 </td>\n<td> 128 </td>\n<td>   0 </td>\n<td> 0011 </td>\n<td>    3    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/3.png\" alt=\"\" /><img src=\"https://michael.homer.nz/notes/dither-windows-3/3b.png\" alt=\"\" /> Dark yellow</td></tr>\n<tr><td>0 </td>\n<td>   0 </td>\n<td> 128 </td>\n<td> 0100 </td>\n<td>    4    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/4.png\" alt=\"\" /><img src=\"https://michael.homer.nz/notes/dither-windows-3/4b.png\" alt=\"\" /> Dark blue</td></tr>\n<tr><td>128 </td>\n<td>   0 </td>\n<td> 128 </td>\n<td> 0101 </td>\n<td>    5    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/5.png\" alt=\"\" /><img src=\"https://michael.homer.nz/notes/dither-windows-3/5b.png\" alt=\"\" /> Dark magenta</td></tr>\n<tr><td>0 </td>\n<td> 128 </td>\n<td> 128 </td>\n<td> 0110 </td>\n<td>    6    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/6.png\" alt=\"\" /><img src=\"https://michael.homer.nz/notes/dither-windows-3/6b.png\" alt=\"\" /> Dark cyan</td></tr>\n<tr><td>128 </td>\n<td> 128 </td>\n<td> 128 </td>\n<td> 0111 </td>\n<td>    7    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/7.png\" alt=\"\" /><img src=\"https://michael.homer.nz/notes/dither-windows-3/7b.png\" alt=\"\" /> Grey</td></tr>\n<tr><td>- </td>\n<td>   - </td>\n<td>   - </td>\n<td> 1000 </td>\n<td>    8    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/8.png\" alt=\"\" /> (unused)</td></tr>\n<tr><td>255 </td>\n<td>   0 </td>\n<td>   0 </td>\n<td> 1001 </td>\n<td>    9    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/9.png\" alt=\"\" /> Red</td></tr>\n<tr><td>0 </td>\n<td> 255 </td>\n<td>   0 </td>\n<td> 1010 </td>\n<td>   10    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/10.png\" alt=\"\" /> Green</td></tr>\n<tr><td>255 </td>\n<td> 255 </td>\n<td>   0 </td>\n<td> 1011 </td>\n<td>   11    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/11.png\" alt=\"\" /> Yellow</td></tr>\n<tr><td>0 </td>\n<td>   0 </td>\n<td> 255 </td>\n<td> 1100 </td>\n<td>   12    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/12.png\" alt=\"\" /> Blue</td></tr>\n<tr><td>255 </td>\n<td>   0 </td>\n<td> 255 </td>\n<td> 1101 </td>\n<td>   13    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/13.png\" alt=\"\" /> Magenta</td></tr>\n<tr><td>0 </td>\n<td> 255 </td>\n<td> 255 </td>\n<td> 1110 </td>\n<td>   14    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/14.png\" alt=\"\" /> Cyan</td></tr>\n<tr><td>255 </td>\n<td> 255 </td>\n<td> 255 </td>\n<td> 1111 </td>\n<td>   15    </td>\n<td> <img src=\"https://michael.homer.nz/notes/dither-windows-3/15.png\" alt=\"\" /> White</td></tr></table>\n<p>The patent uses a working example of colour (r=128, g=31, b=190), but you can choose your own to follow along with.</p>\n\n\n\n<p>We&#x27;ll use this\n<span class=\"chosen_label\">purple/blue</span> colour,\nr =\n<span class=\"initialR d\">128</span>, g =\n<span class=\"initialG d\">31</span>, b =\n<span class=\"initialB d\">190</span>,\nfor the rest of this article.</p>\n<figure>\n <svg xmlns=\"http://w3.org\" viewBox=\"40 0 160 170\" id=\"palette_cube\">\n  <!-- Inner group to position the 3D origin at pixel (150, 110) and match cartesian geometry -->\n  <g transform=\"translate(150, 110) scale(1, -1)\">\n    <!-- Primary Projecting Axes from 0,0 (Color Coded) -->\n    <line x1=\"0\" y1=\"0\" x2=\"-93.3\" y2=\"-11.5\" stroke=\"#ff0000\" stroke-width=\"3\" stroke-linecap=\"round\"/> <!-- Red Axis -->\n    <line x1=\"0\" y1=\"0\" x2=\"34.9\" y2=\"-31.5\" stroke=\"#00aa00\" stroke-width=\"3\" stroke-linecap=\"round\"/> <!-- Green Axis -->\n    <line x1=\"0\" y1=\"0\" x2=\"0.0\" y2=\"94.0\" stroke=\"#0000ff\" stroke-width=\"3\" stroke-linecap=\"round\"/> <!-- Blue Axis -->\n\n\n    <!-- Remaining Wireframe Edges (Grey) -->\n    <line x1=\"-93.3\" y1=\"-11.5\" x2=\"-58.4\" y2=\"-43.0\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- red-yellow -->\n    <line x1=\"34.9\" y1=\"-31.5\" x2=\"-58.4\" y2=\"-43.0\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- opposite red axis bot -->\n    <line x1=\"-93.3\" y1=\"-11.5\" x2=\"-93.3\" y2=\"82.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- red-magenta -->\n    <line x1=\"34.9\" y1=\"-31.5\" x2=\"34.9\" y2=\"62.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- opposite blue axis rt -->\n    <line x1=\"-58.4\" y1=\"-43.0\" x2=\"-58.4\" y2=\"51.0\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- below white -->\n    <line x1=\"0.0\" y1=\"94.0\" x2=\"-93.3\" y2=\"82.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- blue-magenta -->\n    <line x1=\"0.0\" y1=\"94.0\" x2=\"34.9\" y2=\"62.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- blue-cyan -->\n    <line x1=\"-58.4\" y1=\"51.0\" x2=\"-93.3\" y2=\"82.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- magenta-white -->\n    <line x1=\"-58.4\" y1=\"51.0\" x2=\"34.9\" y2=\"62.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- cyan-white -->\n\n\n    <!-- dashed half-way lines -->\n    <line x1=\"-46.65\" y1=\"-5.75\" x2=\"-11.75\" y2=\"-37.25\" stroke=\"#888\" stroke-dasharray=\"4\" /> <!-- horz, 800-8f0 -->\n    <line x1=\"0\" y1=\"47\" x2=\"34.9\" y2=\"15.5\" stroke=\"#888\" stroke-dasharray=\"4\" /> <!-- horz, 008-0f8 -->\n    <line x1=\"17.45\" y1=\"-15.75\" x2=\"-75.85\" y2=\"-27.25\" stroke=\"#888\" stroke-dasharray=\"4\" /> <!-- horz, 080-f80 -->\n    <line x1=\"-46.65\" y1=\"-5.75\" x2=\"-46.65\" y2=\"88.25\" stroke=\"#888\" stroke-dasharray=\"4\" /> <!-- vert, 800-80f -->\n    <line x1=\"0\" y1=\"47\" x2=\"-93.3\" y2=\"35.5\" stroke=\"#888\" stroke-dasharray=\"4\" /> <!-- horz, 008-f08 -->\n    <line x1=\"17.45\" y1=\"-15.75\" x2=\"17.45\" y2=\"78.25\" stroke=\"#888\" stroke-dasharray=\"4\" /> <!-- vert, 080-08f -->\n\n\n    <!-- <line x1=\"17.45\" y1=\"78.25\" x2=\"-75.85\" y2=\"66.75\" stroke=\"purple\" stroke-dasharray=\"4\" /> vert, 08f-88f -->\n\n\n    <!-- centre vert -->\n    <!-- <line x1=\"-29.75\" y1=\"-21.5\" x2=\"-29.2\" y2=\"72.5\" stroke=\"#888\" stroke-dasharray=\"4\" /> vert, 880-88f -->\n    <line x1=\"-29.75\" y1=\"-21.5\" x2=\"-29.475\" y2=\"25.5\" stroke=\"#888\" stroke-dasharray=\"4\" /> <!-- vert, 880-888 -->\n\n\n    <line x1=\"-46.65\" y1=\"41.25\" x2=\"-29.475\" y2=\"25.5\" stroke=\"#888\" stroke-dasharray=\"4\" /> <!-- horz, 880-888 -->\n    <line x1=\"-29.475\" y1=\"25.5\" x2=\"17.45\" y2=\"31.25\" stroke=\"#888\" stroke-dasharray=\"4\" /> <!-- horz, 888-088 -->\n\n\n\n\n    <!-- half colours -->\n    <circle cx=\"-46.65\" cy=\"-5.75\" r=\"4\" fill=\"#800\" stroke=\"CanvasText\" />\n    <circle cx=\"-29.175\" cy=\"-21.5\" r=\"4\" fill=\"#880\" stroke=\"CanvasText\" />\n    <circle cx=\"0\" cy=\"47\" r=\"4\" fill=\"#008\" stroke=\"CanvasText\" />\n    <circle cx=\"17.45\" cy=\"31.25\" r=\"4\" fill=\"#088\" stroke=\"CanvasText\" />\n    <circle cx=\"17.45\" cy=\"-15.75\" r=\"4\" fill=\"#080\" stroke=\"CanvasText\" />\n    <circle cx=\"-46.65\" cy=\"41.25\" r=\"4\" fill=\"#808\" stroke=\"CanvasText\" />\n\n\n    <!-- grey -->\n    <circle cx=\"-29.475\" cy=\"25.5\" r=\"4\" fill=\"#888\" stroke=\"CanvasText\" />\n\n\n    <!-- full colours -->\n    <circle cx=\"-58.4\" cy=\"51.0\" r=\"4\" fill=\"#fff\" stroke=\"CanvasText\" />\n    <circle cx=\"-93.3\" cy=\"-11.5\" r=\"4\" fill=\"#f00\" stroke=\"CanvasText\" />\n    <circle cx=\"-58.4\" cy=\"-43\" r=\"4\" fill=\"#ff0\" stroke=\"CanvasText\" />\n    <circle cx=\"34.9\" cy=\"-31.5\" r=\"4\" fill=\"#0f0\" stroke=\"CanvasText\" />\n    <circle cx=\"34.9\" cy=\"62.5\" r=\"4\" fill=\"#0ff\" stroke=\"CanvasText\" />\n    <circle cx=\"-93.3\" cy=\"82.5\" r=\"4\" fill=\"#f0f\" stroke=\"CanvasText\" />\n    <circle cx=\"0\" cy=\"94\" r=\"4\" fill=\"#00f\" stroke=\"CanvasText\" />\n\n\n    <!-- black -->\n    <circle cx=\"0\" cy=\"0\" r=\"4\" fill=\"#000\" stroke=\"CanvasText\" />\n\n\n    <!-- example colour -->\n    <line x1=\"-42.59\" y1=\"60.44\" x2=\"-42.59\" y2=\"-9.60\" stroke=\"CanvasText\" id=\"palette_example_spot_dangle\" stroke-dasharray=\"1\"></line>\n    <line x1=\"-46.83\" y1=\"-5.77\" x2=\"-42.59\" y2=\"-9.60\" stroke=\"CanvasText\" id=\"palette_example_spot_bottom_r\" stroke-dasharray=\"1\"></line>\n    <line x1=\"4.24\" y1=\"-3.83\" x2=\"-42.59\" y2=\"-9.60\" stroke=\"CanvasText\" id=\"palette_example_spot_bottom_g\" stroke-dasharray=\"1\"></line>\n    <circle cx=\"-42.59\" cy=\"60.44\" r=\"2\" fill=\"rgb(128 31 190)\" stroke=\"#888\" id=\"palette_example_spot\"></circle>\n\n\n\n\n    <!-- Origin Node -->\n    <!-- <circle cx=\"0\" cy=\"0\" r=\"4\" fill=\"#000000\"/> -->\n    <g transform=\"scale(0.5, -0.5)\" fill=\"CanvasText\">\n\n\n\n\n\n\n\n\n      <text x=\"10\" y=\"0\">0</text>\n      <text x=\"-93.3\" y=\"0\">1</text>\n      <text y=\"31.5\" x=\"45\">2</text>\n      <text x=\"-50\" y=\"35\">3</text>\n\n\n\n\n      <text y=\"-100\" x=\"5\">4</text>\n      <text y=\"-90\" x=\"-85\">5</text>\n      <text y=\"-70\" x=\"40\">6</text>\n      <text y=\"-28.5\" x=\"-52.475\">7</text>\n\n\n\n\n\n\n\n\n      <text x=\"-205\" y=\"20\">9</text>\n      <text x=\"75\" y=\"80\">10</text>\n      <text x=\"-125\" y=\"110\">11</text>\n      <text x=\"0\" y=\"-195\">12</text>\n      <text y=\"-170\" x=\"-215\">13</text>\n      <text x=\"75\" y=\"-130\">14</text>\n      <text x=\"-125\" y=\"-115\">15</text>\n\n\n\n\n\n\n        </g>\n  </g>\n </svg>\n <figcaption>The RGB colour cube with the palette colours marked and the example colour shown in place.</figcaption>\n</figure>\n\n\n\n\n<h3 id=\"Mapping-into-tetrahedral-space-0\">Mapping into tetrahedral space 0</h3>\n<p>The algorithm maps the colour coordinates into one of six tetrahedral subregions of the colour cube, using four vertices of the cube. These tetrahedrons are defined by the planes with equal values R=G, G=B, and R=G (these each cross white, black, and one other opposite pair of vertices). The tetrahedron is then divided again into four subspaces, defined by colours on the vertices. It gets into that first tetrahedron by swapping coordinates such that r &gt;= g &gt;= b, remembering the swaps so that they can be undone at the end. This is:</p>\n<pre><code>swapRB = r &lt; b\nif swapRB:\n    r, b = b, r\nswapGB = g &lt; b\nif swapGB:\n    g, b = b, g\nswapRG = r &lt; g\nif swapRG:\n    r, g = g, r</code></pre>\n<p>The swapXY values are used later to transform colours back. For our running example, we start with (r =\n<span class=\"initialR d\">128</span>, g = <span class=\"initialG d\">31</span>, b = <span class=\"initialB d\">190</span>),\nso swapRB is\n<span class=\"swapRB d\">true</span>, swapGB is <span class=\"swapGB d\">true</span>, and swapRG is <span class=\"swapRG d\">false</span>,\nand for the rest of the algorithm the three values are (r =\n<span class=\"swappedR d\">190</span>, g = <span class=\"swappedG d\">128</span>, b = <span class=\"swappedB d\">31</span>).\nThey will be swapped back at the end.</p>\n\n<figure>\n <svg xmlns=\"http://w3.org\" viewBox=\"40 0 160 170\" id=\"tetrahedral_space_0\">\n  <g transform=\"translate(150, 110) scale(1, -1)\">\n    <!-- primary axes -->\n    <line x1=\"0\" y1=\"0\" x2=\"-93.3\" y2=\"-11.5\" stroke=\"#ff0000\" stroke-width=\"3\" stroke-linecap=\"round\"/> <!-- Red Axis -->\n    <line x1=\"0\" y1=\"0\" x2=\"34.9\" y2=\"-31.5\" stroke=\"#00aa00\" stroke-width=\"3\" stroke-linecap=\"round\"/> <!-- Green Axis -->\n    <line x1=\"0\" y1=\"0\" x2=\"0.0\" y2=\"94.0\" stroke=\"#0000ff\" stroke-width=\"3\" stroke-linecap=\"round\"/> <!-- Blue Axis -->\n\n\n    <!-- Remaining wireframe edges -->\n    <line x1=\"-93.3\" y1=\"-11.5\" x2=\"-58.4\" y2=\"-43.0\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- red-yellow -->\n    <line x1=\"34.9\" y1=\"-31.5\" x2=\"-58.4\" y2=\"-43.0\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- opposite red axis bot -->\n    <line x1=\"-93.3\" y1=\"-11.5\" x2=\"-93.3\" y2=\"82.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- red-magenta -->\n    <line x1=\"34.9\" y1=\"-31.5\" x2=\"34.9\" y2=\"62.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- opposite blue axis rt -->\n    <line x1=\"-58.4\" y1=\"-43.0\" x2=\"-58.4\" y2=\"51.0\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- below white -->\n    <line x1=\"0.0\" y1=\"94.0\" x2=\"-93.3\" y2=\"82.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- blue-magenta -->\n    <line x1=\"0.0\" y1=\"94.0\" x2=\"34.9\" y2=\"62.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- blue-cyan -->\n    <line x1=\"-58.4\" y1=\"51.0\" x2=\"-93.3\" y2=\"82.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- magenta-white -->\n    <line x1=\"-58.4\" y1=\"51.0\" x2=\"34.9\" y2=\"62.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- cyan-white -->\n\n\n    <!-- excluded colours -->\n    <circle cx=\"0\" cy=\"47\" r=\"4\" fill=\"#008\" stroke=\"CanvasText\" />\n    <circle cx=\"17.45\" cy=\"31.25\" r=\"4\" fill=\"#088\" stroke=\"CanvasText\" />\n    <circle cx=\"17.45\" cy=\"-15.75\" r=\"4\" fill=\"#080\" stroke=\"CanvasText\" />\n    <circle cx=\"-46.65\" cy=\"41.25\" r=\"4\" fill=\"#808\" stroke=\"CanvasText\" />\n    <circle cx=\"34.9\" cy=\"-31.5\" r=\"4\" fill=\"#0f0\" stroke=\"CanvasText\" />\n    <circle cx=\"34.9\" cy=\"62.5\" r=\"4\" fill=\"#0ff\" stroke=\"CanvasText\" />\n    <circle cx=\"-93.3\" cy=\"82.5\" r=\"4\" fill=\"#f0f\" stroke=\"CanvasText\" />\n    <circle cx=\"0\" cy=\"94\" r=\"4\" fill=\"#00f\" stroke=\"CanvasText\" />\n\n\n\n\n\n\n\n\n    <!-- inner faces of tetrahedron -->\n    <polygon points=\"0,0 -58.4,51 -93.3,-11.5\" fill=\"#8888\" />\n    <polygon points=\"0,0 -93.3,-11.5 -58.4,-43\" fill=\"#8888\" />\n\n\n    <!-- edges of tetrahedron -->\n    <line x1=\"0\" y1=\"0\" x2=\"-93.3\" y2=\"-11.5\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- 0-9 -->\n    <line x1=\"0\" y1=\"0\" x2=\"-58.4\" y2=\"-43\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- 0-11 -->\n    <line x1=\"0\" y1=\"0\" x2=\"-58.4\" y2=\"51\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- 0-15 -->\n    <line x1=\"-93.3\" y1=\"-11.5\" x2=\"-58.4\" y2=\"-43\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- 9-11 -->\n    <line x1=\"-93.3\" y1=\"-11.5\" x2=\"-58.4\" y2=\"51\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- 9-15 -->\n    <line x1=\"-58.4\" y1=\"-43\" x2=\"-58.4\" y2=\"51\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- 11-15 -->\n\n\n    <!-- included colours of tetrahedron -->\n    <g id=\"tetrahedron_included_colours\">\n      <circle cx=\"0\" cy=\"0\" r=\"4\" fill=\"#000\" stroke=\"CanvasText\" />\n      <circle cx=\"-46.65\" cy=\"-5.75\" r=\"4\" fill=\"#800\" stroke=\"CanvasText\" />\n      <circle cx=\"-29.175\" cy=\"-21.5\" r=\"4\" fill=\"#880\" stroke=\"CanvasText\" />\n      <circle cx=\"-29.475\" cy=\"25.5\" r=\"4\" fill=\"#888\" stroke=\"CanvasText\" />\n      <circle cx=\"-93.3\" cy=\"-11.5\" r=\"4\" fill=\"#f00\" stroke=\"CanvasText\" />\n      <circle cx=\"-58.4\" cy=\"-43\" r=\"4\" fill=\"#ff0\" stroke=\"CanvasText\" />\n      <circle cx=\"-58.4\" cy=\"51.0\" r=\"4\" fill=\"#fff\" stroke=\"CanvasText\" />\n    </g>\n\n\n    <g id=\"tetrahedron_example\">\n      <line x1=\"-52\" y1=\"-12.95\" x2=\"-52.00\" y2=\"-24.38\" stroke=\"#888\" id=\"tetrahedron_example_spot_dangle\" stroke-dasharray=\"1\"></line>\n      <line x1=\"-69.52\" y1=\"-8.57\" x2=\"-52.00\" y2=\"-24.38\" stroke=\"#888\" id=\"tetrahedron_example_spot_bottom_r\" stroke-dasharray=\"2\"></line>\n      <line x1=\"17.52\" y1=\"-15.81\" x2=\"-52.00\" y2=\"-24.38\" stroke=\"#888\" id=\"tetrahedron_example_spot_bottom_g\" stroke-dasharray=\"2\"></line>\n      <circle cx=\"-52.00\" cy=\"-12.95\" r=\"2\" fill=\"rgb(190 128 31)\" stroke=\"Canvas\" id=\"tetrahedron_example_spot\"></circle>\n\n\n      <!-- <circle cx=\"0\" cy=\"0\" r=\"2\" fill=\"#801FBE\" stroke=\"CanvasText\" id=\"tetrahedron_example_spot_r\" />\n      <circle cx=\"0\" cy=\"0\" r=\"2\" fill=\"#801FBE\" stroke=\"CanvasText\" id=\"tetrahedron_example_spot_g\" />\n      <circle cx=\"0\" cy=\"0\" r=\"2\" fill=\"#801FBE\" stroke=\"CanvasText\" id=\"tetrahedron_example_spot_b\" /> -->\n\n\n    </g>\n\n\n    <g transform=\"scale(0.5, -0.5)\" fill=\"CanvasText\">\n\n\n      <text x=\"10\" y=\"0\">0</text>\n      <text x=\"-93.3\" y=\"0\">1</text>\n      <text y=\"31.5\" x=\"45\">2</text>\n      <text x=\"-50\" y=\"35\">3</text>\n      <text y=\"-100\" x=\"5\">4</text>\n      <text y=\"-90\" x=\"-85\">5</text>\n      <text y=\"-70\" x=\"40\">6</text>\n\n\n      <text y=\"-28.5\" x=\"-52.475\">7</text>\n\n\n      <text x=\"-205\" y=\"20\">9</text>\n      <text x=\"75\" y=\"80\">10</text>\n      <text x=\"-125\" y=\"110\">11</text>\n      <text x=\"0\" y=\"-195\">12</text>\n      <text y=\"-170\" x=\"-215\">13</text>\n      <text x=\"75\" y=\"-130\">14</text>\n      <text x=\"-125\" y=\"-115\">15</text>\n\n\n\n\n\n\n\n\n        </g>\n  </g>\n </svg>\n <figcaption>Tetrahedral subregion 0, bounded by vertices 0, 9, 11, and 15, includes colours 0, 1, 3, 7, 9, 11, and 15. The running example colour is shown plotted into this space.</figcaption>\n</figure>\n\n\n<h3 id=\"Finding-the-subspace\">Finding the subspace</h3>\n<p>The subspace is determined by inspecting the coordinates. The <code>ComputeSubspace</code> function in the patent code is written very opaquely, but what it comes down to is:</p>\n<ul>\n <li class=\"if_subspace_0\">You're in subspace 0 if r &lt; 128; or</li>\n <li class=\"if_subspace_1\">You're in subspace 1 if r + g &lt; 256; or</li>\n <li class=\"if_subspace_2\"> You're in subspace 2 if r + b &lt; 256; or</li>\n <li class=\"if_subspace_3\">You're in subspace 3 otherwise.</li>\n</ul>\n\n\n<p>For the running example, we&#x27;re thus in subspace\n<span class=\"subspace d\">2</span>.</p>\n\n<p>Remembering that the values are ordered r \u2265 g \u2265 b, this subspace calculation is essentially &quot;how bright is this colour&quot;.</p>\n<h3 id=\"Determining-the-vertex-colours\">Determining the vertex colours</h3>\n<p>The four colours to use are the vertices of the subspace tetrahedron. Every subspace contains grey (the centre of the overall colour cube) and three others from those included in tetrahedral space 0. Subspace 0 is all dark colours, subspace 3 is all bright colours, and the others are in between.</p>\n<ul>\n <li class=\"if_subspace_0\">Subspace 0 uses colours 3, 0, 1, 7</li>\n <li class=\"if_subspace_1\">Subspace 1 uses colours 3, 1, 9, 7</li>\n <li class=\"if_subspace_2\">Subspace 2 uses colours 3, 9, 11, 7</li>\n <li class=\"if_subspace_3\">Subspace 3 uses colours 9, 7, 11, 15</li>\n</ul>\n\n\n<p>This corrects subspaces 0\u20132 from the patent code, which lists colour 2 instead of 3 (table 3 has them correct). For the example, the colour values are\n<span class=\"v1 d\">3</span>, <span class=\"v2 d\">9</span>, <span class=\"v3 d\">11</span>, and <span class=\"v4 d\">7</span>.\nRemember, these are the colours as reflected into space 0 at the start, not the final dither colours.</p>\n<figure>\n <svg xmlns=\"http://w3.org\" viewBox=\"40 0 160 170\" id=\"tetra_subspace\">\n  <g transform=\"translate(150, 110) scale(1, -1)\">\n    <!-- primary axes -->\n    <line x1=\"0\" y1=\"0\" x2=\"-93.3\" y2=\"-11.5\" stroke=\"#ff0000\" stroke-width=\"3\" stroke-linecap=\"round\"/> <!-- Red Axis -->\n    <line x1=\"0\" y1=\"0\" x2=\"34.9\" y2=\"-31.5\" stroke=\"#00aa00\" stroke-width=\"3\" stroke-linecap=\"round\"/> <!-- Green Axis -->\n    <line x1=\"0\" y1=\"0\" x2=\"0.0\" y2=\"94.0\" stroke=\"#0000ff\" stroke-width=\"3\" stroke-linecap=\"round\"/> <!-- Blue Axis -->\n\n\n    <!-- Remaining wireframe edges -->\n    <line x1=\"-93.3\" y1=\"-11.5\" x2=\"-58.4\" y2=\"-43.0\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- red-yellow -->\n    <line x1=\"34.9\" y1=\"-31.5\" x2=\"-58.4\" y2=\"-43.0\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- opposite red axis bot -->\n    <line x1=\"-93.3\" y1=\"-11.5\" x2=\"-93.3\" y2=\"82.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- red-magenta -->\n    <line x1=\"34.9\" y1=\"-31.5\" x2=\"34.9\" y2=\"62.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- opposite blue axis rt -->\n    <line x1=\"-58.4\" y1=\"-43.0\" x2=\"-58.4\" y2=\"51.0\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- below white -->\n    <line x1=\"0.0\" y1=\"94.0\" x2=\"-93.3\" y2=\"82.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- blue-magenta -->\n    <line x1=\"0.0\" y1=\"94.0\" x2=\"34.9\" y2=\"62.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- blue-cyan -->\n    <line x1=\"-58.4\" y1=\"51.0\" x2=\"-93.3\" y2=\"82.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- magenta-white -->\n    <line x1=\"-58.4\" y1=\"51.0\" x2=\"34.9\" y2=\"62.5\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\" /> <!-- cyan-white -->\n\n\n    <!-- excluded colours -->\n    <circle cx=\"0\" cy=\"47\" r=\"4\" fill=\"#008\" stroke=\"CanvasText\" />\n    <circle cx=\"17.45\" cy=\"31.25\" r=\"4\" fill=\"#088\" stroke=\"CanvasText\" />\n    <circle cx=\"17.45\" cy=\"-15.75\" r=\"4\" fill=\"#080\" stroke=\"CanvasText\" />\n    <circle cx=\"-46.65\" cy=\"41.25\" r=\"4\" fill=\"#808\" stroke=\"CanvasText\" />\n    <circle cx=\"34.9\" cy=\"-31.5\" r=\"4\" fill=\"#0f0\" stroke=\"CanvasText\" />\n    <circle cx=\"34.9\" cy=\"62.5\" r=\"4\" fill=\"#0ff\" stroke=\"CanvasText\" />\n    <circle cx=\"-93.3\" cy=\"82.5\" r=\"4\" fill=\"#f0f\" stroke=\"CanvasText\" />\n    <circle cx=\"0\" cy=\"94\" r=\"4\" fill=\"#00f\" stroke=\"CanvasText\" />\n\n\n\n\n\n\n\n\n    <!-- inner faces of tetrahedron -->\n    <!-- <polygon points=\"0,0 -58.4,51 -93.3,-11.5\" fill=\"#8888\" />\n    <polygon points=\"0,0 -93.3,-11.5 -58.4,-43\" fill=\"#8888\" /> -->\n\n\n    <!-- edges of tetrahedron -->\n    <line class=\"subspace_edge\" x1=\"-29.175\" y1=\"-21.5\" x2=\"-93.3\" y2=\"-11.5\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\"></line>\n    <line class=\"subspace_edge\" x1=\"-29.175\" y1=\"-21.5\" x2=\"-58.4\" y2=\"-43\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\"></line>\n    <line class=\"subspace_edge\" x1=\"-29.175\" y1=\"-21.5\" x2=\"-29.475\" y2=\"25.5\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\"></line>\n    <line class=\"subspace_edge\" x1=\"-93.3\" y1=\"-11.5\" x2=\"-58.4\" y2=\"-43\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\"></line>\n    <line class=\"subspace_edge\" x1=\"-93.3\" y1=\"-11.5\" x2=\"-29.475\" y2=\"25.5\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\"></line>\n    <line class=\"subspace_edge\" x1=\"-58.4\" y1=\"-43\" x2=\"-29.475\" y2=\"25.5\" stroke=\"CanvasText\" stroke-width=\"1.5\" stroke-linecap=\"round\"></line>\n\n\n\n\n    <!-- included colours of tetrahedron -->\n    <circle cx=\"0\" cy=\"0\" r=\"4\" fill=\"#000\" stroke=\"CanvasText\" />\n    <circle cx=\"-46.65\" cy=\"-5.75\" r=\"4\" fill=\"#800\" stroke=\"CanvasText\" />\n    <circle cx=\"-29.175\" cy=\"-21.5\" r=\"4\" fill=\"#880\" stroke=\"CanvasText\" />\n    <circle cx=\"-29.475\" cy=\"25.5\" r=\"4\" fill=\"#888\" stroke=\"CanvasText\" />\n    <circle cx=\"-93.3\" cy=\"-11.5\" r=\"4\" fill=\"#f00\" stroke=\"CanvasText\" />\n    <circle cx=\"-58.4\" cy=\"-43\" r=\"4\" fill=\"#ff0\" stroke=\"CanvasText\" />\n    <circle cx=\"-58.4\" cy=\"51.0\" r=\"4\" fill=\"#fff\" stroke=\"CanvasText\" />\n\n\n    <!-- example colour plotting -->\n    <line x1=\"-52\" y1=\"-12.95\" x2=\"-52.00\" y2=\"-24.38\" stroke=\"#888\" id=\"subspace_example_spot_dangle\" stroke-dasharray=\"1\"></line>\n    <line x1=\"-69.52\" y1=\"-8.57\" x2=\"-52.00\" y2=\"-24.38\" stroke=\"#888\" id=\"subspace_example_spot_bottom_r\" stroke-dasharray=\"2\"></line>\n    <line x1=\"17.52\" y1=\"-15.81\" x2=\"-52.00\" y2=\"-24.38\" stroke=\"#888\" id=\"subspace_example_spot_bottom_g\" stroke-dasharray=\"2\"></line>\n    <circle cx=\"-52.00\" cy=\"-12.95\" r=\"2\" fill=\"rgb(190 128 31)\" stroke=\"CanvasText\" id=\"subspace_example_spot\"></circle>\n\n\n\n\n\n\n    <g transform=\"scale(0.5, -0.5)\" fill=\"CanvasText\">\n\n\n      <text x=\"10\" y=\"0\">0</text>\n      <text x=\"-93.3\" y=\"0\">1</text>\n      <text y=\"31.5\" x=\"45\">2</text>\n      <text x=\"-50\" y=\"35\">3</text>\n      <text y=\"-100\" x=\"5\">4</text>\n      <text y=\"-90\" x=\"-85\">5</text>\n      <text y=\"-70\" x=\"40\">6</text>\n\n\n      <text y=\"-28.5\" x=\"-52.475\">7</text>\n\n\n      <text x=\"-205\" y=\"20\">9</text>\n      <text x=\"75\" y=\"80\">10</text>\n      <text x=\"-125\" y=\"110\">11</text>\n      <text x=\"0\" y=\"-195\">12</text>\n      <text y=\"-170\" x=\"-215\">13</text>\n      <text x=\"75\" y=\"-130\">14</text>\n      <text x=\"-125\" y=\"-115\">15</text>\n\n\n\n\n\n\n\n\n        </g>\n  </g>\n </svg>\n <figcaption>Colour cube with the example colour's tetrahedral subspace <span class=\"subspace d\">2</span> highlighted.</figcaption>\n</figure>\n\n\n<h3 id=\"Scaling-the-values\">Scaling the values</h3>\n<p>Every colour coordinate is scaled down from 0-255 to 0-64 inclusive, with 255 going to 64, 251-254 to 63, and 0-2 going to 0, which isn&#x27;t quite the 6-bit mapping you&#x27;d expect. The calculation is ((n / 2) + (n % 2) / 2, or more straightforwardly the first division rounds up rather than truncating.</p>\n<math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><semantics><mrow><mrow><mi mathvariant=\"normal\">s</mi><mi mathvariant=\"normal\">c</mi><mi mathvariant=\"normal\">a</mi><mi mathvariant=\"normal\">l</mi><mi mathvariant=\"normal\">e</mi></mrow><mo stretchy=\"false\">(</mo><mi>n</mi><mo stretchy=\"false\">)</mo><mo>=</mo><mrow><mo fence=\"true\">\u230a</mo><mfrac><mrow><mo fence=\"true\">\u2308</mo><mfrac><mi>n</mi><mn>2</mn></mfrac><mo fence=\"true\">\u2309</mo></mrow><mn>2</mn></mfrac><mo fence=\"true\">\u230b</mo></mrow></mrow></semantics>\n</math>\n\n\n<pre><code>r = floor(ceil(r / 2) / 2)\ng = floor(ceil(g / 2) / 2)\nb = floor(ceil(b / 2) / 2)</code></pre>\n<p>The example&#x27;s scaled-down coordinates are r =\n<span class=\"scaledR d\">47</span>, g = <span class=\"scaledG d\">32</span>, b = <span class=\"scaledB d\">8</span>.</p>\n\n<h3 id=\"Transforming-the-coordinates\">Transforming the coordinates</h3>\n<p>Next, a coordinate transformation and inner product is used to calculate the number of pixels that will be in each colour. The coordinates are transformed by the origin of the subspace, which is (64, 0, 0) for subspace 3 and (32, 32, 0) in the others. The net effect is that in subspace 3 the calculation uses <code>r - 64</code> and in the others it uses <code>r - 32</code> and <code>g - 32</code>. These transformed values are multiplied by values from a matrix to calculate three counts, but the matrix from the code listing has some incorrect values. The code also mixes up its <code>tempB</code> and <code>tempG</code> variables. The corrected and simplified calculations are</p>\n<ul>\n <li class=\"if_subspace_0\">In subspace 0:\n    <math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mrow><mo fence=\"true\">[</mo><mtable><mtr><mtd><mo>-</mo><mn>2</mn></mtd><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd></mtr><mtr><mtd><mn>2</mn></mtd><mtd><mn>2</mn></mtd><mtd><mn>0</mn></mtd></mtr><mtr><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd><mtd><mn>2</mn></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo fencee=\"true\">[</mo><mtable><mtr><mtd><mi>R</mi></mtd></mtr><mtr><mtd><mi>G</mi></mtd></mtr><mtr><mtd><mi>B</mi></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo>=</mo><mo fence=\"true\">[</mo><mtable><mtr><mtd><msub><mi>V</mi><mn>0</mn></msub></mtd></mtr><mtr><mtd><msub><mi>V</mi><mn>1</mn></msub></mtd></mtr><mtr><mtd><msub><mi>V</mi><mn>7</mn></msub></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo>=</mo><mo fence=\"true\">[</mo><mtable><mtr><mtd><msub><mi>c</mi><mn>1</mn></msub></mtd></mtr><mtr><mtd><msub><mi>c</mi><mn>2</mn></msub></mtd></mtr><mtr><mtd><msub><mi>c</mi><mn>3</mn></msub></mtd></mtr></mtable><mo fence=\"true\">]</mo></mrow></math>\n    <math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mrow><msub><mi>V</mi><mn>3</mn></msub><mo>=</mo><mn>64</mn><mo>-</mo><msub><mi>V</mi><mn>0</mn></msub><mo>-</mo><msub><mi>V</mi><mn>1</mn></msub><mo>-</mo><msub><mi>V</mi><mn>7</mn></msub></mrow></math>\n    <pre><code>c1 = (r - 32) * -2\nc2 = 2 * (r - 32) - 2 * (g - 32)\nc3 = b * 2</code></pre>\n </li>\n\n\n <li class=\"if_subspace_1\">In subspace 1:\n    <math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mrow><mo fence=\"true\">[</mo><mtable><mtr><mtd><mo>-</mo><mn>2</mn></mtd><mtd><mo>-</mo><mn>2</mn></mtd><mtd><mn>0</mn></mtd></mtr><mtr><mtd><mn>2</mn></mtd><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd></mtr><mtr><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd><mtd><mn>2</mn></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo fencee=\"true\">[</mo><mtable><mtr><mtd><mi>R</mi></mtd></mtr><mtr><mtd><mi>G</mi></mtd></mtr><mtr><mtd><mi>B</mi></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo>=</mo><mo fence=\"true\">[</mo><mtable><mtr><mtd><msub><mi>V</mi><mn>1</mn></msub></mtd></mtr><mtr><mtd><msub><mi>V</mi><mn>9</mn></msub></mtd></mtr><mtr><mtd><msub><mi>V</mi><mn>7</mn></msub></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo>=</mo><mo fence=\"true\">[</mo><mtable><mtr><mtd><msub><mi>c</mi><mn>1</mn></msub></mtd></mtr><mtr><mtd><msub><mi>c</mi><mn>2</mn></msub></mtd></mtr><mtr><mtd><msub><mi>c</mi><mn>3</mn></msub></mtd></mtr></mtable><mo fence=\"true\">]</mo></mrow></math>\n    <math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mrow><msub><mi>V</mi><mn>3</mn></msub><mo>=</mo><mn>64</mn><mo>-</mo><msub><mi>V</mi><mn>1</mn></msub><mo>-</mo><msub><mi>V</mi><mn>9</mn></msub><mo>-</mo><msub><mi>V</mi><mn>7</mn></msub></mrow></math>\n    <pre><code>c1 = (r - 32) * -2 - 2 * (g - 32)\nc2 = (r - 32) * 2\nc3 = b * 2</code></pre>\n </li>\n\n\n <li class=\"if_subspace_2\">In subspace 2:\n    <math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mrow><mo fence=\"true\">[</mo><mtable><mtr><mtd><mn>1</mn></mtd><mtd><mo>-</mo><mn>1</mn></mtd><mtd><mn>0</mn></mtd></mtr><mtr><mtd><mn>1</mn></mtd><mtd><mn>1</mn></mtd><mtd><mn>0</mn></mtd></mtr><mtr><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd><mtd><mn>2</mn></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo fencee=\"true\">[</mo><mtable><mtr><mtd><mi>R</mi></mtd></mtr><mtr><mtd><mi>G</mi></mtd></mtr><mtr><mtd><mi>B</mi></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo>=</mo><mo fence=\"true\">[</mo><mtable><mtr><mtd><msub><mi>V</mi><mn>9</mn></msub></mtd></mtr><mtr><mtd><msub><mi>V</mi><mn>B</mn></msub></mtd></mtr><mtr><mtd><msub><mi>V</mi><mn>7</mn></msub></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo>=</mo><mo fence=\"true\">[</mo><mtable><mtr><mtd><msub><mi>c</mi><mn>1</mn></msub></mtd></mtr><mtr><mtd><msub><mi>c</mi><mn>2</mn></msub></mtd></mtr><mtr><mtd><msub><mi>c</mi><mn>3</mn></msub></mtd></mtr></mtable><mo fence=\"true\">]</mo></mrow></math>\n    <math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mrow><msub><mi>V</mi><mn>3</mn></msub><mo>=</mo><mn>64</mn><mo>-</mo><msub><mi>V</mi><mn>9</mn></msub><mo>-</mo><msub><mi>V</mi><mn>B</mn></msub><mo>-</mo><msub><mi>V</mi><mn>7</mn></msub></mrow></math>\n    <pre><code>c1 = r - g\nc2 = r + g - 64\nc3 = b * 2</code></pre>\n </li>\n\n\n <li class=\"if_subspace_3\">In subspace 3, which isn't correct either in the patent code or in the mathematics of table 5, so you have to figure it out by fiddling:\n    <math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mrow><mo fence=\"true\">[</mo><mtable><mtr><mtd><mo>-</mo><mn>2</mn></mtd><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd></mtr><mtr><mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd><mtd><mo>-</mo><mn>1</mn></mtd></mtr><mtr><mtd><mn>1</mn></mtd><mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo fencee=\"true\">[</mo><mtable><mtr><mtd><mi>R</mi></mtd></mtr><mtr><mtd><mi>G</mi></mtd></mtr><mtr><mtd><mi>B</mi></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo>=</mo><mo fence=\"true\">[</mo><mtable><mtr><mtd><msub><mi>V</mi><mn>7</mn></msub></mtd></mtr><mtr><mtd><msub><mi>V</mi><mn>B</mn></msub></mtd></mtr><mtr><mtd><msub><mi>V</mi><mn>F</mn></msub></mtd></mtr></mtable><mo fence=\"true\">]</mo><mo>=</mo><mo fence=\"true\">[</mo><mtable><mtr><mtd><msub><mi>c</mi><mn>1</mn></msub></mtd></mtr><mtr><mtd><msub><mi>c</mi><mn>2</mn></msub></mtd></mtr><mtr><mtd><msub><mi>c</mi><mn>3</mn></msub></mtd></mtr></mtable><mo fence=\"true\">]</mo></mrow></math>\n    <math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mrow><msub><mi>V</mi><mn>9</mn></msub><mo>=</mo><mn>64</mn><mo>-</mo><msub><mi>V</mi><mn>7</mn></msub><mo>-</mo><msub><mi>V</mi><mn>B</mn></msub><mo>-</mo><msub><mi>V</mi><mn>F</mn></msub></mrow></math>\n    <pre><code>c1 = (r - 64) * -2\nc2 = g - b\nc3 = r + b - 64</code></pre>\n    <span style=\"color: CanvasText; font-weight: normal;\">The patent would have <code>c3 = r + g + b - 64</code>, which gives out-of-range results for bright colours and just wrong ones for the rest.</span>\n </li>\n</ul>\n\n\n<p>For the example, in subspace\n<span class=\"subspace d\">2</span>,\nthe result is c1 =\n<span class=\"c1 d\">15</span>, c2 = <span class=\"c2 d\">15</span>, c3 = <span class=\"c3 d\">16</span>.</p>\n\n<h3 id=\"Pixel-counts\">Pixel counts</h3>\n<p>These computed values determine the number of pixels for each specific colour. There can be between one and four colours with pixels allocated to them. A table of (colour, count) pairs is calculated as:</p>\n<ul>\n<li>If c1 + c2 + c3 &lt; 64, the first colour from the list gets 64 - c1 - c2 - c3 pixels, otherwise 0.</li>\n<li>The other three colours are assigned c1, c2, c3 pixels respectively.</li>\n</ul>\n<p>In the example,\n<span class=\"c1 d\">15</span> +\n<span class=\"c2 d\">15</span> +\n<span class=\"c3 d\">16</span> =\n<span class=\"csum d\">46</span>, so there are\n<span class=\"cdiff d\">18</span> pixels assigned to colour\n<span class=\"v1 d\">3</span>,\n<span class=\"c1 d\">15</span> to <span class=\"v2 d\">9</span>,\n<span class=\"c2 d\">15</span> to <span class=\"v3 d\">11</span>, and\n<span class=\"c3 d\">16</span> to <span class=\"v4 d\">7</span>.</p>\n<table>\n <tr><th>Colour</th><th>Count</th><th>Bit pattern</th></tr>\n <tr><td><span class=\"swatch v1\">3</span></td><td><span class=\"cdiff\">18</span></td><td><span class=\"b1\">0011</span></td></tr>\n <tr><td><span class=\"swatch v2\">9</span></td><td><span class=\"c1\">15</span></td><td><span class=\"b2\">1001</span></td></tr>\n <tr><td><span class=\"swatch v3\">11</span></td><td><span class=\"c2\">15</span></td><td><span class=\"b3\">1011</span></td></tr>\n <tr><td><span class=\"swatch v4\">7</span></td><td><span class=\"c3\">16</span></td><td><span class=\"b4\">0111</span></td></tr>\n</table>\n\n\n<h3 id=\"Projecting-these-pseudocolours-back\">Projecting these pseudo-colours back</h3>\n<p>These &quot;colours&quot; aren&#x27;t the ones used for the final dithering output, however: instead, their bits are permuted according to the three &quot;swap&quot; operations determined earlier.</p>\n<p>For each colour:</p>\n<ul>\n<li>R corresponds to the 1 bit of the colour.</li>\n<li>G corresponds to the 2 bit of the colour.</li>\n<li>B corresponds to the 4 bit of the colour.</li>\n<li>I corresponds to the 8 bit of the colour.</li>\n</ul>\n<p>The <code>SwapRG</code>/<code>SwapGB</code>/<code>SwapRB</code> values chosen at the start are now used to rearrange these, in that order. That is, these actually correspond to swapping the low two bits, the 2 &amp; 4 bits, or the 1 &amp; 4 bits respectively, to match when the numeric red/green/blue channels were swapped at the start. The 8 bit is left as it is.</p>\n<pre><code>if swapRG:\n    R, G = G, R\nif swapGB:\n    G, B = B, G\nif swapRB:\n    R, B = B, R\ncolour = R + G * 2 + B * 4 + I * 8</code></pre>\n\n<p>Since the example has swapRG =\n<span class=\"swapRG d\">false</span>, swapGB =\n<span class=\"swapGB d\">true</span>, and swapRB =\n<span class=\"swapRB d\">true</span>,\nthe calculation runs like this:</p>\n<table>\n <tr><th>Old</th><th>I (8)</th><th>B (4)</th><th>G (2)</th><th>R (1)</th><th>New</th><th>Count</th><th>Colour</th></tr>\n <tr><td><span class=\"b1\">0011</span></td><td class=\"b1_8\">0</td><td class=\"b1_4\">0</td><td class=\"b1_2\">1</td><td class=\"b1_1\">1</td><td class=\"swapped_b1\">0101</td><td><span class=\"cdiff\">18</span></td><td><span class=\"swatch real_v1\">5</span></td></tr>\n <tr><td><span class=\"b2\">1001</span></td><td class=\"b2_8\">1</td><td class=\"b2_4\">0</td><td class=\"b2_2\">0</td><td class=\"b2_1\">1</td><td class=\"swapped_b2\">1100</td><td><span class=\"c1\">15</span></td><td><span class=\"swatch real_v2\">12</span></td></tr>\n <tr><td><span class=\"b3\">1011</span></td><td class=\"b3_8\">1</td><td class=\"b3_4\">0</td><td class=\"b3_2\">1</td><td class=\"b3_1\">1</td><td class=\"swapped_b3\">1101</td><td><span class=\"c2\">15</span></td><td><span class=\"swatch real_v3\">13</span></td></tr>\n <tr><td><span class=\"b4\">0111</span></td><td class=\"b4_8\">0</td><td class=\"b4_4\">1</td><td class=\"b4_2\">1</td><td class=\"b4_1\">1</td><td class=\"swapped_b4\">0111</td><td><span class=\"c3\">16</span></td><td><span class=\"swatch real_v4\">7</span></td></tr>\n</table>\n\n\n<p>These are now the palette colours for the final dither pattern. The table has the same counts corresponding to the new colours.</p>\n<h3 id=\"Sorting-the-colours-by-decreasing-darkness\">Sorting the colours by decreasing darkness</h3>\n<p>The table is then sorted according to a predefined ordering of darkness, with the darkest first. The colours ultimately sort into this order:</p>\n<ul id=\"sort-order\" data-colours=\"\">\n <li class=\"if_colour_0\"> <span class=\"palette\" data-rgbi=\"0\"></span> 0</li>\n <li class=\"if_colour_4\"> <span class=\"palette\" data-rgbi=\"4\"></span> 4</li>\n <li class=\"if_colour_1\"> <span class=\"palette\" data-rgbi=\"1\"></span> 1</li>\n <li class=\"if_colour_2\"> <span class=\"palette\" data-rgbi=\"2\"></span> 2</li>\n <li class=\"if_colour_5\"> <span class=\"palette\" data-rgbi=\"5\"></span> 5</li>\n <li class=\"if_colour_6\"> <span class=\"palette\" data-rgbi=\"6\"></span> 6</li>\n <li class=\"if_colour_3\"> <span class=\"palette\" data-rgbi=\"3\"></span> 3</li>\n <li class=\"if_colour_7\"> <span class=\"palette\" data-rgbi=\"7\"></span> 7</li>\n <li class=\"if_colour_12\"><span class=\"palette\" data-rgbi=\"12\"></span> 12</li>\n <li class=\"if_colour_9\"> <span class=\"palette\" data-rgbi=\"9\"></span> 9</li>\n <li class=\"if_colour_10\"><span class=\"palette\" data-rgbi=\"10\"></span> 10</li>\n <li class=\"if_colour_13\"><span class=\"palette\" data-rgbi=\"13\"></span> 13</li>\n <li class=\"if_colour_14\"><span class=\"palette\" data-rgbi=\"14\"></span> 14</li>\n <li class=\"if_colour_11\"><span class=\"palette\" data-rgbi=\"11\"></span> 11</li>\n <li class=\"if_colour_15\"><span class=\"palette\" data-rgbi=\"15\"></span> 15</li>\n</ul>\n\n\n<p>That is, dark blue is earlier and dark yellow is later, and the same with intense blue and intense yellow.</p>\n<p>The patent body has a table of the sorted elements for its example, but it&#x27;s in the wrong order and has the wrong counts.\nIf you skip this step entirely, the end result looks pretty much as good of a match to the target colour; <a href=\"https://retrocomputing.stackexchange.com/a/18256\">this Stack Exchange answer</a> suggests the ordering (presumably combined with the specific Bayer matrix below) is to avoid discontinuities when transitioning between nearby colours in different (sub)spaces, but I haven&#x27;t found any confirmation of that. In any case, to match the expected behaviour it&#x27;s necessary.</p>\n\n<p>For our example, the final order should be:</p>\n<table>\n <tr><th>Colour</th><th>Count</th></tr>\n <tr><td class=\"swatch order_v1\">5</td><td class=\"order_ct1\">18</td></tr>\n <tr><td class=\"swatch order_v2\">7</td><td class=\"order_ct2\">16</td></tr>\n <tr><td class=\"swatch order_v3\">12</td><td class=\"order_ct3\">15</td></tr>\n <tr><td class=\"swatch order_v4\">13</td><td class=\"order_ct4\">15</td></tr>\n</table>\n\n\n<h3 id=\"Filling-the-pattern\">Filling the pattern</h3>\n<p>Finally, the 8x8 grid is filled with the chosen colours. Each colour appears as many times as its calculated count, spread throughout the pattern according to a pre-determined Bayer matrix. The matrix of allocation orders <sup>^2</sup> is given as:</p>\n<math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mrow><mo fence=\"true\">(</mo><mtable><mtr><mtd><mn>0</mn></mtd><mtd><mn>32</mn></mtd><mtd><mn>8</mn></mtd><mtd><mn>40</mn></mtd><mtd><mn>2</mn></mtd><mtd><mn>34</mn></mtd><mtd><mn>10</mn></mtd><mtd><mn>42</mn></mtd></mtr><mtr><mtd><mn>48</mn></mtd><mtd><mn>16</mn></mtd><mtd><mn>56</mn></mtd><mtd><mn>24</mn></mtd><mtd><mn>50</mn></mtd><mtd><mn>18</mn></mtd><mtd><mn>58</mn></mtd><mtd><mn>26</mn></mtd></mtr><mtr><mtd><mn>12</mn></mtd><mtd><mn>44</mn></mtd><mtd><mn>4</mn></mtd><mtd><mn>36</mn></mtd><mtd><mn>14</mn></mtd><mtd><mn>46</mn></mtd><mtd><mn>6</mn></mtd><mtd><mn>38</mn></mtd></mtr><mtr><mtd><mn>60</mn></mtd><mtd><mn>28</mn></mtd><mtd><mn>52</mn></mtd><mtd><mn>20</mn></mtd><mtd><mn>62</mn></mtd><mtd><mn>30</mn></mtd><mtd><mn>54</mn></mtd><mtd><mn>22</mn></mtd></mtr><mtr><mtd><mn>3</mn></mtd><mtd><mn>35</mn></mtd><mtd><mn>11</mn></mtd><mtd><mn>43</mn></mtd><mtd><mn>1</mn></mtd><mtd><mn>33</mn></mtd><mtd><mn>9</mn></mtd><mtd><mn>41</mn></mtd></mtr><mtr><mtd><mn>51</mn></mtd><mtd><mn>19</mn></mtd><mtd><mn>59</mn></mtd><mtd><mn>27</mn></mtd><mtd><mn>49</mn></mtd><mtd><mn>17</mn></mtd><mtd><mn>57</mn></mtd><mtd><mn>25</mn></mtd></mtr><mtr><mtd><mn>15</mn></mtd><mtd><mn>47</mn></mtd><mtd><mn>7</mn></mtd><mtd><mn>39</mn></mtd><mtd><mn>13</mn></mtd><mtd><mn>45</mn></mtd><mtd><mn>5</mn></mtd><mtd><mn>37</mn></mtd></mtr><mtr><mtd><mn>63</mn></mtd><mtd><mn>31</mn></mtd><mtd><mn>55</mn></mtd><mtd><mn>23</mn></mtd><mtd><mn>61</mn></mtd><mtd><mn>29</mn></mtd><mtd><mn>53</mn></mtd><mtd><mn>21</mn></mtd></mtr></mtable><mo fence=\"true\">)</mo></mrow>\n</math>\n\n\n<p>This corresponds to filling in position (x, y) in this order:</p>\n<pre><code>[\n    (0, 0), (4, 4), (4, 0), (0, 4),\n    (2, 2), (6, 6), (6, 2), (2, 6),\n    (2, 0), (6, 4), (6, 0), (2, 4),\n    (0, 2), (4, 6), (4, 2), (0, 6),\n    (1, 1), (5, 5), (5, 1), (1, 5),\n    (3, 3), (7, 7), (7, 3), (3, 7),\n    (3, 1), (7, 5), (7, 1), (3, 5),\n    (1, 3), (5, 7), (5, 3), (1, 7),\n    (1, 0), (5, 4), (5, 0), (1, 4),\n    (3, 2), (7, 6), (7, 2), (3, 6),\n    (3, 0), (7, 4), (7, 0), (3, 4),\n    (1, 2), (5, 6), (5, 2), (1, 6),\n    (0, 1), (4, 5), (4, 1), (0, 5),\n    (2, 3), (6, 7), (6, 3), (2, 7),\n    (2, 1), (6, 5), (6, 1), (2, 5),\n    (0, 3), (4, 7), (4, 3), (0, 7)\n]</code></pre>\n<p>Each colour gets as many pixels as the count calculated in the colour count table,\nassigned in that order. For the example, that&#x27;s a final pattern of:</p>\n<img id=\"big_pattern\" style=\"float: right;\" alt=\"\" /><pre id=\"finalpattern\">\n  5   7   5  12   5  12   5  12\n 12   5  13   7  13   7  13   7\n  5  12   5  12   5  12   5  12\n 13   7  13   7  13   7  13   7\n  5  12   5  12   5   7   5  12\n 13   7  13   7  13   5  13   7\n  5  12   5  12   5  12   5  12\n 13   7  13   7  13   7  13   7\n</pre>\n\n\n\n<p>The final pattern, compared to the solid colour, looks like this:</p>\n<figure><a href=\"https://michael.homer.nz/notes/dither-windows-3/example.png\"><img src=\"https://michael.homer.nz/notes/dither-windows-3/example.png\" alt=\"Zoomed-in view of the final 8x8 pattern.\" /></a><figcaption>Zoomed-in view of the final 8x8 pattern.</figcaption></figure>\n\n<p>It&#x27;s mostly a pretty good match, though certainly more so on modern high-resolution displays.</p>\n\n\n<h2 id=\"Implementation\">Implementation</h2>\n<p>I built a web-based tool for calculating and displaying these patterns that should run in any recent browser.</p>\n<div class=\"biglink\"><a href=\"https://mwh.nz/dither/\"><div><h3>Dither</h3>Choose a colour, and see a swatch of the colour, one of the resulting dither pattern beside it, and an 8x-zoomed view of the pattern, calculated in-browser.</div></a></div>\n<figure><a href=\"https://michael.homer.nz/notes/dither-windows-3/screenshot.png\"><img src=\"https://michael.homer.nz/notes/dither-windows-3/screenshot.png\" alt=\"Screenshot of the tool user interface\" /></a><figcaption>The tool also allows downloading a PNG image of the dither pattern, copying a data: URI, or copying the image itself, and charts the distribution of each colour in the pattern.</figcaption></figure>\n<p>The library behind it is also available:</p>\n<div class=\"biglink\"><a href=\"https://github.com/mwh/dither3x\"><div><h3>mwh/dither3x on GitHub</h3>Generate accurate Windows 3.x dither patterns for any colour in-browser.\nA JavaScript library.</div></a></div>\n<p>It tries to tread the line between paralleling the patent&#x27;s code \u2014 matching the function names and structure \u2014 and being understandable. It&#x27;s reasonably heavily commented.</p>\n<p>While putting this article together I also built a (much-less-tested) Python version that&#x27;s available for what it is:</p>\n<div class=\"biglink\"><a href=\"https://michael.homer.nz/notes/dither-windows-3/dither.py\"><div><h3>Python implementation of the algorithm.</h3>This implementation is a pretty close match to the description here, and it&#x27;s intended to be correct but has not been tested as much as the other.</div></a></div>\n<p>The HTML version of this article also has a live implementation backing the running interactive example.</p>\n<section class=\"endnotes\">\n<h2>Endnotes</h2>\n<aside>^1 Differences in display technology make these more mismatched. To produce correct rendering on modern displays, dark colours in the screenshot have been brightened by the emulator from their linear RGB values by about 33%. For the rest of this article all colours will operate in modern sRGB space, but standard gamma corrections could be applied to both palette and target colours to approximate rendering of the time. The focus here is on replicating the pattern.</aside>\n<aside>^2 Strictly, the instruction is to set every unfilled pixel where the matrix value is less than the total pixels set previously plus the number of pixels for this colour, rather than order of filling, and the patent algorithm assumes four traversals of the matrix.\n<math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><msub><mi>I</mi><mrow><mi>x</mi><mo>,</mo><mi>y</mi></mrow></msub><mo>&#x2190;</mo><mi>c</mi><mo>&#xA0;</mo><mtext>\ud835\udc22\ud835\udc1f</mtext><mo>&#xA0;</mo><msub><mi>I</mi><mrow><mi>x</mi><mo>,</mo><mi>y</mi></mrow></msub><mo>=</mo><mi>\u2205</mi><mo>&#x2227;</mo><msub><mi>M</mi><mrow><mi>x</mi><mo>,</mo><mi>y</mi></mrow></msub><mo>&lt;</mo><mi>t</mi><mo>+</mo><msub><mi>V</mi><mi>c</mi></msub></math></aside>\n</section>\n                <section class=\"references\">\n                    <h2>References</h2>\n                    <ul>\n                <li><span class=\"h-cite\"><span class=\"h-author h-card\">Weise, David N.</span> and <span class=\"h-author\">H-Gunter Zieber</span>. <time class=\"dt-published\">1990</time>. \u201c<cite class=\"h-name\">Method and system for displaying color on a computer output device using dithering techniques</cite>\u201d. United States Patent 5485558. <a href=\"https://patentimages.storage.googleapis.com/0d/51/6a/42404fa95f7558/US5485558.pdf\" class=\"u-url\">Online</a>.</span></li>\n                    </ul>\n                </section>\n", "attachments": [{"url": "https://michael.homer.nz/notes/dither-windows-3/dither-windows-3.html", "mime_type": "text/html", "title": "Single-file archive of article: Tinted Windows: Reconstructing Historic Dithering", "size_in_bytes": 245702}]}, {"title": "On Accidental Time Travel and Delimited Continuations", "language": "en-US", "url": "https://michael.homer.nz/notes/time-travel-delimited/", "date_published": "2026-04-12T00:00:00", "id": "https://doi.org/10.59350/fwccr-w2162", "authors": [{"name": "Michael Homer", "url": "https://orcid.org/0000-0003-0280-6748"}], "content_html": "<p>A number of my <a href=\"https://doi.org/10.1145/2384592.2384601\">Grace</a> <sup>^1</sup> implementations using the <a href=\"https://doi.org/10.1145/3759426.3760977\">AST embedding</a> I introduced recently are in continuation-passing style, either because they have to be (Haskell) or to avoid blocking.\nIn combination with Grace&#x27;s block semantics and non-local returns, the na\u00efve implementation of this inadvertently enables &quot;time travel&quot;: the program can &quot;go back in time&quot; to return from a function that already returned, continuing execution from then again.\nThis leads to some interesting outcomes.</p>\n<p>What this looks like is a block containing a return statement <code>{ x -&gt; return x }</code> that is stored into a variable somewhere.\nThe method it&#x27;s inside returns normally, but later on, the block is applied\u2014 and the method returns <em>again</em>, and the program keeps going.\nThis is a bug in those implementations, not a Grace language feature, but it arises in an interesting-enough way to be worth discussing.\nThe next sections will briefly introduce the Grace semantics and the core of continuation-passing style, then explore what falls out of these on simple CPS implementations.</p>\n<h2 id=\"Grace-blocks\">Grace blocks</h2>\n<p>Grace is a &quot;curly-brace&quot; language, but control structures like <code>if</code> and <code>for</code> are actually Smalltalk-style multi-part method names. <code>if (true) then { x } else { y }</code> is a three-part method name if-then-else with three parameters, a boolean and two &quot;blocks&quot;: first-class lambdas that can be applied (or not) later, or repeatedly.\nThese result in more-or-less normal-looking imperative code, while user-defined structures can be on par with those shipped with the language.\nPart of supporting that is <em>non-local returns</em>: a block can contain a <code>return</code> statement, which determines the return value not of the block itself but of <em>the lexically-enclosing method</em>.\nThat allows, for example, an early return from a search loop:</p>\n<pre><code>method does(lst) contain(value) {\n    for (lst) do { x -&gt;\n        if (x == value) then {\n            return true\n        }\n    }\n    return false\n}</code></pre>\n<p>The &quot;then&quot; block above will return from the whole <code>does-contain</code> method, just as would happen in typical curly-bracket languages like Java.\nIn doing so, it jumps over the stack frame of the block itself, of if-then, of the outer block, and of for-do, exiting the method itself.\nWith normal code, everything here just works as expected and nobody notices anything out of the ordinary.\nBlocks can be used for control structures, event handlers, and anything else where reifying a piece of code is useful.</p>\n<p>On the implementation side these non-local returns are a bit tricky, depending on the paradigm of the host platform:</p>\n<ul>\n<li>Minigrace C used <a href=\"https://pubs.opengroup.org/onlinepubs/9799919799/functions/setjmp.html\"><code>setjmp</code></a>/<a href=\"https://pubs.opengroup.org/onlinepubs/9799919799/functions/longjmp.html\"><code>longjmp</code></a> and manually tracked the returnable scopes.</li>\n<li>Minigrace JavaScript, Kernan, and the Java wg implementation throw special exceptions, and every method body is essentially wrapped in a try-catch that matches a unique identifier of the activation and performs a return, using the host platform&#x27;s stack unwinding.</li>\n<li>Hopper, and wg&#x27;s Haskell, JavaScript, and CPS-Java implementations, use continuation-passing style everywhere, and so <code>return</code> corresponds to invoking the return continuation of the method where the block was created.</li>\n</ul>\n<p>It&#x27;s the last of these that we&#x27;re looking at here.</p>\n<h2 id=\"Continuationpassing-style\">Continuation-passing style</h2>\n<p>In brief, this implementation style models <em>every</em> step of computation as a function that has access to another function representing the next step of the program.\nThese represent the continuation of the program and so are called &quot;continuations&quot;.\nAt least conceptually, the result of any (sub)step is passed as an argument to the next function, and there&#x27;s a straightforward translation from SSA form: for every assignment, the right-hand side is the &quot;first&quot; function, and its continuation is the assignment itself; the assignment&#x27;s continuation is the <em>next</em> line&#x27;s right-hand side, and so on.\nWhen calling a function, it&#x27;s given the continuation of where it was called from to return to.\nSome languages (mostly Lisps) allow reifying those continuations, but Grace isn&#x27;t (intended) to be one of them.</p>\n<h2 id=\"Accidental-time-travel\">Accidental time travel</h2>\n<p>While Grace doesn&#x27;t allow obtaining or storing a continuation, Grace blocks are first-class, storable values.\nThe also have two <sup>^2</sup> exit paths: their own return value (which gives a value to the code that requested the <code>apply</code> method on the block), and returning from the method the block was written within (giving a value back to the code that requested <em>that</em> method).\nIn combination, under a continuation-passing implementation, it&#x27;s possible to store a value that can attempt to return from a method that <em>has already finished</em>:</p>\n<pre><code>var blk\nmethod test {\n    blk := { x -&gt; return x }\n    return 1\n}\n\n\nvar n := test\nprint(n)\nblk.apply(n + 1)</code></pre>\n<p>The code above is, surprisingly, an infinite loop <sup>^3</sup>.\nIt prints out an unbounded list of integers.\nThat&#x27;s because when <code>blk</code> is applied to an argument, it invokes the return continuation of the <code>test</code> method it was created in \u2014 resuming execution in the <code>var n := test</code> line <em>and continuing from there</em>, with a new value in <code>n</code> and eventually reaching the <code>blk.apply</code> line again to jump back one more time.\nChanges to any visible mutable state persist between invocations, but the call chain and program counter (which line of code we&#x27;re currently at) resets.</p>\n<p>On its own, this is unlikely code to write and just a worse way of obtaining <code>while (true)</code> <sup>^4</sup>.\nIt&#x27;s surprising, but perverse, and probably you&#x27;d never run into it in practice.\nHowever, it would be possible to make deliberate use of it for speculative angelic non-determinism, for example.\nThe implementations where this works probably ought to detect and reject it, but it&#x27;s uncommon enough to write and complex enough to detect that none of them do so far.</p>\n<h2 id=\"Building-delimited-continuations\">Building delimited continuations</h2>\n<p>We can even construct standard <code>shift</code>-<code>reset</code>\u2013style <a href=\"https://doi.org/10.1145/91556.91622\">delimited continuation operators</a>, which will allow any of the many structures definable on those to work.\nThe definitions are a little awkward, but they&#x27;re remarkably short for what they&#x27;re doing.</p>\n<pre><code>var resetPoint := { x -&gt; x }\nmethod reset(blk) {\n    def origReset = resetPoint\n    resetPoint := { val -&gt;\n        resetPoint := origReset\n        return val\n    }\n    def v = blk.apply\n    resetPoint.apply(v)\n}\n\n\nmethod shift(blk) {\n    def cont = { val -&gt; return val }\n    def origReset = resetPoint\n    def v = blk.apply(object {\n        method apply(v) {\n            resetPoint := { val -&gt; return val }\n            cont.apply(v)\n        }\n    })\n    resetPoint := origReset\n    origReset.apply(v)\n}</code></pre>\n<p>These methods work on any of the CPS implementations, and are used like:</p>\n<pre><code>reset {\n    print(1 + shift { k -&gt;\n            k.apply 10\n            k.apply 20\n        })\n}</code></pre>\n<p>Here <code>k</code> is the reified continuation: whenever it&#x27;s applied, <code>shift</code> will return that value, and control flow continues until the end of the enclosing <code>reset</code>, whereupon it will jump back to <em>right after after <code>k.apply</code></em>.\nThe code above will print <code>11</code> <code>21</code>.\nFor a single small use like this it&#x27;s obviously not useful, but we can create McCarthy&#x27;s <code>amb</code> operator trivially too:</p>\n<pre><code>method amb(many) {\n    shift { k -&gt;\n        many.do(k)\n    }\n}</code></pre>\n<p>Whenever this function is used within a <code>reset</code>, it will return each of the values in <code>many</code> in turn, and execution will continue from that return each time.\nThe code will treat <code>amb</code> as returning a single value \u2014 which it does, in any given timeline \u2014 and doesn&#x27;t need to be layered in a loop.\nWhen the control flow reaches the enclosing <code>reset</code>, <code>amb</code> will return the next value in a new timeline, or we can abort by returning to outside the <code>reset</code> scope.\nThere can be <em>multiple</em> amb calls and they will all fork the timeline when reached.</p>\n<p>Consider the following Pythagorean triple finder:</p>\n<pre><code>method pythag {\n    reset {\n        def a = amb [2, 3, 4, 5, 6]\n        def b = amb (2..14)\n        def c = amb (2..14)\n        if (((a * a) + (b * b)) == (c * c)) then {\n            return object {\n                def l1 = a\n                def l2 = b\n                def l3 = c\n            }\n        }\n    }\n}\ndef sol = pythag\nprint &quot;Solution is {sol.l1}^2 + {sol.l2}^2 = {sol.l3}^2&quot;</code></pre>\n<p>This will return the first correct Pythagorean triple in that range (3, 4, 5).\nIf we add a <code>print &quot;{a} {b} {c}</code> right above the <code>if</code>, it will show that all the prior values were tested, and those timelines abandoned because they didn&#x27;t correspond to triples.\nWhen one was found, the <code>return</code> chose the current timeline as the one to continue, and only one set of values came out of <code>pythag</code> so only one solution is printed <sup>^5</sup>.\nLater combinations were not evaluated at all because we abandoned the reset.</p>\n<p>This is a fairly standard demonstration for delimited continuations, and not so remarkable on its own.\nWhat&#x27;s interesting about this result is that we obtained it &quot;for free&quot;: it comes via <em>not</em> performing some checks on blocks&#x27; non-local returns.\nA block, na\u00efvely implemented, provides the primitive for building out these structures in a language that didn&#x27;t intend to have them (and probably doesn&#x27;t want them).</p>\n<h3 id=\"Coroutines\">Coroutines</h3>\n<p>We could also build a simple coroutine system.</p>\n<pre><code>var next := done\nreset {\n    shift { k -&gt; next := k }\n    print(1)\n    shift { k -&gt; next := k }\n    print(2)\n    shift { k -&gt; next := k }\n    print(3)\n}\n\n\nprint &quot;A&quot;\nnext.apply(done)\nprint &quot;B&quot;\nnext.apply(done)\nprint &quot;C&quot;\nnext.apply(done)</code></pre>\n<p>The code above will output, in order, &quot;A 1 B 2 C 3&quot;, with execution passing back and forth between the code within and outside the <code>reset</code> with each <code>shift</code> or <code>next.apply</code>.\nThese could be wrapped up into objects affording a tidier interface, potentially built out of the non-locally-returning blocks directly (i.e. using them as unrestricted continuations) instead of via shift-reset.</p>\n<h2 id=\"Discussion\">Discussion</h2>\n<p>Experimentally, I&#x27;ve added native <code>shift</code> and <code>reset</code> to most of the wg CPS implementations, so the examples above will work even without defining your own methods, but if they are defined then they will override the built-in control structures and verify that the lifting really does work as described.\nThe native implementations are generally a little faster and in some cases keep better track of stack traces for use in exceptions and other reporting.</p>\n<p>Probably, exposing continuations at all is not suitable for a language like Grace; potentially some of the things that can be built out of them are worthwhile to promote to language features.\nFor now, though, creating reified unrestricted continuations through this strange dance with blocks is a path to experimentation.</p>\n<p>CPS implementations of other programming languages might inadvertently expose the same loophole.\nThe main unusual requirement is the non-local return semantics; if there are first-class lambdas and non-local return then it should in principle be possible to reconstruct a reified continuation out of them.\nWhere the underlying continuations are single-shot, the strangest outcomes above are out of reach, though a single time travel might still be possible.\nIf they are multi-shot, either deliberately or by default (e.g. because they are composed straightforwardly out of functions), the full range should in principle be possible.\nExcept when this is deliberately part of the language semantics, return continuations should probably at least be invalidated upon first use, in effect turning those single-shot, though this isn&#x27;t quite enough to escape all the issues.</p>\n<section class=\"endnotes\">\n<h2>Endnotes</h2>\n<aside>^1 For more on Grace, see <a href=\"https://michael.homer.nz/Projects/Grace\">my other publications</a> from that project.</aside>\n<aside>^2 There is a third path, raising an exception (which can happen anywhere), but it is not relevant here.</aside>\n<aside>^3 For the remainder of this article, I&#x27;ll leave out the caveat that code like this probably shouldn&#x27;t work in <em>any</em> Grace implementation, and will only function as described in na\u00efve continuation-passing implementations that don&#x27;t check for returning from expired activations</aside>\n<aside>^4 Literally \u2014 the CPS implementation of the <code>while-do</code> loop invokes its own continuation to go to the next iteration.</aside>\n<aside>^5 Other Pythagorean triples in these ranges are (4, 3, 5), (5, 12, 13), and (6, 8, 10).</aside>\n</section>\n                <section class=\"references\">\n                    <h2>References</h2>\n                    <ul>\n                <li><span class=\"h-cite\"><span class=\"h-author h-card\">Black, Andrew P.</span>, <span class=\"h-author\">Kim B. Bruce</span>, <span class=\"h-author\">Michael Homer</span> and <span class=\"h-author\">James Noble</span>. <time class=\"dt-published\">2012</time>. \u201c<cite class=\"h-name\">Grace: the absence of (inessential) difficulty</cite>\u201d. In Proceedings of the ACM international symposium on New ideas, new paradigms, and reflections on programming and software (SPLASH '12): 85\u201398. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/2384592.2384601\" class=\"u-url\">https://doi.org/10.1145/2384592.2384601</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Danvy, Olivier</span> and <span class=\"h-author\">Andrzej Filinski</span>. <time class=\"dt-published\">1990</time>. \u201c<cite class=\"h-name\">Abstracting control</cite>\u201d. In Proceedings of the 1990 ACM conference on LISP and functional programming (LFP90): 151\u2013160. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/91556.91622\" class=\"u-url\">https://doi.org/10.1145/91556.91622</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Homer, Michael</span> and <span class=\"h-author\">James Noble</span>. <time class=\"dt-published\">2025</time>. \u201c<cite class=\"h-name\">Fast &amp; Easy ASTs for Flexible Embedded Interpreters</cite>\u201d. In Proceedings of the 22nd ACM SIGPLAN International Conference on Managed Programming Languages and Runtimes (MPLR '25): 23\u201330. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3759426.3760977\" class=\"u-url\">https://doi.org/10.1145/3759426.3760977</a>.</span></li>\n<li><span class=\"h-cite\">Josey, Andrew, Donald W. Cragun, Nicholas M. Stoughton, Eric Blake, Cathy Fox, and Geoff Clare eds. <time class=\"dt-published\">2024</time>. \u201c<cite class=\"h-name\">POSIX.1-2024/IEEE Std 1003.1\u2122-2024/The Open Group Standard Base Specifications, Issue 8</cite>\u201d. Standard. The IEEE and The Open Group. <a href=\"https://pubs.opengroup.org/onlinepubs/9799919799/\" class=\"u-url\">Online</a>.</span></li>\n                    </ul>\n                </section>\n", "attachments": [{"url": "https://michael.homer.nz/notes/time-travel-delimited/time-travel-delimited.html", "mime_type": "text/html", "title": "Single-file archive of article: On Accidental Time Travel and Delimited Continuations", "size_in_bytes": 50618}]}, {"title": "Avoiding Accidents in Structural Types", "language": "en-US", "url": "https://michael.homer.nz/notes/structural-accident-prevention/", "date_published": "2025-05-31T00:00:00", "id": "https://doi.org/10.59350/rs6yn-0y530", "authors": [{"name": "Michael Homer", "url": "https://orcid.org/0000-0003-0280-6748"}], "content_html": "<p>This short survey arose from a Stack Exchange question <a href=\"https://langdev.stackexchange.com/q/4318/133\">Language constructs to reduce inadvertent interface implementation in purely structural type systems?</a>, about ways to step out of structural type-compatibility when program logic required it. I&#x27;ve done some work in that area myself, but there are several other versions that make different trade-offs, so this post surveys all those. There are also some &quot;loopholes&quot; to construct pseudo-nominal types, which were <a href=\"https://doi.org/10.4230/LIPIcs.ECOOP.2015.198\">part of what we considered</a> as alternatives in my prior work. We argued that adding a nominal recovery on top of a structural base was preferable to adding structural types within an existing nominal type system, and that it&#x27;s better to have the discretion to expose or not the abilities to create and detect these objects.</p>\n<hr>\n<p>The general solution to this problem is known as &quot;brands&quot;, also &quot;trademarks&quot; or &quot;tags&quot;, and <em>does</em> essentially amount to adding a nominal system on top, but in various different ways that may be more (or less) palatable. This goes back at least as far as Modula-3&#x27;s branded record types in practical languages. A few examples of this to refer to:\n<ul>\n<li><a href=\"https://michael.homer.nz/notes/structural-accident-prevention/about:blank\">Modula-3</a> branded records simply attach a single label string to a structural type.\n<blockquote>\nBrands distinguish types that would otherwise be the same; they have no other semantic effect.\n</blockquote>\n\nThese have no subtyping, and could either have a name specified or just get a generated unique one.</li>\n<li>The original version of Strongtalk had brands (but dropped them).</li>\n<li>Glew&#x27;s &quot;<a href=\"https://doi.org/10.1145/317636.317797\">tagging language</a>&quot; used tags as nominal labels for type-based dispatch.</li>\n<li>Malayeri and Aldrich&#x27;s Unity <a href=\"https://doi.org/10.1007/978-3-540-70592-5_12\">integrated structural and nominal typing</a> with brands that were quite close to nominal classes.</li>\n<li>There was <a href=\"https://web.archive.org/web/20141214075933/http://wiki.ecmascript.org/doku.php?id=strawman:trademarks\">a &quot;trademarks&quot; proposal for JavaScript (ECMAScript)</a>, but it didn&#x27;t go anywhere.</li>\n<li>Lee et al.&#x27;s <a href=\"https://doi.org/10.4230/LIPIcs.ECOOP.2015.174\">theory of tagged objects</a> for Wyvern encoded nominal tags on top of a structural basis.</li>\n<li>And I worked with Jones et al. on <a href=\"https://doi.org/10.4230/LIPIcs.ECOOP.2015.198\">brand objects for Grace</a>, where a &quot;brand&quot; can annotate an object at creation and its &quot;pattern&quot; can detect objects with that brand&#x27;s annotation, with a nominal static checker consistent with that. This is discussed in more depth in Chapter 6 of <a href=\"https://doi.org/10.26686/wgtn.17064350\">Timothy Jones&#x27;s thesis</a>.</li>\n</ul></p>\n<p>All of these make some different tradeoffs and are more or less invasive to the rest of the language, and would be useful to look at when considering adding this functionality. I can speak more to the last one than the others; the goal there was to make use of existing language features (pattern matching and annotations), with the static checking layered on top, so the language semantics itself doesn&#x27;t change, and to separate the ability to brand a value from the ability to discuss a brand type. Others introduce what amount to Java-style nominal classes that can also match structural types, type system extensions that allow more behavioural integration, and other changes that may be interesting to examine.</p>\n<hr>\n<p>One of the common example use cases given for this sort of feature, and one that separates things from the &quot;encoding business logic&quot; part that some people <a href=\"https://langdev.stackexchange.com/a/4320/133\">have disputed</a>, is encoding abstract syntax tree nodes. It&#x27;s not uncommon to have multiple syntax elements that are structurally equivalent \u2014 variable and constant declarations, for example \u2014 but have distinct meanings and can&#x27;t be interchanged. It is useful to be able to distinguish those types even within a generally structural system. Another common example is things like exception hierarchies: IOError and MemoryError are intended to be distinct, but may have the same shape. These go beyond business logic, but even making business-logic illegal states unrepresentable through the type system is a desirable property to allow in many cases.</p>\n<p>&quot;Make invalid states unrepresentable&quot; isn&#x27;t always the right thing to do, and sometimes that just makes the whole program more complicated, but being <em>able</em> to do that validation where it matters, and have the system enforce that it&#x27;s consistent, is very useful.\nThere are a wealth of other reasons to want some sort of brand on a subset of your object types, while leaving the rest extensible, and the systems above make different tradeoffs in how they use that.</p>\n<hr>\n<p>There <em>are</em> some other techniques that don&#x27;t involve nominal types and were considered in the Grace design, although they do mostly look like workarounds for the lack of a branding system.\n<ul>\n<li>&quot;Funny-named members&quot;: in a purely structural system, with no extension, adding additional slots with unique names can distinguish types. The presence of <code>_isVarDecl</code> distinguishes one type from another with <code>_isConstDecl</code>, even if these members are never accessed (or <em>cannot</em> be accessed). This can be done already at the level of user code, but the language can provide mechanisms for creating them automatically.\n<p>\nThese phantom members raise encapsulation questions as a language feature: if the names are given manually, any code can forge affiliation with the brand, which may or may not be desirable. On the other hand, if they are inutterable names produced mechanically then either the mechanism is global (unencapsulated again) or indexed by something else, <em>preventing</em> claims by other parts of the system \u2014 also potentially undesirable when a structural system is setting the baseline expectation.\n</p></li>\n<li>Singleton types: these are neither structural nor nominal, but represent a single object&#x27;s identity as a type. A structural type with a member whose signature involves that singleton type is distinct from any other, and a specific field or method name returning or accepting a singleton type may be reserved for branding purposes. <em>Accepting</em> may be better, as that doesn&#x27;t leak out the singleton object to clients.\n<p>\nSingleton types have (limited) other uses, so they could be desirable anyway. Exactly how they work can vary, and whether the singleton <em>type</em> is distinct from the singleton <em>object</em> is quite important. If you have a reason to have these non-structural types anyway, this is at least a somewhat elegant use of existing language features to meet this goal.\n</p></li>\n<li>Separating from the static type system entirely: a user-space pattern-matching system can allow the sort of logic in your example without doing anything with types. If every VarDecl or LivestockBird is registered in a (weak) set, can be distinguished by the results of some property accesses, or can be detected <em>at run time</em> in any way at all, that can be encapsulated into a pattern to produce at least run-time errors if the constraint is violated. This broadly amounts to the kind of logic that could be implemented manually, but pattern-matching makes it declarative at point of use in a similar way that type annotations are.\n<p>\nDispatch models <a href=\"https://docs.raku.org/language/typesystem#subset\">like Raku&#x27;s</a> could even allow these to produce real type errors for calling a non-existent method, albeit at run time: <code>subset LivestockBird of Bird where * \u2208 $livestockBirds</code> allows you to declare <code>Load(LivestockBird $b)</code>, and then passing in a value that isn&#x27;t a bird or that doesn&#x27;t belong to the set <code>$livestockBirds</code> will tell you a suitable method doesn&#x27;t exist.\n</p></li>\n</ul></p>\n<p>I think both the brand model and pattern matching offer reasonable language ergonomics, depending on what you&#x27;re trying to do elsewhere in the language. The singletons &amp; phantom method approaches feel like a hack you use when the language hasn&#x27;t given you what you need for the task, but they are quite cheap inclusions to the language.</p>\n                <section class=\"references\">\n                    <h2>References</h2>\n                    <ul>\n                <li><span class=\"h-cite\"><span class=\"h-author h-card\">Glew, Neal</span>. <time class=\"dt-published\">1999</time>. \u201c<cite class=\"h-name\">Type dispatch for named hierarchical types</cite>\u201d. In Proceedings of the fourth ACM SIGPLAN international conference on Functional programming (ICFP99): 172\u2013182. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/317636.317797\" class=\"u-url\">https://doi.org/10.1145/317636.317797</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Jones, Timothy</span>, <span class=\"h-author\">Michael Homer</span> and <span class=\"h-author\">James Noble</span>. Boyland, John Tang ed. n.d. \u201c<cite class=\"h-name\">Brand Objects for Nominal Typing</cite>\u201d. In LIPIcs, Volume 37, ECOOP 2015 37: 198\u2013221. Schloss Dagstuhl \u2013 Leibniz-Zentrum f\u00fcr Informatik. <a href=\"https://doi.org/10.4230/LIPICS.ECOOP.2015.198\" class=\"u-url\">https://doi.org/10.4230/LIPICS.ECOOP.2015.198</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Jones, Timothy</span>. <time class=\"dt-published\">2017</time>. \u201c<cite class=\"h-name\">Classless Object Semantics</cite>\u201d. Victoria University of Wellington Library. <a href=\"https://doi.org/10.26686/wgtn.17064350\" class=\"u-url\">https://doi.org/10.26686/wgtn.17064350</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Lee, Joseph</span>, <span class=\"h-author\">Jonathan Aldrich</span>, <span class=\"h-author\">Troy Shaw</span> and <span class=\"h-author\">Alex Potanin</span>. Boyland, John Tang ed. n.d. \u201c<cite class=\"h-name\">A Theory of Tagged Objects</cite>\u201d. In LIPIcs, Volume 37, ECOOP 2015 37: 174\u2013197. Schloss Dagstuhl \u2013 Leibniz-Zentrum f\u00fcr Informatik. <a href=\"https://doi.org/10.4230/LIPICS.ECOOP.2015.174\" class=\"u-url\">https://doi.org/10.4230/LIPICS.ECOOP.2015.174</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Malayeri, Donna</span> and <span class=\"h-author\">Jonathan Aldrich</span>. <time class=\"dt-published\">2008</time>. \u201c<cite class=\"h-name\">Integrating Nominal and Structural Subtyping</cite>\u201d. In Lecture Notes in Computer Science: 260\u2013284. Springer Berlin Heidelberg, Berlin, Heidelberg. ISBN: 9783540705918. <a href=\"https://doi.org/10.1007/978-3-540-70592-5_12\" class=\"u-url\">https://doi.org/10.1007/978-3-540-70592-5_12</a>.</span></li>\n<li><span class=\"h-cite\">Nelson, Greg ed. <time class=\"dt-published\">1991</time>. \u201c<cite class=\"h-name\">Systems Programming with Modula-3</cite>\u201d. Prentice-Hall. </span></li>\n                    </ul>\n                </section>\n", "attachments": [{"url": "https://michael.homer.nz/notes/structural-accident-prevention/structural-accident-prevention.html", "mime_type": "text/html", "title": "Single-file archive of article: Avoiding Accidents in Structural Types", "size_in_bytes": 44734}]}, {"title": "What does it really mean to say a programming language has \"strong\" or \"weak\" typing?", "language": "en-US", "url": "https://michael.homer.nz/notes/strong-weak-typing/", "date_published": "2025-01-05T00:00:00", "id": "https://doi.org/10.59350/syzm8-2tz51", "authors": [{"name": "Michael Homer", "url": "https://orcid.org/0000-0003-0280-6748"}], "content_html": "<p>This survey started from a Stack Exchange question <a href=\"https://langdev.stackexchange.com/q/3741/133\">How are &quot;strong&quot; and &quot;weak&quot; typing defined?</a>, asking what is meant by describing a programming language as &quot;strongly-typed&quot; or &quot;weakly-typed&quot;. The question pointed at the Wikipedia article on the subject and to a <a href=\"https://blogs.perl.org/users/ovid/2010/08/what-to-know-before-debating-type-systems.html\">blog post by Curtis Poe</a> featuring this quote:</p>\n<blockquote cite=\"https://blogs.perl.org/users/ovid/2010/08/what-to-know-before-debating-type-systems.html\" >\n<p>Probably the most common way type systems are classified is &quot;strong&quot; or &quot;weak.&quot; This is unfortunate, since these words have nearly no meaning at all. It is, to a limited extent, possible to compare two languages with very similar type systems, and designate one as having the stronger of those two systems. Beyond that, the words mean nothing at all.</p>\n<p>Therefore: I give the following general definitions for strong and weak typing, at least when used as absolutes:</p>\n<ul>\n<li><strong>Strong typing</strong>: A type system that I like and feel comfortable with</li>\n<li><strong>Weak typing</strong>: A type system that worries me, or makes me feel uncomfortable</li>\n</ul>\n</blockquote>\n\n<p>This note collects my survey results looking at how these terms were actually used, and whether we could assign any valuable meaning to either of them.</p>\n<hr>\n<p>The Wikipedia article, and the blog post, are correct that these terms are not well-defined and are not useful for communicating in general, and at best can be used in a relative way within a particular context. Sometimes they refer to accessing the bit representation of a value as another type, sometimes they are about implicit type coercions, sometimes they refer to polymorphism, sometimes they refer to the presence of static types at all, sometimes they are about expressivity or dependent properties like bounds checking, and sometimes they are just a subjective impression of quality (&quot;A type system that I like and feel comfortable with&quot;, indeed).</p>\n<p>You will see these terms used with quietly different meanings, and you&#x27;ll see those meanings explicitly disputed, sometimes both by what you might consider experts. That doesn&#x27;t mean that they aren&#x27;t used or can&#x27;t be useful within narrow contexts where the relativity and the elements under comparison are understood and help, but it does suggest that they&#x27;re not useful as freestanding terms.</p>\n<p>I&#x27;m going to look at some uses of these terms from academic literature over time, and then highlight some conflicts that arise within it, both explicitly and implicitly.</p>\n<hr>\n<p>Firstly, let&#x27;s try to look for an actual definition, and then see whether that tracks with usage.</p>\n<p>The <em>Oxford Dictionary of Computer Science</em> <a href=\"https://www.oxfordreference.com/display/10.1093/acref/9780199688975.001.0001/acref-9780199688975-e-5115?rskey=Le0DZb&amp;result=1\">defines &quot;strong typing&quot;</a> as:</p>\n<blockquote cite=\"https://doi.org/10.1093/acref/9780199688975.001.0001\" >\n<p>A feature of some programming languages that requires the type of each data item to be declared, precludes the application of operators to inappropriate data types, and prevents the interaction of incompatible types.</p>\n</blockquote>\n\n<p>It does not have a definition for &quot;weak typing&quot;. This is probably the closest thing to an &quot;authoritative definition&quot; (at least, it&#x27;s trying to be), but you might find points to question in there already, and many times the term is used will not be entirely consistent with it; it also depends on the meanings of several <em>other</em> terms, like &quot;type&quot;.</p>\n<p>Strongly-, weakly-, and even statically- and dynamically-typed just are not as firmly-defined as people tend to think while they&#x27;re using them. To highlight this I sometimes put forward the case that &quot;C is strongly-typed <sup>^1</sup>, Perl is statically-typed <sup>^2</sup>&quot;: both of these claims are <strong>obviously definitionally false</strong>, and yet... they seem to be true, from a certain point of view, under conventional descriptions of what those terms mean, and many definitions inadvertently include them.</p>\n<hr>\n<p>A historical wart here is also that &quot;strong typing&quot; and &quot;static typing&quot; have at times been used as essentially exact synonyms; for example, see this <a href=\"https://doi.org/10.1145/141936.290558\">Bertrand Meyer abstract from OOPSLA 1992, <em>Ensuring strong typing in an object-oriented language</em></a>:</p>\n<blockquote cite=\"https://doi.org/10.1145/141936.290558\" >\n<p>Static typing, also known as strong typing, has a long and rich history in programming languages.</p>\n</blockquote>\n\n<p>On the other hand, we can look to <a href=\"https://doi.org/10.1145/97945.97964\"><em>Strong typing of object-oriented languages revisited</em> from OOPSLA/ECOOP 1990</a>:</p>\n<blockquote cite=\"https://doi.org/10.1145/97945.97964\" >\n<p>We regard this as a continuum where <strong>weakly typed means that the type of an expression carries little or no information</strong>. Smalltalk is an example of such a language where the type of instance variables convey very little information on what messages are legal to send to the denoted object. A <strong>perfectly strongly typed language would exclusively have expressions where its type carries all information about the denoted object</strong>.\n...\nLanguages with a hierarchical type system and qualified references serve as a compromise since some, but not necessarily all, operations on an object can be inferred from the qualification of the reference.</p>\n</blockquote>\n\n<p>So, here, typical object-oriented polymorphism is contrary to strong typing. Among users of functional languages, this is a common concept of strong typing.</p>\n<p>Further back, <a href=\"https://doi.org/10.1145/988113.988117\"><em>Type equivalence in strongly typed languages: one more look</em> from 1979</a> says:</p>\n<blockquote cite=\"https://doi.org/10.1145/988113.988117\" >\n<p>In a strongly typed language, each value has a unique type. The key impact of this is that, starting with the knowledge of the type of each identifier and constant appearing in the program, it is possible to determine the type of every expression in the program.</p>\n</blockquote>\n\n<p>This is more or less just a description of sound static typing. It <em>may</em> rule out polymorphism again, although the languages being discussed simply didn&#x27;t have it and that probably wasn&#x27;t in mind.</p>\n<p>A direct, if handwaved, definition is given in <a href=\"https://doi.org/10.1145/3192366.3192388\">this paper</a>:</p>\n<blockquote cite=\"https://doi.org/10.1145/3192366.3192388\" >\n<p>Type systems may be static (compile time), dynamic (run time), strong (strict), or weak (loose). The type system of C/C++ is static and weak,\nmeaning that it is up to the programmer to prevent type errors from occurring at runtime</p>\n</blockquote>\n\n<p>followed by</p>\n<blockquote>\n<p>The C/C++ type system is intentionally weak, i.e., allowing for arbitrary pointer casting</p>\n</blockquote>\n\n<p>thus definining &quot;weak typing&quot; as memory reinterpretation. This is also a fairly conventional meaning for strong &amp; weak typing.</p>\n<p>We can see discussion of the very conflict you bring up as well, such as in <a href=\"https://dl.acm.org/doi/10.5555/369279.369331\"><em>Assessing the ripple effect of CS1 language choice</em> in 2000</a>:</p>\n<blockquote >\n<p><strong>Some say that C++ is strongly typed but</strong> this statement should be taken only with respect to the default static typing done on calls made through base class pointers. <strong>Otherwise, C++ is weakly typed like C</strong>, full of implicit casts, the interpretation of any type as a boolean, and the treatment of all primitives as integers -- language constructs that fail to reinforce the notion of a type to a novice.</p>\n</blockquote>\n\n<p>We can see that there are some pretty divergent uses of &quot;strong&quot; and &quot;weak&quot; already. If these uses are sufficiently disjoint, maybe that would be ok.</p>\n<hr>\n<p>There are direct conflicts in the uses of the terms. Let&#x27;s zoom in on just one language and see how it&#x27;s been described. We can see <a href=\"https://doi.org/10.1145/3639061\">identification of Python as strongly-typed</a>:</p>\n<blockquote cite=\"https://doi.org/10.1145/3639061\" >\n<p>In contrast, using <strong>strongly typed text languages (even dynamically like Python)</strong> requires the understanding of data types and the respect of languages grammar and syntax</p>\n</blockquote>\n\n<p>And we can see <a href=\"https://doi.org/10.1145/3486608.3486909\">identification of Python</a> as <a href=\"https://doi.org/10.1145/3458817.3476176\">not strongly-typed</a>:</p>\n<blockquote cite=\"https://doi.org/10.1145/3486608.3486909\" >\n<p>This is rather difficult to detect in Python due to the lack of strong typing, and as such, there are additional uses that are not included in the results</p>\n</blockquote>\n\n<blockquote cite=\"https://doi.org/10.1145/3458817.3476176\" >\n<p>TensorFlow used Python\u2019s weak typing system to construct graphs from Python functions</p>\n</blockquote>\n\n<p>We can see a set of languages classified as strongly- or weakly-typed in <a href=\"https://doi.org/10.1145/2635868.2635922\"><em>A Large Scale Study of Programming Languages and Code Quality in GitHub</em>, FSE 2014</a>:</p>\n<table><tr><th>Strong</th>\n<th>Weak</th></tr>\n<tr><td>C# </td>\n<td> C</td></tr>\n<tr><td>Java </td>\n<td> C++</td></tr>\n<tr><td>Python </td>\n<td> Objective-C</td></tr>\n<tr><td>Ruby </td>\n<td> CoffeeScript</td></tr>\n<tr><td>Clojure </td>\n<td> JavaScript</td></tr>\n<tr><td>Erlang </td>\n<td> Perl</td></tr>\n<tr><td>Haskell </td>\n<td> PHP</td></tr>\n<tr><td>Scala </td>\n<td></td></tr></table>\n<p>Once more, Python is identified as strongly-typed. We can also <a href=\"https://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic%20language%20and%20also%20a%20strongly%20typed%20language\">find</a> many <a href=\"https://news.ycombinator.com/item?id=17049499\">other</a> real-world <a href=\"https://www.linkedin.com/advice/0/what-difference-between-strongly-weakly-typed-eqwlc\">uses</a> that <a href=\"https://wiki.python.org/moin/StrongVsWeakTyping\">classify</a> it in both directions, give conflicting definitions, and argue quite intensely.</p>\n<p>You might agree or disagree with some others of those listed languages as well \u2014 and you&#x27;ll see <em>that</em> reflected in the literature too, including directly in\n<a href=\"https://doi.org/10.1145/3340571\"><em>On the Impact of Programming Languages on Code Quality: A Reproduction Study</em> in TOPLAS from Emery D. Berger, Celeste Hollenbeck, Petr Maj, Olga Vitek, Jan Vitek in 2019</a>:</p>\n<blockquote cite=\"https://doi.org/10.1145/3340571\" >\n<p>The Type category is the most counter-intuitive for programming language experts as it expresses whether a language allows\nvalue of one type to be interpreted as another, e.g., due to automatic conversion.\nThe <a href=\"https://doi.org/10.1145/3126905\">CACM paper</a>\nattempted to clarify this definition with the example of the ID type. In Objective-C, an ID variable\ncan hold any value. If this is what the authors intend, then <strong>Python, Ruby, Clojure, and Erlang</strong>\nwould be weak<strong> as they have similar generic types.</strong></p>\n</blockquote>\n\n<p>The CACM paper referred to changes the name of that category to &quot;Implicit type conversion&quot;, so clarifying that as the meaning they intended by &quot;strong/weak typing&quot; the first time (and likely reflecting pushback in between on this very controversy over the terms!). This is <em>one</em> meaning of &quot;weak typing&quot;, but as we have seen above it&#x27;s not the only one.</p>\n<hr>\n<p>JavaScript given there is one of the categorical examples of weak typing, used all over the place, because of <a href=\"https://262.ecma-international.org/#sec-applystringornumericbinaryoperator\">its many implicit coercions</a>. On the other hand, another conventional perspective is that weak typing is about reinterpreting a value&#x27;s memory representation as another type \u2014 this is where C falls \u2014 and you definitely <em>can&#x27;t</em> do that in JavaScript \u2014 is it strongly typed? On yet another hand, C does define some types that can&#x27;t be reinterpreted that way (function pointers) so is it strongly-typed too? We are stretching the terms beyond meaning at this point, but it highlights where these handwaves don&#x27;t hold up when pushed.</p>\n<p>Even this perspective that it&#x27;s the implicit coercions that make JavaScript weakly-typed isn&#x27;t as particular as it seems on the surface.</p>\n<ul>\n<li>Is every language with implicit widening conversion from float to double then weakly-typed also? Java does that, but is typically seen as strongly-typed.</li>\n<li>Is it the sheer number of type coercions? Java and C# actually have <em>more</em>, because they have multiple numeric types.</li>\n<li>Is it accessing methods or fields from different types through the same variable? This is just dynamic typing or polymorphism, so what&#x27;s the role of &quot;weak&quot; here?</li>\n<li>Is it automatically invoking a stringifying operation during string concatenation? Many of the supposedly strongly-typed languages given above do that too.</li>\n<li>Is it that typical JavaScript code tends to invoke these coercions often? That doesn&#x27;t have much to do with typing and is an empirical claim about usage, rather than the design of the language.</li>\n<li>Is it converting from strings back to numbers? Now we&#x27;re getting somewhere specific, but we&#x27;ve come a long way from &quot;is weakly-typed&quot; and could more usefully say that directly!</li>\n</ul>\n<p>On the other hand, in Perl there is no need for such a string\u21d2number conversion, because numbers and strings are the same type all along: is it then strongly-typed? Or perhaps all those other languages are weakly-typed too \u2014 but clearly that perspective is not universal, so we&#x27;d need to be explaining what we meant, and one wonders why we want to use these specific words for that.</p>\n<p>Even other answers <em>to the very question that led to this investigation</em> pick out JavaScript as obviously without question <a href=\"https://langdev.stackexchange.com/a/3762/133\">one</a> or the <a href=\"https://langdev.stackexchange.com/a/3761/133\">other</a>. They give entirely contradictory rationales that let us distinguish the meanings in use, but clearly the terms then don&#x27;t tell us anything themselves. We need to explain what properties we are concerned with, and then the explanation can stand on its own instead.</p>\n<p>We can also look to <a href=\"https://langdev.stackexchange.com/questions/3741/how-are-strong-and-weak-typing-defined#comment11892_3741\">Eric Lippert, a designer of C#, to comment on that language</a>:</p>\n<blockquote>\n<p>Well if I&#x27;m an authority then: I agree with Curtis. Is C# strongly typed? It has many features that people describe as &quot;strong&quot; and many features that people describe as &quot;weak&quot;. If you mean &quot;allows casting&quot;, say &quot;allows casting&quot;, not &quot;weak&quot;. If you mean &quot;objects can describe their types&quot; then say &quot;objects can describe their types&quot;, not &quot;strong&quot;.</p>\n</blockquote>\n\n<p>This is highlighting where these terms are not useful on their own: it would be better to say <em>what you actually mean</em> about the type system, rather than bundling it up into &quot;strong&quot; or &quot;weak&quot;, if you want to communicate something. Someone else will have a different meaning and a different understanding. The Python examples above highlight that it simply isn&#x27;t useful to say &quot;strong typing&quot;: you need to say what you&#x27;re actually talking about, or you&#x27;ll be talking past one another.</p>\n<hr>\n<p>I wanted to sum up with a general high-level description of things that were suggestive of each category, but I&#x27;m not sure I can even do that. We might try to say that:</p>\n<ul>\n<li>A strongly-typed language is one where a value can only be used as its one true type \u2014 which could still be multiple types, or perhaps it can&#x27;t, and what exactly is a type in this language, anyway?, and of course I want my language be strongly-typed and perhaps I don&#x27;t want yours to be (witness the arguments about Python), and\u2014</li>\n<li>A weakly-typed language is one where the bits of a value can be reinterpreted as another type \u2014 or one where they can&#x27;t, but there are &quot;too many&quot; coercions available, or the coercions that exist are too error-prone, or the <em>operations</em> that give me a value of one type given a value of another type aren&#x27;t the right shape, or the <em>types</em> that exist aren&#x27;t the sorts of types I want to think of as types, or\u2014</li>\n</ul>\n<p>We can see a pattern there that &quot;weak typing&quot; is mostly negative framing, and I think that brings us back around to the quote from the original question:</p>\n<blockquote cite=\"https://blogs.perl.org/users/ovid/2010/08/what-to-know-before-debating-type-systems.html\" >\n<ul>\n<li>Strong typing: A type system that I like and feel comfortable with</li>\n<li>Weak typing: A type system that worries me, or makes me feel uncomfortable</li>\n</ul>\n</blockquote>\n\n<p>Yes, that seems about right for how people use those terms. <em>In context</em>, it can make sense to contrast different approaches in that way, with a particular value system as a frame of reference, but in the abstract &quot;strong typing&quot; and &quot;weak typing&quot; don&#x27;t communicate much more.</p>\n<section class=\"endnotes\">\n<h2>Endnotes</h2>\n<aside>^1 i.e. C has two strong types, &quot;function pointer&quot; and &quot;not a function pointer&quot; that (per spec) can&#x27;t be reinterpreted into each other; &quot;not a function pointer&quot; is just a single polymorphic type.</aside>\n<aside>^2 i.e. Perl has static types called &quot;scalar&quot;, &quot;array&quot;, &quot;hash&quot;, &quot;file&quot;, and &quot;subroutine&quot; and sigils are used to declare these types statically, so the type system is atypical, but still static.</aside>\n</section>\n                <section class=\"references\">\n                    <h2>References</h2>\n                    <ul>\n                <li><span class=\"h-cite\"><span class=\"h-author h-card\">Berger, Emery D.</span>, <span class=\"h-author\">Celeste Hollenbeck</span>, <span class=\"h-author\">Petr Maj</span>, <span class=\"h-author\">Olga Vitek</span> and <span class=\"h-author\">Jan Vitek</span>. <time class=\"dt-published\">2019</time>. \u201c<cite class=\"h-name\">On the Impact of Programming Languages on Code Quality: A Reproduction Study</cite>\u201d. In ACM Transactions on Programming Languages and Systems 41 (4): 1\u201324. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/3340571\" class=\"u-url\">https://doi.org/10.1145/3340571</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Berger, Emery D.</span>, <span class=\"h-author\">Celeste Hollenbeck</span>, <span class=\"h-author\">Petr Maj</span>, <span class=\"h-author\">Olga Vitek</span> and <span class=\"h-author\">Jan Vitek</span>. <time class=\"dt-published\">2019</time>. \u201c<cite class=\"h-name\">On the Impact of Programming Languages on Code Quality: A Reproduction Study</cite>\u201d. In ACM Transactions on Programming Languages and Systems 41 (4): 1\u201324. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/3340571\" class=\"u-url\">https://doi.org/10.1145/3340571</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Berry, Daniel M.</span> and <span class=\"h-author\">Richard L. Schwartz</span>. <time class=\"dt-published\">1979</time>. \u201c<cite class=\"h-name\">Type equivalence in strongly typed languages: one more look</cite>\u201d. In ACM SIGPLAN Notices 14 (9): 35\u201341. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/988113.988117\" class=\"u-url\">https://doi.org/10.1145/988113.988117</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Berry, Daniel M.</span> and <span class=\"h-author\">Richard L. Schwartz</span>. <time class=\"dt-published\">1979</time>. \u201c<cite class=\"h-name\">Type equivalence in strongly typed languages: one more look</cite>\u201d. In ACM SIGPLAN Notices 14 (9): 35\u201341. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/988113.988117\" class=\"u-url\">https://doi.org/10.1145/988113.988117</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Branth\u00f4me, Matthieu</span>. <time class=\"dt-published\">2024</time>. \u201c<cite class=\"h-name\">Pyrates: Design and Evaluation of a Serious Game Aimed at Introducing Python Programming and Easing the Transition from Blocks</cite>\u201d. In ACM Transactions on Computing Education 24 (1): 1\u201324. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/3639061\" class=\"u-url\">https://doi.org/10.1145/3639061</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Branth\u00f4me, Matthieu</span>. <time class=\"dt-published\">2024</time>. \u201c<cite class=\"h-name\">Pyrates: Design and Evaluation of a Serious Game Aimed at Introducing Python Programming and Easing the Transition from Blocks</cite>\u201d. In ACM Transactions on Computing Education 24 (1): 1\u201324. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/3639061\" class=\"u-url\">https://doi.org/10.1145/3639061</a>.</span></li>\n<li><span class=\"h-cite\">Butterfield, Andrew and Gerard Ekembe Ngondi eds. <time class=\"dt-published\">2016</time>. \u201c<cite class=\"h-name\">A Dictionary of Computer Science</cite>\u201d. Oxford University Press. ISBN: 9780199688975. <a href=\"https://doi.org/10.1093/acref/9780199688975.001.0001\" class=\"u-url\">https://doi.org/10.1093/acref/9780199688975.001.0001</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Dingle, Adair</span> and <span class=\"h-author\">Carol Zander</span>. <time class=\"dt-published\">2000</time>. \u201c<cite class=\"h-name\">Assessing the ripple effect of CS1 language choice</cite>\u201d. In Journal of Computing Sciences in Colleges 16 (2). Consortium for Computing Sciences in Colleges. </span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Duck, Gregory J.</span> and <span class=\"h-author\">Roland H. C. Yap</span>. <time class=\"dt-published\">2018</time>. \u201c<cite class=\"h-name\">EffectiveSan: type and memory error detection using dynamically typed C/C++</cite>\u201d. In Proceedings of the 39th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI '18): 181\u2013195. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3192366.3192388\" class=\"u-url\">https://doi.org/10.1145/3192366.3192388</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Duck, Gregory J.</span> and <span class=\"h-author\">Roland H. C. Yap</span>. <time class=\"dt-published\">2018</time>. \u201c<cite class=\"h-name\">EffectiveSan: type and memory error detection using dynamically typed C/C++</cite>\u201d. In Proceedings of the 39th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI '18): 181\u2013195. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3192366.3192388\" class=\"u-url\">https://doi.org/10.1145/3192366.3192388</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Farooq, Aamir</span> and <span class=\"h-author\">Vadim Zaytsev</span>. <time class=\"dt-published\">2021</time>. \u201c<cite class=\"h-name\">There is more than one way to zen your Python</cite>\u201d. In Proceedings of the 14th ACM SIGPLAN International Conference on Software Language Engineering (SLE '21): 68\u201382. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3486608.3486909\" class=\"u-url\">https://doi.org/10.1145/3486608.3486909</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Farooq, Aamir</span> and <span class=\"h-author\">Vadim Zaytsev</span>. <time class=\"dt-published\">2021</time>. \u201c<cite class=\"h-name\">There is more than one way to zen your Python</cite>\u201d. In Proceedings of the 14th ACM SIGPLAN International Conference on Software Language Engineering (SLE '21): 68\u201382. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3486608.3486909\" class=\"u-url\">https://doi.org/10.1145/3486608.3486909</a>.</span></li>\n<li><span class=\"h-cite\">Guo, Shu-yu, Michael Ficarra, and Kevin Gibbons eds. n.d. \u201c<cite class=\"h-name\">ECMAScript\u00ae 2024 Language Specification, ECMA-262-2024</cite>\u201d. Standard. Ecma International. <a href=\"https://tc39.es/ecma262/2024/\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">JakeRobb</span>. <time class=\"dt-published\">2024</time>. \u201c<cite class=\"h-name\">Answer on \u2018How are \"strong\" and \"weak\" typing defined?\u2019</cite>\u201d. In Programming Language Design and Implementation Stack Exchange. <a href=\"https://langdev.stackexchange.com/a/3762/133\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Lippert, Eric</span>. <time class=\"dt-published\">2024</time>. \u201c<cite class=\"h-name\">Comment on \u2018How are \"strong\" and \"weak\" typing defined?\u2019</cite>\u201d. In Programming Language Design and Implementation Stack Exchange. <a href=\"https://langdev.stackexchange.com/questions/3741/how-are-strong-and-weak-typing-defined#comment11892_3741\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Madsen, Ole Lehrmann</span>, <span class=\"h-author\">Boris Magnusson</span> and <span class=\"h-author\">Birger M\u00f8lier-Pedersen</span>. <time class=\"dt-published\">1990</time>. \u201c<cite class=\"h-name\">Strong typing of object-oriented languages revisited</cite>\u201d. In Proceedings of the workshop on Object-based concurrent programming (OOPSLA/ECOOP '90): 140\u2013150. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/97945.97964\" class=\"u-url\">https://doi.org/10.1145/97945.97964</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Madsen, Ole Lehrmann</span>, <span class=\"h-author\">Boris Magnusson</span> and <span class=\"h-author\">Birger M\u00f8lier-Pedersen</span>. <time class=\"dt-published\">1990</time>. \u201c<cite class=\"h-name\">Strong typing of object-oriented languages revisited</cite>\u201d. In Proceedings of the workshop on Object-based concurrent programming (OOPSLA/ECOOP '90): 140\u2013150. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/97945.97964\" class=\"u-url\">https://doi.org/10.1145/97945.97964</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Meyer, Bertrand</span>. <time class=\"dt-published\">1992</time>. \u201c<cite class=\"h-name\">Ensuring strong typing in an object-oriented language (abstract)</cite>\u201d. In Conference proceedings on Object-oriented programming systems, languages, and applications (OOPSLA92): 89\u201390. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/141936.290558\" class=\"u-url\">https://doi.org/10.1145/141936.290558</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Meyer, Bertrand</span>. <time class=\"dt-published\">1992</time>. \u201c<cite class=\"h-name\">Ensuring strong typing in an object-oriented language (abstract)</cite>\u201d. In Conference proceedings on Object-oriented programming systems, languages, and applications (OOPSLA92): 89\u201390. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/141936.290558\" class=\"u-url\">https://doi.org/10.1145/141936.290558</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Poe, Curtis</span>. <time class=\"dt-published\">2010</time>. \u201c<cite class=\"h-name\">What to know before debating type systems</cite>\u201d. <a href=\"https://blogs.perl.org/users/ovid/2010/08/what-to-know-before-debating-type-systems.html\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Ray, Baishakhi</span>, <span class=\"h-author\">Daryl Posnett</span>, <span class=\"h-author\">Vladimir Filkov</span> and <span class=\"h-author\">Premkumar Devanbu</span>. <time class=\"dt-published\">2014</time>. \u201c<cite class=\"h-name\">A large scale study of programming languages and code quality in github</cite>\u201d. In Proceedings of the 22nd ACM SIGSOFT International Symposium on Foundations of Software Engineering (SIGSOFT/FSE'14): 155\u2013165. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/2635868.2635922\" class=\"u-url\">https://doi.org/10.1145/2635868.2635922</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Ray, Baishakhi</span>, <span class=\"h-author\">Daryl Posnett</span>, <span class=\"h-author\">Premkumar Devanbu</span> and <span class=\"h-author\">Vladimir Filkov</span>. <time class=\"dt-published\">2017</time>. \u201c<cite class=\"h-name\">A large-scale study of programming languages and code quality in GitHub</cite>\u201d. In Communications of the ACM 60 (10): 91\u2013100. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/3126905\" class=\"u-url\">https://doi.org/10.1145/3126905</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">TheHans255</span>. <time class=\"dt-published\">2024</time>. \u201c<cite class=\"h-name\">Answer on \u2018How are \"strong\" and \"weak\" typing defined?\u2019</cite>\u201d. In Programming Language Design and Implementation Stack Exchange. <a href=\"https://langdev.stackexchange.com/a/3761/133\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Ziogas, Alexandros Nikolaos</span>, <span class=\"h-author\">Timo Schneider</span>, <span class=\"h-author\">Tal Ben-Nun</span>, <span class=\"h-author\">Alexandru Calotoiu</span>, <span class=\"h-author\">Tiziano De Matteis</span>, <span class=\"h-author\">Johannes de Fine Licht</span>, <span class=\"h-author\">Luca Lavarini</span> and <span class=\"h-author\">Torsten Hoefler</span>. <time class=\"dt-published\">2021</time>. \u201c<cite class=\"h-name\">Productivity, portability, performance: data-centric Python</cite>\u201d. In Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis (SC '21): 1\u201313. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3458817.3476176\" class=\"u-url\">https://doi.org/10.1145/3458817.3476176</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Ziogas, Alexandros Nikolaos</span>, <span class=\"h-author\">Timo Schneider</span>, <span class=\"h-author\">Tal Ben-Nun</span>, <span class=\"h-author\">Alexandru Calotoiu</span>, <span class=\"h-author\">Tiziano De Matteis</span>, <span class=\"h-author\">Johannes de Fine Licht</span>, <span class=\"h-author\">Luca Lavarini</span> and <span class=\"h-author\">Torsten Hoefler</span>. <time class=\"dt-published\">2021</time>. \u201c<cite class=\"h-name\">Productivity, portability, performance: data-centric Python</cite>\u201d. In Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis (SC '21): 1\u201313. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3458817.3476176\" class=\"u-url\">https://doi.org/10.1145/3458817.3476176</a>.</span></li>\n                    </ul>\n                </section>\n", "attachments": [{"url": "https://michael.homer.nz/notes/strong-weak-typing/strong-weak-typing.html", "mime_type": "text/html", "title": "Single-file archive of article: What does it really mean to say a programming language has \"strong\" or \"weak\" typing?", "size_in_bytes": 83454}]}, {"title": "What factors make a programming language more (or less) beginner-friendly?", "language": "en-US", "url": "https://michael.homer.nz/notes/beginner-friendly-pl/", "date_published": "2024-06-13T00:00:00", "id": "https://doi.org/10.59350/4qw7b-60q70", "authors": [{"name": "Michael Homer", "url": "https://orcid.org/0000-0003-0280-6748"}], "content_html": "<p>Building from an answer I wrote to a Stack Exchange question <a href=\"https://langdev.stackexchange.com/q/3687/133\">What makes a language &#x27;beginner-friendly&#x27;?</a>, asking what aspects of a programming language made it more (or implicitly less) suitable for a beginner, here I discuss what&#x27;s in the literature about this topic.\nMy PhD and a lot of my subsequent work has been with <a href=\"https://doi.org/10.1145/2384592.2384601\">Grace</a>, intended as an educational language, but this is a more complex question than it seems on the surface.</p>\n<hr>\n<p>One challenge with addressing this question is that the evidence base is much thinner than you might expect, and much of the wisdom floating around isn&#x27;t based on anything much, and quite often does not hold up when examined \u2014 but so little <em>is</em> examined that we don&#x27;t necessarily know which.</p>\n<p>There just aren&#x27;t as many actual experiments looking at programming-language design <em>in total</em> as you might expect \u2014 <a href=\"https://urn.fi/URN:ISBN:978-951-39-6388-0\">Antti-Juhani Kaijanaho&#x27;s 2012 PhD thesis</a> identifies somewhere between 37 and 137, depending on your inclusion criteria, and there haven&#x27;t been vast numbers since then. Many of those are not specifically relevant to beginners.</p>\n<hr>\n<p>The most significant recent study on language <em>syntax and keywords</em> for novices comes from Andreas Stefik and Susanna Siebert in 2013, <a href=\"https://doi.org/10.1145/2534973\">An Empirical Investigation into Programming Language Syntax</a>. Some of the results there are likely to be surprising, while others are obvious at least in retrospect. Overall, the major takeaways are that</p>\n<ul>\n<li>Use of <strong>words rather than symbols</strong> generally helps matters, but ...</li>\n<li>... the <strong><em>typical</em> words</strong> used in programming languages are <strong>not very effective</strong>.\n<br/>\nFor example, terms like &quot;foreach&quot;, &quot;while&quot;, and &quot;for&quot; perform very poorly, while data type names like &quot;float&quot; and &quot;string&quot; don&#x27;t do well either.</li>\n<li><strong>Metaphorical terms are especially weak</strong>: things like throw and catch do worse than &quot;error&quot; \u2014 but in place of both of those, which would probably be confusing too.</li>\n<li>In general, <strong>using fewer tokens</strong> to express something performs better than using more, but this is in tension with using helpful non-symbolic terms.</li>\n<li>Neither beginners nor more-experienced programmers are very consistent in just about any facet, except that the experienced programmers favour the choices they&#x27;re accustomed to (for example, the term <code>cout</code> tested very favourably for output).</li>\n</ul>\n<p>When experimenting on whole languages, Ruby, Python, and their <a href=\"https://quorumlanguage.com/\">Quorum</a> language performed best. However, these experiments were still only in an artificial environment and did not measure learning, only accuracy.</p>\n<hr>\n<p>There is a reasonable amount of evidence that <strong>static typing produces better results</strong> for novice programmers by catching their errors, but also that <strong>type <em>annotations</em> produce more syntax errors</strong> for them. The most well-known work in this vein is by Stefan Hanenberg, but it has been replicated a few times. Pedagogical experience reports often highlight the latter point as more significant at the very beginning \u2014 it&#x27;s that &quot;more tokens&quot; issue again.</p>\n<p><strong>Block-based programming</strong> environments, which prevent the creation of various kinds of syntax and type error, have been shown to <strong>perform better with novices than textual languages</strong> even when the language is otherwise identical, such as by Thomas W. Price and Tiffany Barnes in 2015, <a href=\"https://doi.org/10.1145/2787622.2787712\">Comparing Textual and Block Interfaces in a Novice Programming Environment</a>; there has also been significant work by David Weintrop and others. However, while these provide good support in the early stages of learning, they can also impose meaningful exit friction when the novice advances beyond what the block environment gives them. Most block environments also provide very easy access to graphical or interactive primitives, and may show live stepping or other debugging affordances.</p>\n<p>&quot;Good&quot; error messages do increase learner performance, but those that are &quot;too helpful&quot; are counterproductive, especially if they can make a bad guess about the user&#x27;s intent. A substantial number of novices will &quot;freeze&quot; when there are many error or even warning indicators given to them, but presenting all errors at once outperforms one-at-a-time reporting. Allowing erroneous programs to run partially has shown some effectiveness. Improvements to error reporting have been studied among others by Becker, Brett A., Graham Glanville, Ricardo Iwashima, Claire McDonnell, Kyle Goslin, and Catherine Mooney in 2016, <a href=\"https://doi.org/10.1080/08993408.2016.1225464\">Effective Compiler Error Message Enhancement for Novice Programming Students</a>. Negative responses to error messages perceived as directed at the user are common in new learners and the phrasing and content needs to be balanced carefully.</p>\n<p>Limited sub-languages that progress towards the unrestricted language have shown value as far back as SP/k, but it is Racket more recently that has brought them to the fore. The advantage these systems provide is that learners do not &quot;stumble&quot; into advanced functionality they didn&#x27;t intend to reach; this sometimes happens when error messages lead them astray (a common anecdote is a novice who turns their entire Java program <code>static</code> after they try to fix an error saying they are unable to access an instance method or field from <code>main</code>). However, these limited languages also introduce more kinds of error and more ways for the learner to go wrong.</p>\n<p>Although the language &quot;paradigm&quot; seems like it should form a major component of the answer, the evidence for and against functional, imperative, object-oriented, procedural, ... models for learners is very mixed over time. There does not seem to be a strong recommendation that could be made in isolation backed by anything but personal assertion (and many have done so). The other aspects already mentioned, and any structured teaching wrapped around it, are much bigger factors.</p>\n<p>Localisation <a href=\"https://doi.org/10.1145/3622780.3623645\">is a significant issue</a>, particularly for younger learners. Learning keywords and following already-opaque error messages in an unknown language is challenging and new programmers understandably tend to perform better when the language keywords, library elements, word order, and diagnostic messages match their native language. For beginners who are non-English speakers (or are also beginners there), a localised language of some kind will probably help them at first. However, in many cases those localised languages that do exist have limited progression paths out of them and limited library support. <a href=\"https://doi.org/10.1145/3372782.3406262\">Hedy</a> is a language that tries to navigate this, with detailed localisation including the complex elements like word and writing order, but representing Python behind the scenes, and there&#x27;s been quite a bit of study of how that works out.</p>\n<p>Finally, and this is a bit out of left field, the programming language that the most beginners pick up unassisted and to productive use is <strong>the spreadsheet</strong>, a spatial dataflow language. These do have <a href=\"https://eusprig.org/research-info/research-and-best-practice/\">very high error rates</a>, but immediate accessibility that virtually no other programming environment matches. The true answer for a randomly-selected &quot;beginner&quot; is probably Excel.</p>\n<hr>\n<p>Other topics I won&#x27;t touch on more, but are worth keeping in mind also:</p>\n<ul>\n<li>There is a concept in educational psychology called <em>transfer</em>. A beginner won&#x27;t be a beginner forever, and if they soon have to move on to another language they likely will not be able to transfer what they&#x27;ve learned already easily, unless they have directed instruction guiding them. It&#x27;s often assumed that if the new target is &quot;similar enough&quot; to what they have been using already then this will happen for free, but research does <em>not</em> bear that out. A language that is merely similar to one the learner may want to use in future may not be helpful.</li>\n<li>In considering the starting question, &quot;Which coding languages should a beginner learn?&quot;, there are a lot of other elements outside of the language designs themselves, and these are likely to dominate in practice. Those with significant communities or commercial appeal are often going to be better choices than those more ideal for a beginner to learn.</li>\n<li>If the beginner is to pick up the language on their own, the answer is likely different to if it is to be taught to them. In particular, teaching with explicit bridging content can allow the &quot;exit path&quot; from novice-specific languages that is much harder to navigate alone.</li>\n<li>Accessibility varies significantly by the tooling, more so than the language, but these are often very correlated. IDE support for screenreaders and other assistive technology varies greatly, and some language families have a much worse time than others.</li>\n</ul>\n<p>Broad overviews of the long-standing research on learning programming are in</p>\n<ul>\n<li><a href=\"https://doi.org/10.1145/1345375.1345441\">Pears <em>et al.</em> 2007</a>, &quot;A survey of literature on the teaching of introductory programming&quot;.</li>\n<li><a href=\"https://doi.org/10.1076/csed.13.2.137.14200\">Robins <em>et al.</em> 2003</a>, &quot;Learning and teaching programming: A review and discussion&quot;.</li>\n<li>And <a href=\"https://michael.homer.nz/Thesis#@s3.1.0\">section 2.1 of my thesis</a> gives an overview of the state of things in 2014.</li>\n</ul>\n                <section class=\"references\">\n                    <h2>References</h2>\n                    <ul>\n                <li><span class=\"h-cite\"><span class=\"h-author h-card\">Becker, Brett A.</span>, <span class=\"h-author\">Graham Glanville</span>, <span class=\"h-author\">Ricardo Iwashima</span>, <span class=\"h-author\">Claire McDonnell</span>, <span class=\"h-author\">Kyle Goslin</span> and <span class=\"h-author\">Catherine Mooney</span>. <time class=\"dt-published\">2016</time>. \u201c<cite class=\"h-name\">Effective compiler error message enhancement for novice programming students</cite>\u201d. In Computer Science Education 26 (2-3): 148\u2013175. Informa UK Limited. <a href=\"https://doi.org/10.1080/08993408.2016.1225464\" class=\"u-url\">https://doi.org/10.1080/08993408.2016.1225464</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Black, Andrew P.</span>, <span class=\"h-author\">Kim B. Bruce</span>, <span class=\"h-author\">Michael Homer</span> and <span class=\"h-author\">James Noble</span>. <time class=\"dt-published\">2012</time>. \u201c<cite class=\"h-name\">Grace: the absence of (inessential) difficulty</cite>\u201d. In Proceedings of the ACM international symposium on New ideas, new paradigms, and reflections on programming and software (SPLASH '12): 85\u201398. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/2384592.2384601\" class=\"u-url\">https://doi.org/10.1145/2384592.2384601</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">European Spreadsheet Risk Interest Group</span>. n.d. \u201c<cite class=\"h-name\">Research and Best Practice</cite>\u201d. Accessed 2024-04-03. <a href=\"https://eusprig.org/research-info/research-and-best-practice/\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Hermans, Felienne</span>. <time class=\"dt-published\">2020</time>. \u201c<cite class=\"h-name\">Hedy: A Gradual Language for Programming Education</cite>\u201d. In Proceedings of the 2020 ACM Conference on International Computing Education Research (ICER '20): 259\u2013270. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3372782.3406262\" class=\"u-url\">https://doi.org/10.1145/3372782.3406262</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Homer, Michael</span>. <time class=\"dt-published\">2014</time>. \u201c<cite class=\"h-name\">Graceful Language Extensions and Interfaces</cite>\u201d. Victoria University of Wellington Library. <a href=\"https://doi.org/10.26686/wgtn.17008246\" class=\"u-url\">https://doi.org/10.26686/wgtn.17008246</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Kaijanaho, Antti-Juhani</span>. <time class=\"dt-published\">2015</time>. \u201c<cite class=\"h-name\">Evidence-based Programming Language Design: A Philosophical and Methodological Exploration (PhD thesis)</cite>\u201d. Thesis. University of Jyv\u00e4skyl\u00e4. ISBN: 9789513963880. <a href=\"https://urn.fi/URN:ISBN:978-951-39-6388-0\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Pears, Arnold</span>, <span class=\"h-author\">Stephen Seidman</span>, <span class=\"h-author\">Lauri Malmi</span>, <span class=\"h-author\">Linda Mannila</span>, <span class=\"h-author\">Elizabeth Adams</span>, <span class=\"h-author\">Jens Bennedsen</span>, <span class=\"h-author\">Marie Devlin</span> and <span class=\"h-author\">James Paterson</span>. <time class=\"dt-published\">2007</time>. \u201c<cite class=\"h-name\">A survey of literature on the teaching of introductory programming</cite>\u201d. In ACM SIGCSE Bulletin 39 (4): 204\u2013223. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/1345375.1345441\" class=\"u-url\">https://doi.org/10.1145/1345375.1345441</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Price, Thomas W.</span> and <span class=\"h-author\">Tiffany Barnes</span>. <time class=\"dt-published\">2015</time>. \u201c<cite class=\"h-name\">Comparing Textual and Block Interfaces in a Novice Programming Environment</cite>\u201d. In Proceedings of the eleventh annual International Conference on International Computing Education Research (ICER '15): 91\u201399. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/2787622.2787712\" class=\"u-url\">https://doi.org/10.1145/2787622.2787712</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Robins, Anthony</span>, <span class=\"h-author\">Janet Rountree</span> and <span class=\"h-author\">Nathan Rountree</span>. <time class=\"dt-published\">2003</time>. \u201c<cite class=\"h-name\">Learning and Teaching Programming: A Review and Discussion</cite>\u201d. In Computer Science Education 13 (2): 137\u2013172. Informa UK Limited. <a href=\"https://doi.org/10.1076/csed.13.2.137.14200\" class=\"u-url\">https://doi.org/10.1076/csed.13.2.137.14200</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Stefik, Andreas</span> and <span class=\"h-author\">Susanna Siebert</span>. <time class=\"dt-published\">2013</time>. \u201c<cite class=\"h-name\">An Empirical Investigation into Programming Language Syntax</cite>\u201d. In ACM Transactions on Computing Education 13 (4): 1\u201340. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/2534973\" class=\"u-url\">https://doi.org/10.1145/2534973</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Swidan, Alaaeddin</span> and <span class=\"h-author\">Felienne Hermans</span>. <time class=\"dt-published\">2023</time>. \u201c<cite class=\"h-name\">A Framework for the Localization of Programming Languages</cite>\u201d. In Proceedings of the 2023 ACM SIGPLAN International Symposium on SPLASH-E (SPLASH-E '23): 13\u201325. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3622780.3623645\" class=\"u-url\">https://doi.org/10.1145/3622780.3623645</a>.</span></li>\n                    </ul>\n                </section>\n", "attachments": [{"url": "https://michael.homer.nz/notes/beginner-friendly-pl/beginner-friendly-pl.html", "mime_type": "text/html", "title": "Single-file archive of article: What factors make a programming language more (or less) beginner-friendly?", "size_in_bytes": 52145}]}, {"title": "What evidence exists about brace- and indentation-based syntax for novice programmers?", "language": "en-US", "url": "https://michael.homer.nz/notes/novice-braces-indentation/", "date_published": "2024-03-19T00:00:00", "id": "https://doi.org/10.59350/0xy8d-5vb59", "authors": [{"name": "Michael Homer", "url": "https://orcid.org/0000-0003-0280-6748"}], "content_html": "<p>A Stack Exchange question (<a href=\"https://langdev.stackexchange.com/q/3252/133\">Studies on learnability of braces vs. indentation for code blocks for beginners?</a>) asked for information about what evidence existed about the impact on new programmers of different ways of delimiting block structure in code.\nThis is the literature survey I composed in response, which finds there really isn&#x27;t very much around at all.</p>\n<hr>\n<p>I don&#x27;t believe that there is any systematic pedagogical study on braces versus indentation specifically. Studies of actual <em>learning</em> are complex, time-consuming, and expensive, and I don&#x27;t think any has been done (or likely would be) on that difference in isolation. The totality of human-factors studies comparing language design decisions likely numbers in the dozens (depending on your counting criteria, somewhere between 35 and 137 existed in 2012, per <a href=\"https://urn.fi/URN:ISBN:978-951-39-6388-0\">Antti-Juhani Kaijanaho&#x27;s PhD thesis</a>).</p>\n<p>However, there has been work on program comprehension and reproduction under different indentation and delimiting modalities, and on learning performance in different languages and editing environments. These are more mixed on indentation than you might expect: it&#x27;s widely accepted that correctly indenting to match structure is helpful, but in fact empirical studies often show limited or no impact in practice. My high-level summary of the state of the art for block structure <em>for novice programmers</em> is that</p>\n<ol>\n<li>indentation is somewhat valuable, but they don&#x27;t use it unless enforced;</li>\n<li>keyword-based systems are more effective than symbol-based systems; and</li>\n<li>redundancy enables enhanced errors and thus improves effectiveness.</li>\n</ol>\n<p>None of these are strongly results. It is not clear that the benefits of keywords survive to expert programmers or with editor support for delimiting symbols, while indentation may well be <em>more</em> valuable to experts than novices. There has also been a lot of work with Lisps for novices, which are all delimiter and no indentation. It is likely that the ideal system for learners at this stage will both use delimiters <em>and</em> enforce that indentation is consistent with those delimiters (and this is the approach that recent novice-targeting languages like Pyret and Grace have taken, albeit without specific evidentiary basis).</p>\n<p>I will point to some specific studies below, although they are mostly not as strongly-grounded as I would like. None of the indentation-focused ones are comparing to <em>brace</em>-based languages or to semantically-significant indentation, though there are some suggestive pointers. I think the most relevant result is from the Quorum studies, suggesting that braces in particular perform poorly.</p>\n<hr>\n<ul>\n<li><a href=\"https://doi.org/10.1016/S0020-7373(84)80068-1\">The effect of indentation on program comprehension</a>, Thomas E. Kesler, Randy B. Uram, Ferial Magareh-Abed, Ann Fritzche, Carl Amport, H.E. Dunsmore, 1982.</li>\n<li><a href=\"https://doi.org/10.1145/182.358437\">Program Indentation and Comprehensibility</a>, Richard J. Miara, Joyce A. Musselman, Juan A. Navarro, Ben Shneiderman, 1983.</li>\n</ul>\n<p>These studies looked at Pascal (keyword blocks, but all <code>begin</code>-<code>end</code>) programs with different indentation levels. The findings were that moderate indentation was beneficial for novice comprehension over both no indentation and greater levels of indentation. These are not brace-based programs and participants were already learning or programming in Pascal; the effect size is also surprisingly large for the difference between four- and six-space indents. There were a number of indentation studies around this time, with generally similar results, but not with novices in pure indentation-structured languages.</p>\n<ul>\n<li><a href=\"https://doi.org/10.1109/ICPC.2019.00033\">Indentation: Simply a Matter of Style or Support for Program Comprehension?</a>, Jennifer Bauer, Janet Siegmund, Norman Peitek, Johannes C. Hofmeister, Sven Appel, 2019.</li>\n</ul>\n<p>This was a partial replication of the previous study, using curly-brace Java instead of Pascal, with students, and eight instead of six spaces. They found <em>no impact from indentation at all</em> on any of their measures: even zero indentation gave similar performance.</p>\n<ul>\n<li><a href=\"https://doi.org/10.1016/S0020-7373(84)80065-6\">Conditional statements and program coding: an experimental evaluation</a>, Iris Vessey, Ron Weber, 1984.</li>\n</ul>\n<p>This compares an IF-GOTO-based structure with (non-semantic) indentation with a variety of block-structured systems, using genuine novices in a controlled trial. The closest to braces is the <code>begin</code>-<code>end</code> variant of the language, and the programs in the GOTO version <em>looked like</em> an off-side-rule language, but the line labels were semantically significant; the other variants used <code>IF</code>-<code>OTHERWISE</code> and <code>IF x</code>-<code>NOT x</code>. The methodology had participants review a short tutorial on the language condition in use, and then write programs to implement a specific condition table. Indentation helped for all but one of their variants, but the version with distinguished start and end markers for <em>each</em> conditional performed worse. Participants had a lot of difficulty getting the indentation right in the GOTO case, but when they did their programs were no worse than others.</p>\n<ul>\n<li><a href=\"https://doi.org/10.1145/1852786.1852850\">Impact of maintainability defects on code inspections</a>, \u00d6zlem Albayrak, David Davenport, ESEM 2010.</li>\n</ul>\n<p>This study measured defect detection rates from inspection of Java code with mid-level students, and found that bad indentation impaired detection more than bad naming. This was inconsistent indentation, rather than none, which the other studies haven&#x27;t touched. These programs would likely be static errors in an indentation-based language, but might misbehave instead.</p>\n<ul>\n<li><a href=\"https://doi.org/10.5220/0012087500003538\">Indentation in Source Code: A Randomized Control Trial on the Readability of Control Flows in Java Code with Large Effects</a>, Johannes Morzeck, Stefan Hanenberg, Ole Werger, and Volker Gruhn, in ICSOFT 2023.</li>\n<li><a href=\"https://doi.org/10.1007/s10664-024-10531-y\">Indentation and reading time: a randomized control trial on the differences between generated indented and non-indented if-statements</a>, Stefan Hanenberg, Johannes Morzeck, and Volker Gruhn in Empirical Software Engineering 29(5), 2024.</li>\n</ul>\n<p>These studies look at the time taken to comprehend braced Java code (not edit it) with different indentation structures. The result is that indentation reduces the time taken for the reader to react to a prompt about the control flow of a piece of code substantially, incorrect responses were provided more often in unindented code, and higher TLX difficulty was associated with lack of indentation; the suggestion then is that indentation makes the code more understandable. All the programs were artificial nested-<code>if</code>-statement structures operating on integers, and all had braces (a similar set of authors <a href=\"https://doi.org/10.1007/978-3-031-61753-9_4\">also examined indentation of JSON</a> instead of control flow, with comparable results).</p>\n<p>There was an interesting inter-group analysis result: professional programmers were the most accurate group by a long way in the indented condition, and the <em>least</em> accurate group in the unindented condition, while undergraduate students were the fastest in both cases. This may be another point for reduced effect of indentation for learners in particular, but it&#x27;s not clear what to take away from it, and it did not examine any effects on learning. There&#x27;s also quite a high level of variation between individuals.</p>\n<p>I think this last one is the highest-quality study that isolated indentation as a factor, and none of them approach your question directly. We can take some hints that indentation is probably useful and can be sufficient, but that delimited block structure can <em>also</em> be sufficient on its own, and learners seem if anything to get <em>less</em> benefit from indentation than others.</p>\n<hr>\n<ul>\n<li><a href=\"https://doi.org/10.1145/2534973\">An Empirical Investigation into Programming Language Syntax</a>, Andreas Stefik and Susanna Siebert, 2013.</li>\n</ul>\n<p>This is part of the <a href=\"https://quorumlanguage.com/\">Quorum</a> project, a focal point for modern empirical language design for novices (and the source of the famous &quot;randomly-generated language more usable than Perl&quot; claim). Most of this relates to keyword choices and indentation was not studied specifically, but of relevance here was a finding that <strong>block constructions that <em>did not</em> use braces had better performance metrics than those that did</strong>, although that subgroup included both the indentation-only Python and the <em>keyword</em>-<code>end</code> Quorum and Ruby.</p>\n<p>The best performers, very close together, were Ruby and then Python, with Perl and Java not statistically significantly different from Randomo, which was Quorum with braces and random ASCII keywords. Some issues writing the <code>end</code> keyword correctly were reported, but Quorum still uses that keyword today, so it wasn&#x27;t found to be that bad of an issue.</p>\n<p>In all cases, &quot;correct&quot; indentation was presented in the learning material, but I don&#x27;t believe it was required in what was produced except for Python. Indentation is encouraged <em>but not required</em> in Quorum, I think to reduce unnecessary errors during editing. Follow-up work on Quorum has reinforced these results for it.</p>\n<p>There are various other studies on teaching language pairs that I&#x27;m sure you&#x27;ve seen, but they don&#x27;t generally get to this level of granularity and there are significant confounds for teasing out anything like this.</p>\n<hr>\n<p>To conclude here, I don&#x27;t believe such a study exists. I am confident that there isn&#x27;t one prior to 2014, and I am not aware of and couldn&#x27;t find any since either. I am sceptical that it would be worthwhile to run one for novices, because the value of that one factor is unlikely to be worth the substantial cost of running a genuine learning study, though a comprehension study that gets deeper than Hanenberg, Morzeck, and Gruhn is plausible.</p>\n<p>The general direction of the literature is that <em>braces</em> are not highly effective for novices, although lexically substituting an explicit end-of-block delimiter <em>word</em> for <code>}</code> seems to do better, and omitting any opening delimiter has some support too. Semantic indentation isn&#x27;t supported one way or the other by this, but there is some evidence that matching end delimiters presents a difficulty, which is a matter that doesn&#x27;t arise for semantic indentation. I don&#x27;t think there is sufficient evidence to draw a conclusion one way or another.</p>\n                <section class=\"references\">\n                    <h2>References</h2>\n                    <ul>\n                <li><span class=\"h-cite\"><span class=\"h-author h-card\">Albayrak, \u00d6zlem</span> and <span class=\"h-author\">David Davenport</span>. <time class=\"dt-published\">2010</time>. \u201c<cite class=\"h-name\">Impact of maintainability defects on code inspections</cite>\u201d. In Proceedings of the 2010 ACM-IEEE International Symposium on Empirical Software Engineering and Measurement (ESEM '10): 1\u20134. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/1852786.1852850\" class=\"u-url\">https://doi.org/10.1145/1852786.1852850</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Bauer, Jennifer</span>, <span class=\"h-author\">Janet Siegmund</span>, <span class=\"h-author\">Norman Peitek</span>, <span class=\"h-author\">Johannes C. Hofmeister</span> and <span class=\"h-author\">Sven Apel</span>. <time class=\"dt-published\">2019</time>. \u201c<cite class=\"h-name\">Indentation: Simply a Matter of Style or Support for Program Comprehension?</cite>\u201d. In 2019 IEEE/ACM 27th International Conference on Program Comprehension (ICPC): 154\u2013164. IEEE. <a href=\"https://doi.org/10.1109/icpc.2019.00033\" class=\"u-url\">https://doi.org/10.1109/icpc.2019.00033</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Hanenberg, Stefan</span>, <span class=\"h-author\">Johannes Morzeck</span> and <span class=\"h-author\">Volker Gruhn</span>. <time class=\"dt-published\">2024</time>. \u201c<cite class=\"h-name\">Indentation and reading time: a randomized control trial on the differences between generated indented and non-indented if-statements</cite>\u201d. In Empirical Software Engineering 29 (5). Springer Science and Business Media LLC. <a href=\"https://doi.org/10.1007/s10664-024-10531-y\" class=\"u-url\">https://doi.org/10.1007/s10664-024-10531-y</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Hanenberg, Stefan</span>, <span class=\"h-author\">Johannes Morzeck</span>, <span class=\"h-author\">Ole Werger</span>, <span class=\"h-author\">Stefan Gries</span> and <span class=\"h-author\">Volker Gruhn</span>. <time class=\"dt-published\">2024</time>. \u201c<cite class=\"h-name\">Indentation and\u00a0Reading Time: A Controlled Experiment on\u00a0the\u00a0Differences Between Generated Indented and\u00a0Non-indented JSON Objects</cite>\u201d. In Communications in Computer and Information Science (ICSOFT 2023): 50\u201375. Springer Nature Switzerland, Cham. ISBN: 9783031617522. <a href=\"https://doi.org/10.1007/978-3-031-61753-9_4\" class=\"u-url\">https://doi.org/10.1007/978-3-031-61753-9_4</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Kaijanaho, Antti-Juhani</span>. <time class=\"dt-published\">2015</time>. \u201c<cite class=\"h-name\">Evidence-based Programming Language Design: A Philosophical and Methodological Exploration (PhD thesis)</cite>\u201d. Thesis. University of Jyv\u00e4skyl\u00e4. ISBN: 9789513963880. <a href=\"https://urn.fi/URN:ISBN:978-951-39-6388-0\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Kesler, Thomas E.</span>, <span class=\"h-author\">Randy B. Uram</span>, <span class=\"h-author\">Ferial Magareh-Abed</span>, <span class=\"h-author\">Ann Fritzsche</span>, <span class=\"h-author\">Carl Amport</span> and <span class=\"h-author\">H.E. Dunsmore</span>. <time class=\"dt-published\">1984</time>. \u201c<cite class=\"h-name\">The effect of indentation on program comprehension</cite>\u201d. In International Journal of Man-Machine Studies 21 (5): 415\u2013428. Elsevier BV. <a href=\"https://doi.org/10.1016/s0020-7373(84)80068-1\" class=\"u-url\">https://doi.org/10.1016/s0020-7373(84)80068-1</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Miara, Richard J.</span>, <span class=\"h-author\">Joyce A. Musselman</span>, <span class=\"h-author\">Juan A. Navarro</span> and <span class=\"h-author\">Ben Shneiderman</span>. <time class=\"dt-published\">1983</time>. \u201c<cite class=\"h-name\">Program indentation and comprehensibility</cite>\u201d. In Communications of the ACM 26 (11): 861\u2013867. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/182.358437\" class=\"u-url\">https://doi.org/10.1145/182.358437</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Morzeck, Johannes</span>, <span class=\"h-author\">Stefan Hanenberg</span>, <span class=\"h-author\">Ole Werger</span> and <span class=\"h-author\">Volker Gruhn</span>. <time class=\"dt-published\">2023</time>. \u201c<cite class=\"h-name\">Indentation in Source Code: A Randomized Control Trial on the Readability of Control Flows in Java Code with Large Effects</cite>\u201d. In Proceedings of the 18th International Conference on Software Technologies. SCITEPRESS - Science and Technology Publications. <a href=\"https://doi.org/10.5220/0012087500003538\" class=\"u-url\">https://doi.org/10.5220/0012087500003538</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Stefik, Andreas</span> and <span class=\"h-author\">Susanna Siebert</span>. <time class=\"dt-published\">2013</time>. \u201c<cite class=\"h-name\">An Empirical Investigation into Programming Language Syntax</cite>\u201d. In ACM Transactions on Computing Education 13 (4): 1\u201340. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/2534973\" class=\"u-url\">https://doi.org/10.1145/2534973</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Vessey, Iris</span> and <span class=\"h-author\">Ron Weber</span>. <time class=\"dt-published\">1984</time>. \u201c<cite class=\"h-name\">Conditional statements and program coding: an experimental evaluation</cite>\u201d. In International Journal of Man-Machine Studies 21 (2): 161\u2013190. Elsevier BV. <a href=\"https://doi.org/10.1016/s0020-7373(84)80065-6\" class=\"u-url\">https://doi.org/10.1016/s0020-7373(84)80065-6</a>.</span></li>\n                    </ul>\n                </section>\n", "attachments": [{"url": "https://michael.homer.nz/notes/novice-braces-indentation/novice-braces-indentation.html", "mime_type": "text/html", "title": "Single-file archive of article: What evidence exists about brace- and indentation-based syntax for novice programmers?", "size_in_bytes": 53600}]}, {"title": "How often do variables in dynamically-typed code actually change type?", "language": "en-US", "url": "https://michael.homer.nz/notes/dynamic-type-changes/", "date_published": "2023-12-09T00:00:00", "id": "https://doi.org/10.59350/6qc07-pcv74", "authors": [{"name": "Michael Homer", "url": "https://orcid.org/0000-0003-0280-6748"}], "content_html": "<p>I&#x27;ve decided to self-archive some of my Stack Exchange answers that contain real research work, particularly the surveys that represent a lot of synthesis of the literature, so that I&#x27;ve got them here even if Stack Exchange goes away and I can extend and annotate them further.\nThis investigation grew from an answer I wrote to a Stack Exchange question <a href=\"https://langdev.stackexchange.com/q/2707/133\">Are there metrics on how often variables in dynamically-typed languages change their type (not &quot;parametrically&quot;)</a> about programmers in dynamically-typed languages using the same variable with multiple data types, outside of generic functions and automated type coercions.</p>\n<hr>\n<p>A lot of work on type <em>inference</em> for dynamically-typed languages faces this issue, and often enough in practice that it comes up as something to be dealt with, while rarely enough that it&#x27;s sometimes explicitly excluded from support. Some work has looked specifically at the use of dynamically-typed features on their own.</p>\n<p>In most cases the relevant results are on the side of something else, either an inference engine or analysis of more complex dynamic features. It doesn&#x27;t seem like anyone has looked specifically at reassignment, which I do find a little surprising.</p>\n<p>Further down I&#x27;ll note some limitations, and other cases that may or may not be in scope here. Exactly what it means for a variable to hold a new type is potentially a bit tricky to nail down. For example, what constitutes a type in these languages? is it necessary that the variable be reassigned?</p>\n<hr>\n<p>Xia et al. performed <a href=\"https://doi.org/10.1007/978-3-030-04272-1_6\">an empirical study on dynamic-typing behaviours in Python programs</a> on a substantial corpus of popular real Python systems, finding that at least 6.9% of identifiers had multiple types, and another 13.4% were undetermined by their methodology, while at least 79.7% of identifiers had only a single type. They show that even assigning <em>two different types of literal</em> to a variable sequentially has real incidence, while still being uncommon. They present a heat map of this matrix in Figure 5, but unfortunately an &quot;expression&quot; category squashes together what could be many different kinds of conversion into one (and I think the table over-eagerly aligns changes of type with changes of expression kind).</p>\n<p>Chen et al. performed <a href=\"https://doi.org/10.1145/3387904.3389253\">another Python study</a> and found an average of 311 instances of variables being given different types across their benchmark. I don&#x27;t think this average metric they reported is particularly useful, but all but one of the systems under study had more than 50, and these were the most common dynamic-typing behaviours they measured by a large margin. Excluding the low and high outliers, between 10% and 25% of methods they examined in each system contained one or more of the traits they examined, and these would primarily be variable typing. However, most instances found did combine string with another type, noting that</p>\n<blockquote>\n<p>It is often the case that the value of a variable\nis parsed from the user inputs and its type is determined by the\nsource of the inputs (e.g., XML files, database or command line).</p>\n</blockquote>\n\n<p>They do present examples from real-world systems that show other replacements of values with different types, however, including strings. An example is given from IPython where a variable holding a dictionary is assigned one of the values from that dictionary. The reporting does not have enough detail to distinguish all of the cases desired to rule out.</p>\n<p>Furr et al. <a href=\"https://doi.org/10.1145/1640089.1640110\">studied Ruby programs</a> using a profile-guided tool to infer static types for unannotated code. To produce typecheckable programs, the tool required refactoring of the benchmark programs to remove precisely this issue: they performed 11 refactorings to break multi-typed variables into multiple uni-typed variables, out of 226 total refactorings across their suite (a number of these related to specific limitations of this version of the tool, like support for dynamic type tests, so the &quot;true&quot; proportion would be higher). They also note 12 instances of fundamentally untypeable code, notably within the optparse module that parses command-line options: while these issues <em>are</em> string-&gt;other conversions, the problem is that the repeated control flow gives variables multiple values for different options, so you might count these as well. However, both classes are relatively rare and do not occur in the majority of their benchmark programs.</p>\n<p>Pradel et al. <a href=\"https://doi.org/10.1109/ICSE.2015.51\">produced a tool for finding type inconsistencies in JavaScript code</a>, and benchmarked it against a suite of published code. This system was focused on finding potential bugs, rather than observing idioms, but it did encounter several cases of mixed types within functions that seem intentional; most of these involve the &quot;undefined&quot; type and another, which is meaningful within JavaScript&#x27;s type model but could be analysed as a single nullable type as well. Other combinations also existed, but were less common.</p>\n<p>I believe one of the Vitek analyses of R also touches on this question, but I haven&#x27;t been able to find which one, if it does exist. I have <em>seen</em> a presentation on the incidence of this pattern in PHP code (quite common), but can&#x27;t find any archival publication of the result; it&#x27;s an explicit motivator for much of the Hack work, so I expect it has been measured to matter on the Facebook codebase too.</p>\n<hr>\n<p>One limitation that all of these face is that they are benchmarking against <em>published</em> code, which may tend not to use some of these features as much. It is likely that this overwriting is much more common in interactive use, which is inherently ephemeral and so not included in benchmarks. For example, a significant amount of R usage is entirely interactive, and maintaining a single variable for the in-progress results of where the user is up to is not uncommon, notwithstanding that the variable may hold different types internally as further analysis steps are run (and these different types may or may not be significant or known to the user). Some of this sort of interactive use may persist into non-published scripts calcified from interactive sessions.</p>\n<p>It&#x27;s also the case, though, that code where a variable has type X up to a point, and type Y thereafter, isn&#x27;t necessarily resting on dynamic typing at all: without loss of generality, this can be taken as two separate variables that have the same name, one shadowing the other. It&#x27;s only cases where the variable is accessed from a loop or through lexical capture, or the type changes conditionally, where the ability to mix types together really matters. This is what the refactoring from the PRuby work above rested on. The reverse can also be true: idiomatic JavaScript will declare uninitialised variables before a loop to be set inside, such as in a search \u2014 but this is strictly a change of type within JavaScript&#x27;s type model, because &quot;undefined&quot; is its own type! Similarly, Python <code>None</code> is used in the same role, and is not a bottom type either. It&#x27;s necessary to drill down very specifically into what is meant by type changes in order to quantify them.</p>\n<p>I haven&#x27;t touched here on another sort of &quot;type&quot; change: meta-mutable object values may be seen to have a different type each time one of their properties is added or removed. This could happen either as a value is built up originally (consider idiomatic JavaScript <code>let x = {}; x.a = 1; x.b = 2;</code>), either inline or by passing it to other code to populate; it can also happen long afterwards while the variable is still in scope somewhere. A change could also arise from modifications made to inheritance parents. Richards et al. <a href=\"https://doi.org/10.1145/1806596.1806598\">showed that all of these sorts of change are very common</a> in JavaScript code, and many are also possible and idiomatic in other languages on the more dynamic end of the spectrum. In Ruby, both monkeypatching existing types and eigenclass modifications are normal parts of using the language. These would be changes of a variable&#x27;s type from some perspectives, and not others, and I&#x27;m not sure whether they&#x27;re in scope of this question or not.</p>\n                <section class=\"references\">\n                    <h2>References</h2>\n                    <ul>\n                <li><span class=\"h-cite\"><span class=\"h-author h-card\">Chen, Zhifei</span>, <span class=\"h-author\">Yanhui Li</span>, <span class=\"h-author\">Bihuan Chen</span>, <span class=\"h-author\">Wanwangying Ma</span>, <span class=\"h-author\">Lin Chen</span> and <span class=\"h-author\">Baowen Xu</span>. <time class=\"dt-published\">2020</time>. \u201c<cite class=\"h-name\">An Empirical Study on Dynamic Typing Related Practices in Python Systems</cite>\u201d. In Proceedings of the 28th International Conference on Program Comprehension (ICPC '20): 83\u201393. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3387904.3389253\" class=\"u-url\">https://doi.org/10.1145/3387904.3389253</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Furr, Michael</span>, <span class=\"h-author\">Jong-hoon (David) An</span> and <span class=\"h-author\">Jeffrey S. Foster</span>. <time class=\"dt-published\">2009</time>. \u201c<cite class=\"h-name\">Profile-guided static typing for dynamic scripting languages</cite>\u201d. In Proceedings of the 24th ACM SIGPLAN conference on Object oriented programming systems languages and applications (OOPSLA09): 283\u2013300. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/1640089.1640110\" class=\"u-url\">https://doi.org/10.1145/1640089.1640110</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Pradel, Michael</span>, <span class=\"h-author\">Parker Schuh</span> and <span class=\"h-author\">Koushik Sen</span>. <time class=\"dt-published\">2015</time>. \u201c<cite class=\"h-name\">TypeDevil: Dynamic Type Inconsistency Analysis for JavaScript</cite>\u201d. In 2015 IEEE/ACM 37th IEEE International Conference on Software Engineering (ICSE): 314\u2013324. IEEE. <a href=\"https://doi.org/10.1109/icse.2015.51\" class=\"u-url\">https://doi.org/10.1109/icse.2015.51</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Richards, Gregor</span>, <span class=\"h-author\">Sylvain Lebresne</span>, <span class=\"h-author\">Brian Burg</span> and <span class=\"h-author\">Jan Vitek</span>. <time class=\"dt-published\">2010</time>. \u201c<cite class=\"h-name\">An analysis of the dynamic behavior of JavaScript programs</cite>\u201d. In Proceedings of the 31st ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI '10): 1\u201312. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/1806596.1806598\" class=\"u-url\">https://doi.org/10.1145/1806596.1806598</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Xia, Xinmeng</span>, <span class=\"h-author\">Xincheng He</span>, <span class=\"h-author\">Yanyan Yan</span>, <span class=\"h-author\">Lei Xu</span> and <span class=\"h-author\">Baowen Xu</span>. <time class=\"dt-published\">2018</time>. \u201c<cite class=\"h-name\">An Empirical Study of Dynamic Types for Python Projects</cite>\u201d. In Lecture Notes in Computer Science (SATE 2018): 85\u2013100. Springer International Publishing, Cham. ISBN: 9783030042714. <a href=\"https://doi.org/10.1007/978-3-030-04272-1_6\" class=\"u-url\">https://doi.org/10.1007/978-3-030-04272-1_6</a>.</span></li>\n                    </ul>\n                </section>\n", "attachments": [{"url": "https://michael.homer.nz/notes/dynamic-type-changes/dynamic-type-changes.html", "mime_type": "text/html", "title": "Single-file archive of article: How often do variables in dynamically-typed code actually change type?", "size_in_bytes": 45464}]}, {"title": "Live 2D Compositional Programming", "language": "en-US", "url": "https://michael.homer.nz/notes/compositional-2d/", "date_published": "2022-10-20T00:00:00", "id": "https://doi.org/10.59350/hsm7b-2jh64", "authors": [{"name": "Michael Homer", "url": "https://orcid.org/0000-0003-0280-6748"}], "content_html": "<h2 id=\"Introduction\">Introduction</h2>\n<p>This system started out as an experiment in making an obtuse\nprogramming paradigm more approachable.</p>\n<p>Sometimes regarded as &quot;write-only languages&quot;,\nstack-based <em>concatenative</em> <sup>^1</sup>\nlanguages rely on implicit argument\npassing and return values, with programs often just a bare\nsequence of function names, but they provide a very concise\nexpression of pipelines.</p>\n<p>A two-dimensional layout showing both the functions and the\ncorresponding values on the stack could help to make clear\nwhere values are coming from and going.\nHaving built it, though, it turned out to naturally express\nprograms outside of that paradigm as well\u2014and perhaps more usefully.</p>\n<p>In fact, artificial restrictions were necessary to ensure\nthat programs did remain concatenative, while the non-concatenative\nconstructions had clear meaning.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/teaser-demo.mp4\" type=\"video/mp4\">Video: Teaser video showing several interactions with the system</video>\n<p>This system lends itself to live editing of a program and\ndisplaying intermediate (and final) values throughout, and\nto exposing exactly the operations that are available at\nany given time.</p>\n<p>The following sections will discuss the background,\ndemonstrate usage, explore what can be done with it,\nand divert to investigating the uncovered semantics\nof the language.</p>\n<h2 id=\"Compositional-Programming\">Compositional Programming</h2>\n<p><em>Concatenative</em> languages are those taking two programs\nand appending them together forms a new program,\nperforming the operation of one on the results of the other.</p>\n<p>This operation is <em>composition</em>, and is exactly\nanalogous to composition of mathematical functions, or to\ncomposition operators in functional languages like Haskell.</p>\n<p>In fact, concatenative languages can be analysed as\nfunctional languages where the default (juxtaposition)\noperation is function composition, rather than application</p>\n<p>Many concatenative languages are stack-based (e.g. Forth, <a href=\"https://web.archive.org/web/20091227112540/http://www.latrobe.edu.au/philosophy/phimvt/joy/j02maf.html\">Joy</a>, <a href=\"https://doi.org/10.1145/1899661.1869637\">Factor</a>),\npassing arguments\nand return values implicitly between functions, though there are\nnon-stack-based languages as well <sup>^2</sup>;\nwe (largely) do not deal with those here.</p>\n<p>The <a href=\"https://concatenative.org/\">Concatenative wiki</a>\nlists and provides information about a number of concatenative\nlanguages.</p>\n<p>Composing sequences of (sub)programs into larger programs\nis also analogous to Unix pipelines, and composition can be\na core operation without being strictly concatenative.</p>\n<p>One useful feature that these languages tend to have is the\nability for functions to have multiple return values, and\nsend some of them on for further processing.</p>\n<p>This particular aspect of compositional programming is key\nin what this system does.</p>\n<p>The next section will discuss the origins of this system\nin trying to support concatenative programming, before\nrelaxing that requirement and exploring what else can be\ndone with it.</p>\n<h2 id=\"Origins\">Origins</h2>\n<p>The <a href=\"https://doi.org/10.1145/3563836.3568722\">original motivation</a>\nfor this system was to provide a more\napproachable way to read and write concatenative programs.\nConcatenative languages are sometimes seen as &quot;write-only&quot;, because\nreading unknown code often requires simulating the parameter-passing\nstack mentally for each function, and knowing at least the arities of\nevery function involved.</p>\n<p>This two-dimensional notation can represent any of these\nconcatenative programs with all arguments and return values\nexplicitly manifest in the display: each function cell is laid out\ndirectly below its arguments, and stretched above its return\nvalues.\nThe grid thus alternates row-by-row between functions and values.\nA linear concatenative program can be rendered directly\ninto the grid:</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/delinearise.mp4\" type=\"video/mp4\">Video: Animation of program terms from a linear sequence to the grid.</video>\n<p>Either the types or concrete values can be displayed for the\nargument/return values.\nSimilarly, it is possible to linearise\na program back into conventional textual form.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/linearise.mp4\" type=\"video/mp4\">Video: Animation of program terms from the 2D grid to a linear text sequence.</video>\n<p>This notation permits editing the programs as well:\nBecause adjacent stack elements are side-by-side, a single drag\ncan select arguments to give to a function, and a menu of available\nfunctions that can consume those arguments offered to select from.\nIf the values are not in the right order, the option to swap is always\navailable.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/swap.mp4\" type=\"video/mp4\">Video: Two program values being selected and the &quot;swap&quot; operation added to be peformed on them.</video>\n<p>However, for purely concatenative use there is one further\nlimitation: the <em>rightmost</em> argument chosen must be the rightmost <em>output</em>\nof another function, in order for these values\never to be on top of the stack together.</p>\n<p>Relaxing this rule results in programs that <em>do not</em>\ncorrespond to typical concatenative programs, but which still have\nclear meaning: each function consumes the values from above, and\nproduces the values below, and only lacks the ability to turn into\na \u2014 fairly inscrutable \u2014 textual concatenative program.\nThis relaxed non-linearisable system is what we are interested in\nhere, as it provides a number of convenient features for live\nand exploratory programming, despite an execution model that is\nquite strange in conventional terms.</p>\n<h2 id=\"Live-Programming\">Live Programming</h2>\n<p>The interface allows displaying either the types accepted/produced\nby each function, or the concrete values of the arguments/returns.\nIn the latter case, true live programming is available: the user\ncan select a value, or a series of consecutive values,\nand choose the operation to process it or to combine them, and\nthe result is also displayed.</p>\n<p>Working in this way, available operands are always visible and\ntangible.\nThis is useful both for manipulation, and potentially for the\ndisplay itself, which may be the purpose of the program in the\nfirst place.\nAt any point the user can stop, and has a program that calculates\nand displays some values.</p>\n<p>This representation also shows the &lt;em&gt;provenance&lt;/em&gt; of the\nresults innately: all of the precursor steps, and precursor\nvalues, are explicitly part of the display.</p>\n<h3 id=\"Highlevel-Data-Types\">High-level Data Types</h3>\n<p>Examples so far have used primitive types, but arbitrary values\ncould be displayed for the user to work with, including\ncompound types or images.\nDisplaying these higher-level types may again be the purpose, but\nalso provides the opportunity for further affordances.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/image-colours.mp4\" type=\"video/mp4\">Video: Sample program using colours as a first-class editable data type.</video>\n<h3 id=\"Exploratory-Programming\">Exploratory Programming</h3>\n<p>Because all available values are always visible, the user can\nengage in undirected exploration of the program space.\nSelecting a value, or range of values, and seeing what functions\ncan be applied to them, enables discovering available pathways\nto experiment with.</p>\n<p>A function can be chosen, its results seen, and then be either\ncontinued with, or reverted.\nAny function can also be replaced in-place with another of the\nsame type proferred from the menu.</p>\n<p>The nullary functions for literals are also editable live,\nso that a numeric or string constant can be experimentally\nreplaced and the impact on the program seen.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/replace-edit.mp4\" type=\"video/mp4\">Video: Editing a program by replacing values within the grid.</video>\n<h3 id=\"Comments\">Comments</h3>\n<p>The grid structure opens up an interesting possibility for\ncomments: the user can select a range of cells, and add a\ncomment to them.\nThese need not cover the entire row, and can be applied\nto only particular &lt;em&gt;values&lt;/em&gt; rather than functions,\nlines, or blocks.\nOf course, it is still possible to select the entire row\nto comment, but additional flexibility is provided.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/comment.mp4\" type=\"video/mp4\">Video: Illustration of comments spanning terms in the grid.</video>\n<h2 id=\"Working-with-Functions\">Working with Functions</h2>\n<p>So far we have looked only at building a whole program at once\nout of built-in functions, but user-defined functions can work\nin the same way.</p>\n<p>Another option provided when choosing a sequence of arguments is\nto create a new function that could accept them.\nThis function will be in a new tab, with the chosen parameter\ntypes set out across the top, and all the usual affordances\navailable as well.\nThe values left over at the end of the function will be what\nit returns, and the function can be used anywhere that those\nparameter types are available.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/create-function.mp4\" type=\"video/mp4\">Video: A function being created to process selected values.</video>\n<p>As in concatenative programming, abstracting sequences of\noperations to named functions of their own is encouraged,\nto keep the program clear and comprehensible.\nIn this system in particular, the sheer amount of space\ntaken up by the grid layout provides added incentive.</p>\n<h3 id=\"Lifting-Functions\">Lifting Functions</h3>\n<p>This environment provides no looping constructs, and not (so far)\nhigher-order functions.\nGiven a list of values, there is thus no way to apply some\noperation across the list (neither a map nor a fold).</p>\n<p>Instead, user-defined functions have a &quot;lifting&quot; configuration\noption, accessible in their sidebar.\nThese liftings, in effect, wrap the function in a higher-order\noperation: the &quot;Mappable&quot; lifting applies the Functor map\noperation to it, for example.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/lifting-mappable.mp4\" type=\"video/mp4\">Video: &quot;Lifting&quot; a function to be applied across a sequence by selecting the &quot;Mappable&quot; configuration option for it.</video>\n<p>A Mappable function that takes in a single string and returns\na single int can be applied to a list of strings, a set of strings,\nor an optional of string, and will return the same wrapper type\nwith the produced int value(s) inside.\nSimilarly, a Foldable function that takes in an integer\nand a string can be applied to a list of strings and an integer\ninitial value (in the opposite order for compositional convenience).\nThis appproach provides the power of common higher-order functions\nwithout needing users to understand that sort of abstraction,\nand without requiring a more complex &quot;syntax&quot;.</p>\n<p>Additional higher-order operations are possible,\nbut it is likely that at some point they introduce\ntoo much complexity to the type system and user\ninterface.\nSo far, the system does not support first-class functions\nthemselves, but they may be appropriate for a future version.</p>\n<h3 id=\"Identifying-Inputs-and-Outputs\">Identifying Inputs and Outputs</h3>\n<p>For functions with multiple inputs or outputs, it is not\nalways obvious which order these will be in.\nTo make this easier, some functions have a label configured\nfor each argument and return value position, which is\ndisplayed within the cell of the corresponding value,\nas smaller text above the value or type for a\nreturn value label, and below for a label given\nby the function consuming the value.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/sobriquets.mp4\" type=\"video/mp4\">Video: Labels shown on individual inputs and outputs of functions within the grid indicating their roles.</video>\n<p>These annotations are quite noisy and it is not\nclear that they are always advantageous, but the\ncurrent implementation includes them for arithmetic\nfunctions, built-in functions with many arguments,\nand accessors for record types.</p>\n<p>The user can also apply one of these labels to\nany stack value using a special &lt;code&gt;@&lt;i&gt;label&lt;/i&gt;&lt;/code&gt;\nfunction.\nA manual label may be just a note for the programmer,\nbut will also be carried through as a label for a\nreturn value of a user-defined function, and is\npreserved by operations such as <code>swap</code> and <code>dup</code>.</p>\n<video muted loop controls playsinline><source src=\"https://michael.homer.nz/notes/compositional-2d/user-label.mp4\" type=\"video/mp4\">Video: User-defined labels created that appear the same as the built-in function labels above.</video>\n<h2 id=\"Language-Semantics\">Language Semantics</h2>\n<p>While stack-based concatenative programs are expressible in this\nsystem, the non-linearisable programs have an unusual execution\nmodel that would be difficult to express in a conventional\ntextual language.\nWe describe two ways of analysing the execution of\nprograms in this model,\nbut neither is notably <em>useful</em> <sup>^3</sup>;\nthe visible vertical dataflow\nshould be the primary understanding of the approach,\nand we identify these here for completeness only.</p>\n<p>There is a linearisable representation of the semantics,\nbut it is queue-based rather than stack-based.\nFunctions shift their arguments from the queue and push their\nreturn values onto it, and are executed in order left-to-right\nrow-by-row.\nThe &quot;empty&quot; function cells where values span multiple rows are\nrepresented by the identity function in this linearisation,\nso the pushes and pulls will add up but corresponding operations\nwill virtually always be distant from each other.</p>\n<p>It can also be analysed as each row defining a function\nperforming the operations of all its cells, from\na tuple of arguments to a tuple of outputs to be consumed by\nthe next row.</p>\n<p>In either case, this <strong>is</strong> still actually concatenative: a program\nthat transforms A B to C D can be concatenated with one\ntransforming C D to E F to get a program that transforms A B to E F.\nIn terms of the 2D representation, this concatenativity is\nrow-by-row\u2014cutting off the final rows of a program results in a\nnew program that consumes the outputs of the first.\nHowever, the value of this fact is limited and the direct\nargument-function-outputs mapping represented by the\ngrid layout is always likely to be most useful.</p>\n<h2 id=\"Inspirations\">Inspirations</h2>\n<p>A significant inspiration for this work was\ndataflow programming environment.\nUserland&#x27;s approach to shell pipelines in particular, where\nconsecutive stages of the pipeline were laid out horizontally,\nthe standard output of each phase below, showed a path for this\nsort of composition.</p>\n<p>Another of course is the spreadsheet, the most widely-used\ndataflow programming system, and a grid-based one as well.\nThe actual execution of this system is not similar to\nspreadsheets, but some of the affordances for working with\nthe grid are.</p>\n<p>Live dataflow systems like <a href=\"https://natto.dev/\">Natto</a>,\nand Ink &amp;amp; Switch&#x27;s\nproject, also inspire the approach to live updating and high-level data types.\nThe <a href=\"https://doi.org/10.1145/3290327\">Hazel</a> environment and its &quot;typed holes&quot; also\ncontributed to the interaction design.</p>\n<p>The <a href=\"http://kittenlang.org/\">Kitten</a> was an\nentry point into statically-typed concatenative functional\nprogramming languages and influenced the approach taken in the\noriginal version of this system.</p>\n<p>The animations between the textual and visual representations\ndraw on my past work on <a href=\"https://doi.org/10.1109/VISSOFT.2013.6650546\">Tiled Grace</a>,\nwhich introduced this mechanism for making clear the relationship between sides\nin multiple-representation environments.</p>\n<p>Alexander Obenauer&#x27;s\n&quot;<a href=\"https://alexanderobenauer.com/labnotes/000/\">itemized operating system</a>&quot;\nproject also influences the approach to very high-level data types.\nIt would be interesting to integrate with external systems that expose their\ndata in this way, so that it can be programmatically accessed with\nthe pathway to obtain the result being explicit.</p>\n<h2 id=\"Implementation\">Implementation</h2>\n<p>The working environment is accessible in any web browser\nAll programs execute client-side, and the grid is manipulated\nwith the pointer: drag to select values (or click on single values),\ntap on functions to replace, remove, or edit them, and tap on the\nplus button on the right to add a new constant value.\nFunctions and programs are accessed via the tab bar at the top,\nand all functions are saved in the browser&#x27;s local storage upon\nswitching.</p>\n<p>A working version of the system is also below, although it has\nsome limitations in this environment and is less full-featured here.</p>\n\n<div style=\"background:white; color: black;\"><iframe src=\"https://michael.homer.nz/notes/compositional-2d/demo.html\" width=\"100%\" height=\"600\" id=\"theiframe\">\n</iframe></div>\n\n\n\n<p>As it runs in the browser, the implementation is in JavaScript and\nis limited by the browser sandboxing in what it can provide.\nWhen running from a local file (rather than over HTTP) there are further\nlimitations.</p>\n<h2 id=\"Future-Work\">Future Work</h2>\n<p>The present system serves as a demonstration of the concept, and\nhas a range of functionality to explore the ideas. However, there\nare a number of areas for future work, including simply building\nout more comprehensive functionality in the present sphere.</p>\n<p>The core interaction modality seems like it would be a good fit\nfor a mobile device or other touch-screen interaction, and it would\nbe interesting to explore this further.\nSwipe and tap gestures are typical interactions on such devices\nand align directly with the grid and function selection.\nThis approach could provide a mechanism for programming on these\ndevices, in the vein of what\n<a href=\"https://www.microsoft.com/en-us/research/project/touchdevelop/\">TouchDevelop</a>\naimed at.\nSome <a href=\"https://doi.org/10.1145/3532104.3571459\">preliminary work</a>\nextending this system for tablets\nhas shown satisfying interactions.</p>\n<p>The system is currently limited to a single grid, and it would be\ninteresting to explore the possibility of multiple grids visible at once,\nwith\nthe ability to connect them together. This would allow for\nmore complex dataflow systems, and would also allow for\nthe possibility of multiple users working on the same system\nat the same time.</p>\n<p>Additional data types, and richer affordances for working with\nthem, would also be useful.\nEspecially in the exploratory programming context, it would be\nhelpful to have a wide range of operations and clear discovery\nmechanisms, while more direct manipulation of displayed\nhigh-level values to produce code outputs would also be good.</p>\n<p>Connecting to external systems would also be interesting, and the\ndesign of functions permits live-updating inputs for reactive\nprogramming.</p>\n<section class=\"endnotes\">\n<h2>Endnotes</h2>\n<aside>^1 This label is owed to Manfred von Thun and Joy, but languages such as Forth predated the terminology</aside>\n<aside>^2 Non-stack-based concatenative languages include <a href=\"https://www.om-language.org/\">Om</a> and <a href=\"https://doi.org/10.1007/978-3-030-02768-1_10\">Kihi</a>.</aside>\n<aside>^3 It is possible (even likely) that these analyses are helpful\nfor performant <em>execution</em> of programs, but that is\nnot a core focus here.</aside>\n</section>\n                <section class=\"references\">\n                    <h2>References</h2>\n                    <ul>\n                <li><span class=\"h-cite\"><span class=\"h-author h-card\">Frenger, Paul</span>. <time class=\"dt-published\">2003</time>. \u201c<cite class=\"h-name\">The JOY of forth</cite>\u201d. In ACM SIGPLAN Notices 38 (8): 15\u201317. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/944579.944583\" class=\"u-url\">https://doi.org/10.1145/944579.944583</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Homer, Michael</span>. <time class=\"dt-published\">2022</time>. \u201c<cite class=\"h-name\">Interleaved 2D Notation for Concatenative Programming</cite>\u201d. In Proceedings of the 1st ACM SIGPLAN International Workshop on Programming Abstractions and Interactive Notations, Tools, and Environments (PAINT '22): 1\u201310. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3563836.3568722\" class=\"u-url\">https://doi.org/10.1145/3563836.3568722</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Homer, Michael</span> and <span class=\"h-author\">James Noble</span>. <time class=\"dt-published\">2013</time>. \u201c<cite class=\"h-name\">A tile-based editor for a textual programming language</cite>\u201d. In 2013 First IEEE Working Conference on Software Visualization (VISSOFT): 1\u20134. IEEE. <a href=\"https://doi.org/10.1109/vissoft.2013.6650546\" class=\"u-url\">https://doi.org/10.1109/vissoft.2013.6650546</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Homer, Michael</span> and <span class=\"h-author\">Craig Anslow</span>. <time class=\"dt-published\">2022</time>. \u201c<cite class=\"h-name\">Swipe-and-Tap Functional Programming</cite>\u201d. In Companion Proceedings of the 2022 Conference on Interactive Surfaces and Spaces (ISS '22): 18\u201321. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/3532104.3571459\" class=\"u-url\">https://doi.org/10.1145/3532104.3571459</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Jones, Timothy</span> and <span class=\"h-author\">Michael Homer</span>. <time class=\"dt-published\">2018</time>. \u201c<cite class=\"h-name\">The Practice of a Compositional Functional Programming Language</cite>\u201d. In Lecture Notes in Computer Science (APLAS 2018): 166\u2013177. Springer International Publishing, Cham. ISBN: 9783030027674. <a href=\"https://doi.org/10.1007/978-3-030-02768-1_10\" class=\"u-url\">https://doi.org/10.1007/978-3-030-02768-1_10</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Muhammad, Hisham H.</span>. <time class=\"dt-published\">2019</time>. \u201c<cite class=\"h-name\">Userland: creating an integrated dataflow environment for end-users</cite>\u201d. <a href=\"https://youtu.be/gla830WPBVU\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Omar, Cyrus</span>, <span class=\"h-author\">Ian Voysey</span>, <span class=\"h-author\">Ravi Chugh</span> and <span class=\"h-author\">Matthew A. Hammer</span>. <time class=\"dt-published\">2019</time>. \u201c<cite class=\"h-name\">Live functional programming with typed holes</cite>\u201d. In Proceedings of the ACM on Programming Languages 3 (POPL): 1\u201332. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/3290327\" class=\"u-url\">https://doi.org/10.1145/3290327</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Pestov, Sviatoslav</span>, <span class=\"h-author\">Daniel Ehrenberg</span> and <span class=\"h-author\">Joe Groff</span>. <time class=\"dt-published\">2010</time>. \u201c<cite class=\"h-name\">Factor: a dynamic stack-based programming language</cite>\u201d. In ACM SIGPLAN Notices 45 (12): 43\u201358. Association for Computing Machinery (ACM). <a href=\"https://doi.org/10.1145/1899661.1869637\" class=\"u-url\">https://doi.org/10.1145/1899661.1869637</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">von Thun, Manfred</span>. n.d. \u201c<cite class=\"h-name\">Mathematical foundations of Joy</cite>\u201d. Archived: <a href=\"https://web.archive.org/web/20091227112540/http://www.latrobe.edu.au/philosophy/phimvt/joy/j02maf.html\" class=\"u-url\">Online</a>.</span></li>\n<li><span class=\"h-cite\"><span class=\"h-author h-card\">Tillmann, Nikolai</span>, <span class=\"h-author\">Michal Moskal</span>, <span class=\"h-author\">Jonathan de Halleux</span> and <span class=\"h-author\">Manuel Fahndrich</span>. <time class=\"dt-published\">2011</time>. \u201c<cite class=\"h-name\">TouchDevelop: programming cloud-connected mobile devices via touchscreen</cite>\u201d. In Proceedings of the 10th SIGPLAN symposium on New ideas, new paradigms, and reflections on programming and software (SPLASH '11): 49\u201360. ACM, New York, NY, USA. <a href=\"https://doi.org/10.1145/2048237.2048245\" class=\"u-url\">https://doi.org/10.1145/2048237.2048245</a>.</span></li>\n                    </ul>\n                </section>\n", "attachments": [{"url": "https://michael.homer.nz/notes/compositional-2d/compositional-2d.html", "mime_type": "text/html", "title": "Single-file archive of article: Live 2D Compositional Programming", "size_in_bytes": 2910512}]}]}