<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>steen&#x27;s burrow</title>
      <link>https://sgt.hootr.club</link>
      <description>steen&#x27;s online burrow</description>
      <generator>Zola</generator>
      <language>en-us</language>
      <atom:link href="https://sgt.hootr.club/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sun, 01 Feb 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Enumerate your logs</title>
          <pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/enumerate-your-logs/</link>
          <guid>https://sgt.hootr.club/blog/enumerate-your-logs/</guid>
          <description xml:base="https://sgt.hootr.club/blog/enumerate-your-logs/">&lt;p&gt;I&#x27;m working on my Soulseek client &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;kirarin.hootr.club&#x2F;git&#x2F;steinuil&#x2F;snus&quot;&gt;snus&lt;&#x2F;a&gt; (named in honour of &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nicotine-plus.org&#x2F;&quot;&gt;nicotine+&lt;&#x2F;a&gt;), which is a Gleam application targeting the BEAM and an attempt to learn some things about OTP. I&#x27;ve also been working on some new projects at work that use structured logging and &quot;audit logging&quot; using structured fields, and I like the pattern! I&#x27;d like snus to have structured logs as well. How do I do it?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-crash-introduction-to-gleam&quot;&gt;A crash introduction to Gleam&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gleam.run&#x2F;&quot;&gt;Gleam&lt;&#x2F;a&gt; is a very nice programming language with a mauve pink smiling star as a mascot. I like it a lot! I think Gleam is best described as a functional-first ML-like (as in OCaml and SML) language with a Rust-like syntax. In as few words as I can muster:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It&#x27;s expression-based and has first-class functions, generics, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;tour.gleam.run&#x2F;everything&#x2F;#data-types-custom-types&quot;&gt;variants&lt;&#x2F;a&gt;, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;tour.gleam.run&#x2F;everything&#x2F;#advanced-features-opaque-types&quot;&gt;opaque types&lt;&#x2F;a&gt;, pattern matching, tuples, and tail call optimization.&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s &quot;strongly&quot; typed, by which I mean that every expression has a type and the only &quot;escape hatch&quot; is foreign function calls AKA &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;tour.gleam.run&#x2F;everything&#x2F;#advanced-features-externals&quot;&gt;externals&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;In terms of syntactic conveniences it has a &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;tour.gleam.run&#x2F;everything&#x2F;#functions-pipelines&quot;&gt;pipe operator&lt;&#x2F;a&gt;, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;tour.gleam.run&#x2F;everything&#x2F;#functions-labelled-arguments&quot;&gt;labelled arguments&lt;&#x2F;a&gt;, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;tour.gleam.run&#x2F;everything&#x2F;#data-types-record-accessors&quot;&gt;flow-based record accessors&lt;&#x2F;a&gt; (which sacrifice global type inference for some great syntactical convenience), a very nifty &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;tour.gleam.run&#x2F;everything&#x2F;#advanced-features-use&quot;&gt;&lt;code&gt;use&lt;&#x2F;code&gt; keyword&lt;&#x2F;a&gt; that can act as a &lt;code&gt;do&lt;&#x2F;code&gt; operator and much more, and &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;tour.gleam.run&#x2F;everything&#x2F;#data-types-bit-arrays&quot;&gt;binary pattern matching&lt;&#x2F;a&gt; borrowed from Erlang.&lt;&#x2F;li&gt;
&lt;li&gt;It compiles to both Erlang and JavaScript (and has a nice &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;lustre&#x2F;index.html&quot;&gt;Elm-like framework for single page apps&lt;&#x2F;a&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;It uses a &lt;code&gt;Result(t, err)&lt;&#x2F;code&gt; type to handle errors and it prefers &lt;code&gt;Error(Nil)&lt;&#x2F;code&gt; to &lt;code&gt;option.None&lt;&#x2F;code&gt; in cases where the failure mode is obvious, which encourages a separation of operational failure from a semantical lack of a value.&lt;&#x2F;li&gt;
&lt;li&gt;It uses &lt;code&gt;+&lt;&#x2F;code&gt; for integer addition and &lt;code&gt;+.&lt;&#x2F;code&gt; for float addition like OCaml does, which makes me happy.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;structured-logging&quot;&gt;Structured logging&lt;&#x2F;h2&gt;
&lt;p&gt;Go has a &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;log&#x2F;slog&quot;&gt;structured logging library&lt;&#x2F;a&gt; whose functions take an unstructured message, and then pairs of &lt;code&gt;(string, any)&lt;&#x2F;code&gt; as varargs. I don&#x27;t remember which library my workplace uses for structured logging in Python, but I&#x27;m sure it has a similar structured.&lt;&#x2F;p&gt;
&lt;p&gt;Gleam does not have a dictionary&#x2F;hash(map)&#x2F;object literal: it handles dicts just like &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;package.elm-lang.org&#x2F;packages&#x2F;elm&#x2F;core&#x2F;latest&#x2F;Dict&quot;&gt;Elm&lt;&#x2F;a&gt;. You can make a dict from a &lt;code&gt;List&lt;&#x2F;code&gt; of &lt;code&gt;#(key, value)&lt;&#x2F;code&gt; if you want, so if you wanted to reproduce &lt;code&gt;slog&lt;&#x2F;code&gt; in Gleam you&#x27;d have a &lt;code&gt;print(level: LogLevel, data: List(#(String, String)))&lt;&#x2F;code&gt; function. There&#x27;s a &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;glogg&#x2F;index.html&quot;&gt;few&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;flash&#x2F;index.html&quot;&gt;structured&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;glight&#x2F;index.html&quot;&gt;logging&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;gclog&#x2F;index.html&quot;&gt;libraries&lt;&#x2F;a&gt; on the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;packages.gleam.run&#x2F;&quot;&gt;Gleam package index&lt;&#x2F;a&gt; that fit the bill, but I didn&#x27;t like any of them and I was wondering how I could reconsider that approach.&lt;&#x2F;p&gt;
&lt;p&gt;Gleam doesn&#x27;t have a built-in syntax for booleans; &lt;code&gt;Bool&lt;&#x2F;code&gt; is a &quot;custom type&quot; like any other that could just as easily be defined in a library:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; Bool {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  False&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Booleans could just as well be reprenented as the strings &lt;code&gt;&quot;true&quot;&lt;&#x2F;code&gt; and &lt;code&gt;&quot;false&quot;&lt;&#x2F;code&gt;, but languages like Gleam and OCaml and Rust and C# and Java and Go choose to represent booleans as their own type. How many states could a &lt;code&gt;String&lt;&#x2F;code&gt; have compared to a &lt;code&gt;Bool&lt;&#x2F;code&gt;? Would you say, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=EWKB86iSlFo&quot;&gt;ten million&lt;&#x2F;a&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;Now think about your application&#x27;s logs. How many states that are meaningful enough to be logged does your service have? Can you enumerate all of them? Can you gracefully handle the failures? Does the user need to know about it? Is it useful for debugging? Will the log message have to be localized at some point?&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;glight&quot;&gt;several&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;glogg&quot;&gt;structured&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;flash&quot;&gt;logging&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;birch&quot;&gt;libraries&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;viva_telemetry&#x2F;index.html&quot;&gt;available&lt;&#x2F;a&gt; in the Gleam package registry. Gleam is a new language whose library ecosystem is still growing, none of these libraries seem to be leading the pack, so I&#x27;m not sure I should be committing to any of them. Instead I added a &lt;code&gt;logging&lt;&#x2F;code&gt; module containing a &lt;code&gt;Log&lt;&#x2F;code&gt; variant that contains all the types of things I want to be logging in the application, along with some data that I think will be useful to debug any problems. This will let me easily switch logging implementation in the future, but I think it&#x27;s also a good pattern to keep in my toolbox.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;pub type&lt;&#x2F;span&gt;&lt;span&gt; Log {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  PeerAlreadyConnected(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    conn_type: ConnectionType,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ip: IP,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    port: Int,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  PeerViolatedConnectionOrder(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    username: String,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message: String,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;  &#x2F;&#x2F; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Enumerating special cases in a program is a way to give them semantical meaning. I think logs can be as deserving of care and thought as much as the important part of our applications: maybe just the act of adding the failure case can give you a chance to think about it more carefully. Consider enumerating your logs.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building smaller Docker images faster</title>
          <pubDate>Fri, 12 Dec 2025 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/docker-protips/</link>
          <guid>https://sgt.hootr.club/blog/docker-protips/</guid>
          <description xml:base="https://sgt.hootr.club/blog/docker-protips/">&lt;p&gt;I&#x27;ve been tasked (more or less) with building the first Go service at &lt;code&gt;$DAYJOB&lt;&#x2F;code&gt;, which is almost exclusively a Python shop. Why Go of all languages? Well, some of my coworkers are big fans of the gopher, it&#x27;s an easy language, it&#x27;s attached to one of the big companies, and it&#x27;s much faster than Python, so I feel more comfortable pushing for this rather than Rust or (sadly) Nix.&lt;&#x2F;p&gt;
&lt;p&gt;The project that recently fell onto my lap is basically RCE-as-a-service, and it just so happens that &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;starlark-lang.org&#x2F;&quot;&gt;one of the few languages&lt;&#x2F;a&gt; that I would feel comfortable letting users execute remotely on our servers has &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;starlark-go&quot;&gt;a Go implementation&lt;&#x2F;a&gt;, which is as good an excuse as any to take a break from the usual snekslop.&lt;&#x2F;p&gt;
&lt;p&gt;I still haven&#x27;t convinced anybody here to get on the Nix train, so after a short stint of building the project and the OCI images with a recursive cycle of Make and Nix, I breathed a heavy sigh and dropped it for Docker and Docker Compose, which is what we generally use here.&lt;&#x2F;p&gt;
&lt;aside class=&quot;note&quot;&gt;
  &lt;p&gt;And just between you and me, I don&#x27;t think we use them very well. CI is painfully slow and we all just kinda live with it because figuring out how the damn &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; works sucks even more.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;Which is a shame, because Nix is pretty good at building OCI images. This is all the code you need to create a minimal image that contains only your service and nothing else, not even &lt;code&gt;&#x2F;bin&#x2F;sh&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt; pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &amp;lt;nixpkgs&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {} }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;dockerTools&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;streamLayeredImage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;someimage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  tag&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;latest&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Cmd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;hello&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;bin&#x2F;hello&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can build that with &lt;code&gt;nix-build docker-test.nix&lt;&#x2F;code&gt; and inside &lt;code&gt;.&#x2F;result&lt;&#x2F;code&gt; you&#x27;ll find a script that generates the image&#x27;s tarball. Load it up with &lt;code&gt;.&#x2F;result | docker load&lt;&#x2F;code&gt; and Docker will report a new image called &lt;code&gt;someimage:latest&lt;&#x2F;code&gt; when you run &lt;code&gt;docker image ls&lt;&#x2F;code&gt;. It&#x27;s only 45.8MB, and most of that is taken up by glibc.&lt;&#x2F;p&gt;
&lt;p&gt;But what if I told you that you can get more or less the same result with a simple &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; if you know what you&#x27;re doing? Especially if you&#x27;re building a static executable, which Go famously does (at least when you set &lt;code&gt;CGO_ENABLED=0&lt;&#x2F;code&gt;) and if you&#x27;re willing to sacrifice a few affordances. Who needs coreutils anyway?&lt;&#x2F;p&gt;
&lt;p&gt;In this post I&#x27;ll show you a few tricks I found while striving to make small and fast-building images. I&#x27;m not an expert in Docker by any stretch so maybe you know all of this stuff already, but perhaps you might learn something new.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-barebones-image&quot;&gt;A barebones image&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m using &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pressly&#x2F;goose&quot;&gt;&lt;code&gt;goose&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; for database migrations. I configured the &lt;code&gt;docker-compose.yml&lt;&#x2F;code&gt; to run its container just after the database has started and before the actual service runs, and since it&#x27;s a very small self-contained tool I figured I could try to make the image as small and quick to build as possible. Let me show you the relevant part of the &lt;code&gt;docker-compose.yml&lt;&#x2F;code&gt; first:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  migrate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    image&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; migrate:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    pull_policy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    build&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      context&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; https:&#x2F;&#x2F;github.com&#x2F;pressly&#x2F;goose.git#v3.26.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      dockerfile&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; $PWD&#x2F;Dockerfile.migrate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    environment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      GOOSE_DBSTRING&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; postgresql:&#x2F;&#x2F;AzureDiamond:hunter2@db:5432&#x2F;bobby&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      GOOSE_MIGRATION_DIR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &#x2F;migrations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      GOOSE_DRIVER&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; postgres&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    depends_on&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      db&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        condition&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; service_started&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    volumes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; .&#x2F;migrations:&#x2F;migrations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;concepts&#x2F;context&#x2F;&quot;&gt;The &lt;code&gt;build.context&lt;&#x2F;code&gt; parameter&lt;&#x2F;a&gt; specifies the &quot;set of files&quot; that are available from the host while building a Docker image. It&#x27;s the parameter that you pass after &lt;code&gt;docker build&lt;&#x2F;code&gt;, generally &lt;code&gt;.&lt;&#x2F;code&gt; (the PWD). Here I specified a GitHub URL with a tag, so when I&#x27;m referring to &lt;code&gt;.&lt;&#x2F;code&gt; in the &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; above I get the root of the &lt;code&gt;goose&lt;&#x2F;code&gt; project on that tag&#x27;s commit. I didn&#x27;t know Docker could do that!&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;build.dockerfile&lt;&#x2F;code&gt; path includes &lt;code&gt;$PWD&lt;&#x2F;code&gt; because even if Docker itself can refer to Dockerfiles outside of its context, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;docker&#x2F;compose&#x2F;issues&#x2F;4926&quot;&gt;Docker Compose apparently can&#x27;t&lt;&#x2F;a&gt; unless you specify it as an absolute path. I think this will break if you try to run &lt;code&gt;docker compose&lt;&#x2F;code&gt; from another directory, but it&#x27;s good enough for now.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s take a look at the &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; itself:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;docker&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; golang:1.25-alpine3.23 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; builder&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;WORKDIR&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; CGO_ENABLED=0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; GOCACHE=&#x2F;root&#x2F;.cache&#x2F;go-build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; GOMODCACHE=&#x2F;root&#x2F;.cache&#x2F;go-mod&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span&gt; --mount=type=cache,target=&#x2F;root&#x2F;.cache&#x2F;go-build \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=cache,target=&#x2F;root&#x2F;.cache&#x2F;go-mod \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=bind,source=.,target=&#x2F;build \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  go build -tags=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;#39;no_clickhouse no_libsql no_sqlite3 no_mssql no_vertica no_mysql no_ydb&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; -o &#x2F;goose .&#x2F;cmd&#x2F;goose&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; scratch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; --from=builder &#x2F;goose &#x2F;goose&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;CMD&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;&#x2F;goose&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;up&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few things to note:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I used the Alpine image for Go because it&#x27;s the smallest and it includes everything I need to build the tool.&lt;&#x2F;li&gt;
&lt;li&gt;I set &lt;code&gt;CGO_ENABLED=0&lt;&#x2F;code&gt; to ensure that the executable only builds with the Go toolchain and does not link to libc. According to &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;cmd&#x2F;cgo&quot;&gt;the docs&lt;&#x2F;a&gt;, &lt;em&gt;The cgo tool is enabled by default for native builds on systems where it is expected to work&lt;&#x2F;em&gt; so you have to take extra care to disable it if you don&#x27;t want or need it.&lt;&#x2F;li&gt;
&lt;li&gt;I set &lt;code&gt;GOCACHE&lt;&#x2F;code&gt; and &lt;code&gt;GOMODCACHE&lt;&#x2F;code&gt; (&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;cmd&#x2F;go#hdr-Environment_variables&quot;&gt;docs&lt;&#x2F;a&gt;) to a known location to ensure that I can take advantage of &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;cache&#x2F;optimize&#x2F;#use-cache-mounts&quot;&gt;cache mounts&lt;&#x2F;a&gt; on subsequent rebuilds. Admittedly this is not very useful for an external tool that I&#x27;m only expecting to build once but hey, every little bit helps.&lt;&#x2F;li&gt;
&lt;li&gt;Instead of &lt;code&gt;ADD&lt;&#x2F;code&gt;ing or &lt;code&gt;COPY&lt;&#x2F;code&gt;ing the package&#x27;s source I &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;cache&#x2F;optimize&#x2F;#use-bind-mounts&quot;&gt;bind mounted&lt;&#x2F;a&gt; it to the workdir. This should make it a bit faster because it avoids an extra copy into the build container.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;FROM scratch&lt;&#x2F;code&gt; defines a &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;building&#x2F;multi-stage&#x2F;&quot;&gt;second build stage&lt;&#x2F;a&gt; that ensures any artifacts from the actual build are discarded. &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;_&#x2F;scratch&#x2F;&quot;&gt;&lt;code&gt;scratch&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is the null image.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The result is an image with one layer containing one file that builds, loads and boots extremely fast and is only &lt;small&gt;&lt;em&gt;*chef&#x27;s kiss*&lt;&#x2F;em&gt;&lt;&#x2F;small&gt; 15.9MB in size. Not too bad!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;docker-protips&#x2F;smol-image.png&quot; alt=&quot;Screenshot of dive showing the resulting image&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-build-context&quot;&gt;The build context&lt;&#x2F;h2&gt;
&lt;p&gt;Earlier I mentioned the build context. &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;cache&#x2F;optimize&#x2F;#keep-the-context-small&quot;&gt;Keeping it small&lt;&#x2F;a&gt; is important because &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;concepts&#x2F;context&#x2F;#dockerignore-files&quot;&gt;Docker copies all files available in the build context to the builder&lt;&#x2F;a&gt; every time you run a &lt;code&gt;docker build&lt;&#x2F;code&gt;, so if you have lots of files in your repo that you don&#x27;t need to build the image you&#x27;ll probably want to exclude them. The way you do that is by keeping a &lt;code&gt;.dockerignore&lt;&#x2F;code&gt; file alongside your &lt;code&gt;Dockerfile&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I have to stress one point: the build context includes &lt;strong&gt;everything&lt;&#x2F;strong&gt; inside the directory you run &lt;code&gt;docker build&lt;&#x2F;code&gt; from unless it&#x27;s listed in &lt;code&gt;.dockerignore&lt;&#x2F;code&gt;. I thought some obvious things like &lt;code&gt;.git&lt;&#x2F;code&gt; would be excluded by default, but a quick test disproved that:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;docker&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; busybox&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;WORKDIR&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; . .&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;CMD&lt;&#x2F;span&gt;&lt;span&gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Try saving that file as &lt;code&gt;Dockerfile.test&lt;&#x2F;code&gt; in one of your repos, build it with &lt;code&gt;docker build -f Dockerfile.text -t build-context .&lt;&#x2F;code&gt;, open a shell with &lt;code&gt;docker run --rm -it build-context &#x2F;bin&#x2F;sh&lt;&#x2F;code&gt; and run &lt;code&gt;find&lt;&#x2F;code&gt;. Everything&#x27;s in there: &lt;code&gt;.git&lt;&#x2F;code&gt;, &lt;code&gt;.jj&lt;&#x2F;code&gt;, the &lt;code&gt;Dockerfile.test&lt;&#x2F;code&gt; itself, and all the rest of the build artifacts and assorted junk you have accumulated in your project directory. Ignoring them won&#x27;t make your images smaller, but it might make the build quicker.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# .dockerignore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Dockerfile*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;docker-compose.yml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;granular-layers&quot;&gt;Granular layers&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;cache&#x2F;optimize&#x2F;#order-your-layers&quot;&gt;Splitting and ordering layers&lt;&#x2F;a&gt; is probably the most well-known and obvious Docker build time optimization there is, but it doesn&#x27;t hurt to mention. This is the &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; for the service I&#x27;m building:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;docker&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; golang:1.25-alpine3.23 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; builder&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;WORKDIR&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; CGO_ENABLED=0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; GOCACHE=&#x2F;root&#x2F;.cache&#x2F;go-build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ARG&lt;&#x2F;span&gt;&lt;span&gt; GOMODCACHE=&#x2F;root&#x2F;.cache&#x2F;go-mod&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span&gt; --mount=type=cache,target=&#x2F;root&#x2F;.cache&#x2F;go-build \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=cache,target=&#x2F;root&#x2F;.cache&#x2F;go-mod \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  go install github.com&#x2F;DataDog&#x2F;orchestrion@latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span&gt; --mount=type=bind,source=go.mod,target=go.mod \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=bind,source=go.sum,target=go.sum \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=cache,target=&#x2F;root&#x2F;.cache&#x2F;go-build \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=cache,target=&#x2F;root&#x2F;.cache&#x2F;go-mod \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  go mod download&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span&gt; --mount=type=bind,source=go.mod,target=go.mod \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=bind,source=go.sum,target=go.sum \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=cache,target=&#x2F;root&#x2F;.cache&#x2F;go-build \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=cache,target=&#x2F;root&#x2F;.cache&#x2F;go-mod \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=bind,source=internal,target=internal \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=bind,source=cmd,target=cmd \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --mount=type=bind,source=orchestrion.tool.go,target=orchestrion.tool.go \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  orchestrion go build -o server .&#x2F;cmd&#x2F;server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; alpine:3.23 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; prod&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;WORKDIR&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;app&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; --from=builder &#x2F;build&#x2F;server .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;ENTRYPOINT&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot;&#x2F;app&#x2F;server&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can see all the bind and cache mount tricks from earlier, not much has changed. I&#x27;m installing &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DataDog&#x2F;orchestrion&quot;&gt;&lt;code&gt;orchestrion&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; (a great tool to bloat up your binary size if you ever feel like it) early because that&#x27;s the least likely to change. After that I bind mount &lt;code&gt;go.mod&lt;&#x2F;code&gt; and &lt;code&gt;go.sum&lt;&#x2F;code&gt; and only download the dependencies, because that&#x27;s the step that generally takes the most time and dependencies change less often than code. Only at the end do I bind mount the package directories and build the server.&lt;&#x2F;p&gt;
&lt;aside class=&quot;note&quot;&gt;
  &lt;p&gt;I opted for good ol&#x27; Alpine for the base final image. It only adds a couple more MBs and I&#x27;m sure whoever will eventually have to shell into a prod container will appreciate having something to work with.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;You can iterate on this by changing one of the relevant files and ensuring that all the previous steps are marked as &lt;code&gt;CACHED&lt;&#x2F;code&gt; on the next &lt;code&gt;docker build&lt;&#x2F;code&gt;. Again, this won&#x27;t save you space but it&#x27;ll save you a lot of time while iterating.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;&#x2F;h2&gt;
&lt;p&gt;Summing up:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Read the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;cache&#x2F;optimize&#x2F;&quot;&gt;Optimize cache usage in builds&lt;&#x2F;a&gt; page in the Docker documentation, and maybe take a look at the rest while you&#x27;re there.&lt;&#x2F;li&gt;
&lt;li&gt;Split, merge and order layers according to how often you expect them to be rebuilt. Directives like &lt;code&gt;ENV&lt;&#x2F;code&gt;, &lt;code&gt;ARG&lt;&#x2F;code&gt; and &lt;code&gt;WORKDIR&lt;&#x2F;code&gt; should go before everything else.&lt;&#x2F;li&gt;
&lt;li&gt;Use &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;building&#x2F;multi-stage&#x2F;&quot;&gt;multi-stage builds&lt;&#x2F;a&gt; to shrink the final image size. Consider if you can get away with using &lt;code&gt;scratch&lt;&#x2F;code&gt;, &lt;code&gt;busybox&lt;&#x2F;code&gt; or &lt;code&gt;alpine&lt;&#x2F;code&gt; for the final image.&lt;&#x2F;li&gt;
&lt;li&gt;If your project contains multiple separate applications (for example a backend and a frontend) you can create separate build stages for each of them, and then copy the results into a final image. Stages that don&#x27;t depend on each other &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;buildkit&#x2F;&quot;&gt;are built in parallel if you&#x27;re using BuildKit&lt;&#x2F;a&gt;, and you can do &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@tonistiigi&#x2F;advanced-multi-stage-build-patterns-6f741b852fae&quot;&gt;all sorts of tricks&lt;&#x2F;a&gt; with multi-stage builds to ensure your CPU runs hot all the time.&lt;&#x2F;li&gt;
&lt;li&gt;Add a &lt;code&gt;.dockerignore&lt;&#x2F;code&gt; to your project, and make sure to put &lt;code&gt;.git&lt;&#x2F;code&gt; in there if you don&#x27;t need it.&lt;&#x2F;li&gt;
&lt;li&gt;Try to set up cache mounts to make sure dependencies and intermediate build artifacts are persisted across builds. Some CI services (GitHub actions, apparently) even let you set up &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;cache&#x2F;optimize&#x2F;#use-an-external-cache&quot;&gt;external caches&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Try to use bind mounts instead of &lt;code&gt;COPY&lt;&#x2F;code&gt;ing source files into the builder. Bind mounts are usually read-only, but you can make them read-write if you really need to.&lt;&#x2F;li&gt;
&lt;li&gt;I didn&#x27;t use it in my &lt;code&gt;Dockerfile&lt;&#x2F;code&gt;s, but apparently you should be using &lt;code&gt;ADD&lt;&#x2F;code&gt; for downloading files, archives or Git repos during your build. &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;building&#x2F;best-practices&#x2F;#add-or-copy&quot;&gt;The docs&lt;&#x2F;a&gt; mention that &lt;code&gt;COPY&lt;&#x2F;code&gt; should mostly be used for copying files between stages or for files you need in your final image, and for the rest you should try to use bind mounts.&lt;&#x2F;li&gt;
&lt;li&gt;Use smaller base images when possible and if you &lt;em&gt;really&lt;&#x2F;em&gt; have to install stuff with &lt;code&gt;apt&lt;&#x2F;code&gt; you should make an intermediate image and upload it to your container repository of choice so your poor runners don&#x27;t have to hammer Debian&#x27;s mirrors every single time you push.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;One last trick before the post is over: I just discovered that Docker Compose &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;compose&#x2F;how-tos&#x2F;file-watch&#x2F;&quot;&gt;has a watch mode&lt;&#x2F;a&gt; that tracks changes in the build context and rebuilds (or does other things with) your image on every change. Nice!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Hacking on the reMarkable 2</title>
          <pubDate>Sun, 30 Nov 2025 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/hacking-on-the-remarkable-2/</link>
          <guid>https://sgt.hootr.club/blog/hacking-on-the-remarkable-2/</guid>
          <description xml:base="https://sgt.hootr.club/blog/hacking-on-the-remarkable-2/">&lt;p&gt;Since last Wednesday, I am the proud owner of a &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;remarkable.com&#x2F;products&#x2F;remarkable-2&quot;&gt;reMarkable 2&lt;&#x2F;a&gt;. There was a black friday discount and some refurbished deals that meant I could get the tablet, the Marker Plus and the book cover for a little over the asking price of the base tablet and marker so I decided to take advantage of it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-short-review&quot;&gt;A short review&lt;&#x2F;h2&gt;
&lt;p&gt;The reMarkable is marketed as a distraction-free reading and writing device, and I can confirm that it excels at writing. The friction of the stylus on the epaper display feels just right, the latency is low enough that it resembles writing with ink that takes an instant to set on the page, and you can customize the brush enough to achieve whatever effect you want. I haven&#x27;t written this much since I was in high school and I think I kind of missed it.&lt;&#x2F;p&gt;
&lt;p&gt;The Marker Plus includes an eraser on the other end of the stylus; if you&#x27;re thinking of getting one of these things I&#x27;d recommend spending a bit more to get this version of the marker because I can imagine erasing things to get very annoying. The other options you have for erasing are a two-tap gesture to undo the last stroke (which I use a lot to delete the last letter if it didn&#x27;t turn out right), and a selection eraser tool which is useful for deleting entire paragraphs.&lt;&#x2F;p&gt;
&lt;p&gt;The touch gestures are not great: they don&#x27;t always register and they always need a bit more pressure than you might expect. Navigating with the stylus is much more pleasant, but some things (turning pages) you can only do by swiping. The reMarkable 1 used to have three physical buttons below the screen, and I wish the second iteration had kept those.&lt;&#x2F;p&gt;
&lt;p&gt;Reading PDFs is serviceable (papers are kind of annoying to read because you have to zoom in just a little to read comfortably) and I haven&#x27;t tested ebooks yet.&lt;&#x2F;p&gt;
&lt;p&gt;Obviously the reMarkable has &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;support.remarkable.com&#x2F;s&#x2F;article&#x2F;About-Connect-Subscription&quot;&gt;its own subscription service&lt;&#x2F;a&gt; called Connect that unlocks some useful features like unlimited cloud storage (the free plan seems to delete files from the cloud after 50 days of inactivity), editing documents in the companion desktop and mobile apps, &quot;premium templates&quot;, screen sharing, an extended warranty, and a few other things.&lt;&#x2F;p&gt;
&lt;p&gt;At $29.90 a year honestly it&#x27;s not such a bad deal, and even the free plan&#x27;s limitations are not too bad. The signup procedure for my.reMarkable deceptively funnels you into signing up for a 50 day trial and I couldn&#x27;t find a comprehensive page that shows what you get without paying, but if you don&#x27;t give up your payment info you&#x27;ll find that you can still see your cloud synced files and enable the Google Drive&#x2F;Dropbox&#x2F;OneDrive integration.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reading-sheet-music&quot;&gt;Reading sheet music&lt;&#x2F;h2&gt;
&lt;p&gt;One of the reasons why I wanted to get an epaper tablet was to read sheet music. I sing in a choir and we have a concert coming up during which we won&#x27;t be allowed to use regular tablets due to the backlighting, and it felt like the perfect excuse to get one of these things to play with.&lt;&#x2F;p&gt;
&lt;p&gt;Reading sheet music is not great on the reMarkable. The refresh rate while turning pages is serviceable, and annotating PDFs with the stylus is a great experience, but there&#x27;s a few features I have gotten used to on MobileSheets on my tablet that the reMarkable does not support:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Cropping pages individually. The document viewer lets you zoom in, but it&#x27;s imprecise and it gets applied to the whole document, and I don&#x27;t think the zoom level and offset is saved when switching to another document.&lt;&#x2F;li&gt;
&lt;li&gt;Half-page turning. When you turn a page in MobileSheets, you can set it to overlay half of the next page on the current one so you can follow the last bar while checking what&#x27;s coming up next.&lt;&#x2F;li&gt;
&lt;li&gt;Turning pages with a single touch. I mentioned that I don&#x27;t really like the swipe gestures and I&#x27;m not looking forward to fighting gesture recognition while trying to turn a page during a performance.&lt;&#x2F;li&gt;
&lt;li&gt;Library management. The reMarkable only displays a PDF&#x27;s filename, but I like to keep my scores indexed by both name and composer&#x2F;arranger and I&#x27;d much rather use metadata rather than a naming scheme to manage them.&lt;&#x2F;li&gt;
&lt;li&gt;Setlists. During a performance you don&#x27;t want to be searching for the next piece and most pieces are going to be shared between many concerts. While you can organize documents by folder, duplicating a score in many different folders is not ideal.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These features are non-negotiable for a good music sheet reader and the reMarkable&#x27;s stock software is not up to par. When I bought the reMarkable 2 I figured I could &lt;em&gt;just&lt;&#x2F;em&gt; write my own PDF reader and manager for it and work these features into it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;running-homebrew-software&quot;&gt;Running homebrew software&lt;&#x2F;h2&gt;
&lt;p&gt;The reMarkable is a computer like any other, so I believe that since I bought it, I own it, and I should be able to run whatever software I want on it. On this front I have to give credit where it&#x27;s due. reMarkable the company could&#x27;ve easily locked the device down to get you to pay for its subscription and buy new iterations of the device when they end support for the old ones, but if you go into the &lt;strong&gt;Settings &amp;gt; Help &amp;gt; Copyright and licenses&lt;&#x2F;strong&gt; menu you&#x27;ll find a paragraph explaining that to comply with the GPLv3 the end user should be able to modify the software that&#x27;s running on the device, and below that a short explaination on how to SSH into the reMarkable as &lt;code&gt;root&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At first glance the reMarkable has an active homebrew software ecosystem too. There&#x27;s &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;remarkable.guide&#x2F;&quot;&gt;a community-maintained wiki&lt;&#x2F;a&gt; with guides on &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;remarkable.guide&#x2F;guide&#x2F;software&#x2F;index.html&quot;&gt;how to install toltec (a package manager)&lt;&#x2F;a&gt; and &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;remarkable.guide&#x2F;devel&#x2F;index.html&quot;&gt;developing software&lt;&#x2F;a&gt; for the device. After I unpacked the tablet I was eager to get Toltec running and install some software, but...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;hacking-on-the-remarkable-2&#x2F;toltec-versions.png&quot; alt=&quot;Toltec only supports OS builds between 2.6.1.71 and 3.3.2.1666&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Toltec has &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;toltec-dev.org&#x2F;#install-toltec&quot;&gt;strict bounds&lt;&#x2F;a&gt; on which versions of the reMarkable OS it supports. After unboxing and setting up my reMarkable 2 I ended up on version 3.23.0.64, which is way beyond what Toltec supports. Am I cooked? Well, the guide mentions that you &lt;em&gt;can&lt;&#x2F;em&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;remarkable.guide&#x2F;faqs.html#can-i-downgrade-to-a-different-os-version&quot;&gt;downgrade to a different OS version&lt;&#x2F;a&gt;, so maybe...&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;h3 id=&quot;caveat-for-downgrading-from-3-11-2-5-or-greater-to-a-version-less-than-3-11-2-5&quot;&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Jayy001&#x2F;codexctl?tab=readme-ov-file#caveat-for-downgrading-from-31125-or-greater-to-a-version-less-than-31125&quot;&gt;Caveat for downgrading from 3.11.2.5 or greater to a version less than 3.11.2.5&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If your reMarkable device is at or above 3.11.2.5 and you want to downgrade to a version below 3.11.2.5, codexctl cannot do this currently. Please refer to &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Jayy001&#x2F;codexctl&#x2F;issues&#x2F;95#issuecomment-2305529048&quot;&gt;#95 (comment)&lt;&#x2F;a&gt; for manual instructions.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Ok, looks like you can do it, but if you want to downgrade that far you&#x27;re looking for trouble. But why do I have to do this, have the maintainers simply lost interest? Let&#x27;s take a look at &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;toltec-dev&#x2F;toltec&#x2F;issues&#x2F;859&quot;&gt;the GitHub issue&lt;&#x2F;a&gt; that tracks newer OS builds support.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;hacking-on-the-remarkable-2&#x2F;rm2fb-support.png&quot; alt=&quot;Due to the current state of rm2fb support in the community. We will not be supporting every OS release after 3.3.2. Instead, we will be adding 3.5.2 and 3.8.2 support. When timower&amp;#39;s rm2fb is updated to support newer OS versions, we will work on only supporting those versions.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Ah. And what exactly is &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ddvk&#x2F;remarkable2-framebuffer&quot;&gt;rm2fb&lt;&#x2F;a&gt; then?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;hacking-on-the-remarkable-2&#x2F;rm2fb-readme.png&quot; alt=&quot;rm2fb can open the framebuffer and draw to it. rm2fb-server exposes a simple API for other processes to draw to the framebuffer using shared mem and message queues. rm2fb-client is a shim that creates a fake framebuffer device for apps to use, allowing rM1 apps to seamlessly draw to the display of the rM2.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Ok. I don&#x27;t understand why you&#x27;d need to use shared memory and message queues to draw to a framebuffer, but maybe the rest of the README will have more clues on that. If you scroll down to the FAQ there&#x27;s a helpful link called &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ddvk&#x2F;remarkable2-framebuffer&#x2F;issues&#x2F;5#issuecomment-718948222&quot;&gt;how does rm2fb work?&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The server process will rely on using functions from either &lt;code&gt;xochitl&lt;&#x2F;code&gt; or &lt;code&gt;remarkable-shutdown&lt;&#x2F;code&gt; but those processes don&#x27;t actually run. We use LD_PRELOAD to take over the process, like &lt;code&gt;LD_PRELOAD=rm2fb.so xochitl&lt;&#x2F;code&gt; and then we use our own main() func but call into the APIs we need to 1) start the SWTCON threads and 2) send updates. If we go server model, this only needs to happen once (instead of each application doing this dance)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;At this point we should talk about &lt;code&gt;xochitl&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;proprietary-software-woes&quot;&gt;Proprietary software woes&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;xochitl&lt;&#x2F;code&gt; is &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;developer.remarkable.com&#x2F;documentation&#x2F;xochitl&quot;&gt;the main application running on the reMarkable&lt;&#x2F;a&gt;, and it is proprietary software. Unfortunately it also makes for the only documentation the homebrew community has for driving the framebuffer that draws to the reMarkable&#x27;s epaper display; the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;developer.remarkable.com&#x2F;documentation&#x2F;sdk&quot;&gt;SDK&lt;&#x2F;a&gt; provided by reMarkable only supports &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;developer.remarkable.com&#x2F;documentation&#x2F;qt_epaper&quot;&gt;Qt Quick&lt;&#x2F;a&gt; so if you don&#x27;t want to use Qt or link to &lt;code&gt;libqsgepaper.so&lt;&#x2F;code&gt; you&#x27;re stuck with reverse engineering &lt;code&gt;xochitl&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The reMarkable 1&#x27;s display driver seems to have been completely reverse engineered, but not the reMarkable 2&#x27;s (except for individual efforts, which I&#x27;ll talk about later).
Most of the homebrew programs for the reMarkable are written to directly draw to the framebuffer, and they expect to interact with the reMarkable 1&#x27;s framebuffer.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;rm2fb&lt;&#x2F;code&gt; comes in as a compatibility layer that allows programs that interact with the rm1&#x27;s framebuffer to run on the rm2. Since the rm2&#x27;s framebuffer driver had not been completely reverse engineered when this was developed, &lt;code&gt;rm2fb&lt;&#x2F;code&gt; hooks into &lt;code&gt;xochitl&lt;&#x2F;code&gt; and uses its code to interact with the framebuffer. This relies on &lt;code&gt;rm2fb&lt;&#x2F;code&gt; knowing the offsets and the signatures of those functions into the &lt;code&gt;xochitl&lt;&#x2F;code&gt; binary, so obviously when the OS build changes and &lt;code&gt;xochitl&lt;&#x2F;code&gt; is upgraded, those offsets and signatures are invalidated and &lt;code&gt;rm2fb&lt;&#x2F;code&gt; will stop working until somebody works those out again.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;freeing-the-remarkable-2-from-xochitl&quot;&gt;Freeing the reMarkable 2 from Xochitl&lt;&#x2F;h2&gt;
&lt;p&gt;At this point I had already loaded up &lt;code&gt;xochitl&lt;&#x2F;code&gt; into Ghidra and I was starting to figure out how the framebuffer is used inside it. I mean, how hard could it be?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;hacking-on-the-remarkable-2&#x2F;xochitl-in-ghidra.png&quot; alt=&quot;xochitl in Ghidra with the ioctl function highlighted&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I mostly searched for calls to &lt;code&gt;ioctl&lt;&#x2F;code&gt; and quickly made some good progress. Then I figured that if this was so easy somebody else with more experience than me must have have already done it, so I did some more searching.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;rm2fb&lt;&#x2F;code&gt;&#x27;s FAQ section also includes another link, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;timower&#x2F;rM2-stuff&#x2F;&quot;&gt;what about implementing an open source SWTCON?&lt;&#x2F;a&gt; which links to a repository containing &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;timower&#x2F;rM2-stuff&#x2F;?tab=readme-ov-file#rm2fb&quot;&gt;a version&lt;&#x2F;a&gt; of &lt;code&gt;rm2fb&lt;&#x2F;code&gt; that works on newer OS build versions, and &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;timower&#x2F;rM2-stuff&#x2F;?tab=readme-ov-file#swtcon&quot;&gt;an initial implementation&lt;&#x2F;a&gt; of a reMarkable 2 display driver that only relies on &lt;code&gt;xochitl&lt;&#x2F;code&gt; for some things. I also joined the reMarkable community Discord server to see if I could glean more info. There were some posts by &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;timower&quot;&gt;timower&lt;&#x2F;a&gt; (the owner of the &lt;code&gt;rm2-stuff&lt;&#x2F;code&gt; repo) talking about the problems they faced while reverse engineering it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;hacking-on-the-remarkable-2&#x2F;discord-generator-thread.png&quot; alt=&quot;Screenshot of the Discord server with comments by timower and okeh explaining the generator thread&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A few years later, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matteodelabre&quot;&gt;Mattéo Delabre&lt;&#x2F;a&gt; shared a link to &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matteodelabre&#x2F;waved&quot;&gt;a repository&lt;&#x2F;a&gt; containing a C++ driver for the reMarkable 2 display that did not rely on on &lt;code&gt;xochitl&lt;&#x2F;code&gt;, though development seems to have stagnated around 2022. Then a few months ago, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jakubvf&quot;&gt;jakubvf&lt;&#x2F;a&gt; shared &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jakubvf&#x2F;dazed&quot;&gt;their own implementation&lt;&#x2F;a&gt; of a rm2 display driver in Zig with support for SDL3.&lt;&#x2F;p&gt;
&lt;p&gt;Neither of these efforts seem to have caught on, but the Toltec maintainers are working on support for newer OS build versions that can safely be downgraded to from 3.23.* so I hope that in the near future I&#x27;ll be able to install Toltec on my device without messing too much with it.&lt;&#x2F;p&gt;
&lt;p&gt;As for me, I started my yak shaving chain to build the music sheet reader by &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;remfab&quot;&gt;reimplementing waved&#x2F;dazed&lt;&#x2F;a&gt; in my poison of choice, Rust. I&#x27;m not going to finish this program in time for the concert but that&#x27;s ok.&lt;&#x2F;p&gt;
&lt;p&gt;This is where I would&#x27;ve liked to include a section on how you can drive the reMarkable 2&#x27;s display with just a few syscalls but I&#x27;m still figuring that out! This post has already turned out to be longer than I thought so I&#x27;ll leave that stuff for a possible future post on how to write software for the reMarkable 2.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, I&#x27;d like to thank the reMarkable hacking community for all their work on reverse engineering and developing software for it, because I would definitely have regretted this purchase if it weren&#x27;t for their efforts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;hacking-on-the-remarkable-2&#x2F;thanks-for-reading.jpg&quot; alt=&quot;Thanks for reading!&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>My Immich setup feat. NixOS</title>
          <pubDate>Fri, 03 Oct 2025 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/immich-setup/</link>
          <guid>https://sgt.hootr.club/blog/immich-setup/</guid>
          <description xml:base="https://sgt.hootr.club/blog/immich-setup/">&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;immich-app&#x2F;immich&#x2F;releases&#x2F;tag&#x2F;v2.0.0&quot;&gt;Immich reached a stable release!&lt;&#x2F;a&gt; I started using it several months ago and I&#x27;ve been having a very pleasant experience with it, so I figured I could write a few lines about it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;immich.app&#x2F;&quot;&gt;Immich&lt;&#x2F;a&gt; is an open source self-hosted replacement for Google Photos and other similar paid services. It has a very polished web UI and mobile apps. This is what it looks like:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;immich-setup&#x2F;ui.png&quot; alt=&quot;A screenshot of my instance of Immich, featuring photos of my keycaps, my newly installed kitchen knife rack, and some pigeons sleeping on a power line&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Basically you slap it on your home server, upload your photos, and you get all the good parts of a photo hosting service without selling your soul and throwing away your privacy! &lt;em&gt;For free!!&lt;&#x2F;em&gt; Free unless you&#x27;re counting hardware and electricity bills that is, but then again, once you have a home server to run Immich you can use it to run &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.navidrome.org&#x2F;&quot;&gt;all&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;transmissionbt.com&#x2F;&quot;&gt;sorts&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;jellyfin.org&#x2F;&quot;&gt;of&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;syncthing.net&#x2F;&quot;&gt;cool&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dani-garcia&#x2F;vaultwarden&quot;&gt;software&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It is licensed under the AGPL and the people behind &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;futo.org&#x2F;&quot;&gt;the company that maintains it&lt;&#x2F;a&gt; seem to have a strong commitment to privacy and open source. Immich uses several (optional) ML-powered features like facial recognition, contextual search, and duplicate detection, all of which run locally. (This is what I like to call &lt;em&gt;&quot;one of the few useful uses of AI.&quot;&lt;&#x2F;em&gt;) As far as I can tell, Immich does not &quot;phone home&quot; or contact the external world on its own.&lt;&#x2F;p&gt;
&lt;p&gt;If you snoop around on the website you&#x27;ll see a &quot;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;buy.immich.app&#x2F;&quot;&gt;Purchase&lt;&#x2F;a&gt;&quot; link, and you&#x27;d be forgiven for thinking that this is a Freemium™ product that paywalls some features. But it&#x27;s not! If you &quot;buy&quot; Immich all you get is a badge showing that you&#x27;re a supporter on the web UI. You&#x27;ll notice that I have the badge in my screenshot: I was so impressed with Immich after using it for a few weeks that I figured I should show my support in some way. For the 2.0.0 release they&#x27;ve also started selling some branded clothes and &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;immich.store&#x2F;en-eur&#x2F;products&#x2F;immich-retro&quot;&gt;an actual demo disk&lt;&#x2F;a&gt; on their store, which I think is just lovely.&lt;&#x2F;p&gt;
&lt;p&gt;(If this sounds a lot like shilling, I can assure you I&#x27;m not related to the Immich project or FUTO in any way. I&#x27;m just a happy user.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-i-m-running-immich&quot;&gt;Why I&#x27;m running Immich&lt;&#x2F;h2&gt;
&lt;p&gt;The short answer is I like self-hosting and open source software and I don&#x27;t like Google. I&#x27;m self-hosting a bunch of other things, some of which I linked above.&lt;&#x2F;p&gt;
&lt;aside class=&quot;note&quot;&gt;
  &lt;p&gt;Actually I&#x27;m not running &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;jellyfin.org&#x2F;&quot;&gt;Jellyfin&lt;&#x2F;a&gt; yet, but I&#x27;d like to do so in the near future. I&#x27;m planning to upgrade the mainboard of my &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;frame.work&#x2F;&quot;&gt;Framework 13&quot; laptop&lt;&#x2F;a&gt; and place the old one in &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;frame.work&#x2F;products&#x2F;cooler-master-mainboard-case&quot;&gt;a case&lt;&#x2F;a&gt; which will go on my home &quot;rack&quot;, and take some of the load off my Raspberry Pi. That poor thing can&#x27;t handle transcoding movies.&lt;&#x2F;p&gt;
&lt;p&gt;Also, my partner wants me to set it up so they can watch the movies by &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;letterboxd.com&#x2F;director&#x2F;nanni-moretti&#x2F;&quot;&gt;Nanni Moretti&lt;&#x2F;a&gt; I have sitting on my home server when they&#x27;re not at my place. But I&#x27;m getting ahead of myself.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;The long answer involves several old hard drives with photos recovered from dead phones and busted cameras, and still-working phones bursting with photos. The latter are my partner&#x27;s and my mother&#x27;s phones. Both of them were often complaining about how they couldn&#x27;t download a file or take another photo because their phones&#x27; storage had run out of space, and I, the most Computer Person of the family, would have to regularly swoop in and save the day by deleting some old files and apps they weren&#x27;t really using. The old hard drives are my own, and I wasn&#x27;t keen on losing those old memories.&lt;&#x2F;p&gt;
&lt;p&gt;All of us had photo library problems, and something had to be done.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-immich-on-nixos&quot;&gt;Setting up Immich on NixOS&lt;&#x2F;h2&gt;
&lt;p&gt;...is really easy. On &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;lobste.rs&#x2F;c&#x2F;ldgtdn&quot;&gt;the Lobste.rs thread&lt;&#x2F;a&gt; of the announcement, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;michael.stapelberg.ch&#x2F;&quot;&gt;Michael stapelberg&lt;&#x2F;a&gt; commented that it&#x27;s as easy as adding this snippet to your NixOS config:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  services&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;immich&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    enable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    host&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;photos.example.ts.net&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;kirarin.hootr.club&#x2F;git&#x2F;steinuil&#x2F;flakes&quot;&gt;My setup&lt;&#x2F;a&gt; is a bit more complicated than that. First of all I dedicated a spare hard drive to it, which is mounted to Immich&#x27;s service directory. The &lt;code&gt;wantedBy&lt;&#x2F;code&gt; rule ensures that Immich can only run after the drive has been mounted.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  systemd&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;mounts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;ext4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      what&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;dev&#x2F;sdb2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      where&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;var&#x2F;lib&#x2F;immich&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      wantedBy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;immich-server.service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Immich has several features that depend on &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.immich.app&#x2F;features&#x2F;ml-hardware-acceleration&quot;&gt;machine learning&lt;&#x2F;a&gt;, which is cool but definitely not something the Raspberry Pi I&#x27;m running it on can handle! Luckily for the poor Pi, you can &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.immich.app&#x2F;guides&#x2F;remote-machine-learning&quot;&gt;run the ML service on another computer&lt;&#x2F;a&gt; and configure Immich to use it, so this is what my actual Immich config looks like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  services&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;immich&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    enable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    host&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;0.0.0.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    openFirewall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    machine-learning&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;enable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = false;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The ML service runs on my much beefier desktop. NixOS&#x27;s Immich module &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;issues&#x2F;436487&quot;&gt;doesn&#x27;t currently support&lt;&#x2F;a&gt; running the ML service separately from Immich, so I had to write my own module for this. You can find the full module &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;kirarin.hootr.club&#x2F;git&#x2F;steinuil&#x2F;flakes&#x2F;src&#x2F;commit&#x2F;ca508fe53af0edd87d0966f900e6d036a616b671&#x2F;modules&#x2F;nixos&#x2F;immich-ml&#x2F;default.nix&quot;&gt;here&lt;&#x2F;a&gt; (which someday I will try to upstream), but just to give you an idea:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;  # module options elided for brevity&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; lib&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mkIf cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;enable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    networking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;firewall&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;allowedTCPPorts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;span&gt; cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;port&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    systemd&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;immich-ml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      after&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;network.target&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      wantedBy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;multi-user.target&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      environment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        IMMICH_HOST&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;host&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        IMMICH_PORT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; toString&lt;&#x2F;span&gt;&lt;span&gt; cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;port&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        MACHINE_LEARNING_CACHE_FOLDER&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;var&#x2F;cache&#x2F;immich-ml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        IMMICH_MACHINE_LEARNING_WORKERS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; toString&lt;&#x2F;span&gt;&lt;span&gt; cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;workers&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        IMMICH_MACHINE_LEARNING_WORKER_TIMEOUT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; toString&lt;&#x2F;span&gt;&lt;span&gt; cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;workerTimeout&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        MPLCONFIGDIR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;var&#x2F;lib&#x2F;immich-ml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      serviceConfig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        ExecStart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;getExe cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;package&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        StateDirectory&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;immich-ml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        CacheDirectory&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;immich-ml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        DynamicUser&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;        # some hardening and nvidia-related options...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;aside class=&quot;note&quot;&gt;
  &lt;p&gt;I never got it to use my GPU for the ML tasks. There is &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;issues&#x2F;418799&quot;&gt;an issue on nixpkgs&lt;&#x2F;a&gt; about this which was closed 2 weeks ago, so I&#x27;ll have to check the next time I update my flake inputs.&lt;&#x2F;p&gt;
&lt;p&gt;And by the way, if you go look at the service definition in my flake you&#x27;ll see that it&#x27;s supposed to be a socket-activated service, but I&#x27;m not sure that it&#x27;s working as it&#x27;s supposed to. Someone who is good at the systemd please help me fix this. &lt;del&gt;my family is dying&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;After this you can go on Immich and navigate to &lt;strong&gt;Administration&lt;&#x2F;strong&gt; &amp;gt; &lt;strong&gt;Settings&lt;&#x2F;strong&gt; &amp;gt; &lt;strong&gt;Machine Learning Settings&lt;&#x2F;strong&gt; to add the URL of your ML worker machine.&lt;&#x2F;p&gt;
&lt;p&gt;I also set up backups. Backups are super important because I&#x27;m not the only user. My partner would be quite sad if I lost their photos due to a busted drive.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m using &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;restic.net&#x2F;&quot;&gt;restic&lt;&#x2F;a&gt; and backing up to &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.backblaze.com&#x2F;cloud-storage&quot;&gt;Backblaze B2&lt;&#x2F;a&gt; and another drive, for safety. In my configuration I&#x27;m using a helper module I wrote that sources the secrets, repository, and password from files set up by &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Mic92&#x2F;sops-nix&quot;&gt;sops-nix&lt;&#x2F;a&gt;, but this is the moral equivalent:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  services&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;restic&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;backups&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;immich&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    paths&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;var&#x2F;lib&#x2F;immich&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    # The environment file should contain:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    # AWS_ACCESS_KEY_ID=&amp;lt;key_id from the B2 bucket&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    # AWS_SECRET_ACCESS_KEY=&amp;lt;key_secret from the B2 bucket&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    environmentFile&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;super&#x2F;secret&#x2F;environment-file&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    passwordFile&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;super&#x2F;secret&#x2F;password-file&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    repository&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;lt;repository&amp;gt;&#x2F;&amp;lt;bucket name&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    timerConfig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      OnCalendar&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;daily&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      Persistent&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, I used a &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;tailscale.com&#x2F;kb&#x2F;1223&#x2F;funnel&quot;&gt;Tailscale Funnel&lt;&#x2F;a&gt; to expose it to the internet, so that my relatives could use it without setting up Tailscale on their devices. I wrote a very simple service to take care of this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  systemd&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;tailscale-funnel-immich&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Tailscale Funnel forwarding for Immich&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    # https:&#x2F;&#x2F;old.reddit.com&#x2F;r&#x2F;Tailscale&#x2F;comments&#x2F;ubk9mo&#x2F;systemd_how_do_get_something_to_run_if_tailscale&#x2F;jia3pwn&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    after&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;sys-subsystem-net-devices-tailscale0.device&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;tailscaled.service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;immich-server.service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    wantedBy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;multi-user.target&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    serviceConfig&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;ExecStart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;getExe config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tailscale&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;package&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; funnel &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${builtins.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;immich&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;port&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And that&#x27;s it! I&#x27;ve only ever had two issues with this setup.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Every once in a while, the Tailscale session on my Pi expires and that makes the funnel inaccessible... I should probably set up some sort of notification for that.&lt;&#x2F;li&gt;
&lt;li&gt;I don&#x27;t run upgrades on the Pi very often, so I ran into a &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.immich.app&#x2F;errors&#x2F;#typeorm-upgrade&quot;&gt;migration issue&lt;&#x2F;a&gt; that only happened if you skipped versions between v1.132.3 and v1.137.0. To fix it I had to pin a version of nixpkgs that packaged an intermediate version of Immich, deploy it on the Pi, wait for it to migrate, and then upgrade again to the latest version.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Overall it&#x27;s been a very low maintenance setup.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-immich&quot;&gt;Using Immich&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m sure you could tell from the introduction of this post, but I think Immich is really good! My partner uploaded all their photos and they&#x27;re very happy with it. Unfortunately at some point my mother caved in to the pressure and subscribed to Google One, and I still haven&#x27;t bothered to talk her out of it.&lt;&#x2F;p&gt;
&lt;p&gt;I have uploaded all of my photos to it, an archive going back to 2014. The web UI makes it easy and quick to scroll and jump around the timeline; it has brought me back to many good memories. I also like the actual &quot;memory&quot; feature which finds photos you took on the same calendar day the previous years and shows them on top of the gallery. I haven&#x27;t used any software to organize my photos since &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Picasa&quot;&gt;Picasa&lt;&#x2F;a&gt; was still a thing. Immich made me remember how nice that can be.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a lot of other things I enjoy so I&#x27;ll quickly go through them in a list:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The reverse geocoding features are fun and useful. I like how I can see all the places I&#x27;ve taken photos in plotted on a map, and it&#x27;s helped me remember holidays and good restaurants.&lt;&#x2F;li&gt;
&lt;li&gt;Maybe this tells you more about me than about Immich, but the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.immich.app&#x2F;features&#x2F;searching&quot;&gt;contextual CLIP search&lt;&#x2F;a&gt; kinda feels like magic. I can just search for &quot;cat&quot; and it&#x27;ll show me all the cat photos I&#x27;ve ever taken! Unbelievable.&lt;&#x2F;li&gt;
&lt;li&gt;Facial recognition requires some manual fixing sometimes, but I like it! It&#x27;s fun to see how my friends used to look like 8 years ago.&lt;&#x2F;li&gt;
&lt;li&gt;You can share albums between different users and also create external links with an expiration date that allow anybody with the link to upload their photos. I used this a bunch of times to share photos with friends I went on vacation with.&lt;&#x2F;li&gt;
&lt;li&gt;This is not exactly a feature of Immich itself, but I like that the team behind Immich shares the &quot;cursed knowledge&quot; they came across while building Immich &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;immich.app&#x2F;cursed-knowledge&quot;&gt;on a page on the website&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In conclusion: I think you should give Immich a try! Don&#x27;t let big corporations use your memories as training data for their torment nexi. Take control of your data. There are alternatives for most of the services you use regularly and they&#x27;re actually really nice. Support those instead of throwing money at a subscription service.&lt;&#x2F;p&gt;
&lt;p&gt;Thank you for reading, have &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;soundcloud.com&#x2F;sozenotsubo&#x2F;kioku&quot;&gt;a song I like&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>About this blog</title>
          <pubDate>Wed, 01 Oct 2025 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/about-this-blog/</link>
          <guid>https://sgt.hootr.club/blog/about-this-blog/</guid>
          <description xml:base="https://sgt.hootr.club/blog/about-this-blog/">&lt;p&gt;I just changed my blog&#x27;s tech stack, so the time has come for me to write the obligatory meta-post about my blog.&lt;&#x2F;p&gt;
&lt;p&gt;I spent thursday night setting up this blog again, throwing out my own &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;racket-lang.org&#x2F;&quot;&gt;Racket&lt;&#x2F;a&gt;-based &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;steinuil.github.io&#x2F;blob&#x2F;master&#x2F;generate.rkt&quot;&gt;static blog generator&lt;&#x2F;a&gt; and replacing it with &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;. I&#x27;m not particularly happy about killing my generator, but it was either choosing an off-the-shelf option or reimplementing a parser for a Markdown-based Racket &lt;code&gt;#lang&lt;&#x2F;code&gt; that let me escape to Racket in a similar way to &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.racket-lang.org&#x2F;pollen&#x2F;index.html&quot;&gt;Pollen&lt;&#x2F;a&gt; and rewriting my (admittedly not very long tail of) previous posts in this new language, and I wasn&#x27;t making much progress on that front.&lt;&#x2F;p&gt;
&lt;p&gt;Why throw out the old stack? Well, I had a few annoyances about how it worked. First of all, the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.racket-lang.org&#x2F;markdown&#x2F;index.html&quot;&gt;Markdown library&lt;&#x2F;a&gt; I was using did not handle Markdown extensions like tables, and there wasn&#x27;t a way I could plug into the parser to implement those or add custom elements such as &lt;em&gt;Cool Bear&#x27;s hot tips&lt;&#x2F;em&gt; from &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;fasterthanli.me&#x2F;&quot;&gt;@fasterthanlime&#x27;s blog&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Then there was the problem of footnotes: the library generates unstable IDs for footnotes, so every time I regenerated the blog every single ID would change, which was annoying and led me to this cycle of generating the blog, then manually &lt;code&gt;git restore&lt;&#x2F;code&gt;ing every page that was not supposed to be touched by the change. It was all a bit laborious. I&#x27;d also like to implement footnotes in the sidebar, and again, the library made it all kind of tedious.&lt;&#x2F;p&gt;
&lt;p&gt;These were not insurmountable problems. I could probably embed the library into the generator and hook into the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.racket-lang.org&#x2F;sxml-intro&#x2F;index.html&quot;&gt;SXML&lt;&#x2F;a&gt; emitter to change the output it produces. I just never felt like doing that. Maybe I just don&#x27;t like working in Racket anymore because it&#x27;s generally untyped and its &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.racket-lang.org&#x2F;ts-guide&#x2F;&quot;&gt;typed variant&lt;&#x2F;a&gt; is excruciatingly slow and often very annoying to work with.&lt;&#x2F;p&gt;
&lt;p&gt;It took me a few hours of work to get Zola running and redesign the blog. I wrote a custom template, changed all the fonts, finally added support for &lt;a href=&quot;&#x2F;tags&#x2F;&quot;&gt;tags&lt;&#x2F;a&gt;, made sure that all the old posts would render correctly, and aliased all the old URLs to the new ones. Zola has a built-in server with live reload that made this process much quicker. It was all very easy.&lt;&#x2F;p&gt;
&lt;p&gt;I went with Zola entirely based on vibes, but I think I like its approach. It tries to generalize some standard things you&#x27;d want in a blog into more powerful concepts: posts become a &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;templates&#x2F;pages-sections&#x2F;&quot;&gt;section with pages&lt;&#x2F;a&gt;, tags become one of many &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;templates&#x2F;taxonomies&#x2F;&quot;&gt;taxonomies&lt;&#x2F;a&gt; that the website can have. Just about every template is customizable. It looks very fast: my whole blog renders in a hundred milliseconds, which sounds about right. I also set up its premade GitHub Actions workflow for deploying the blog to GitHub pages.&lt;&#x2F;p&gt;
&lt;p&gt;I can&#x27;t say I &lt;em&gt;enjoy&lt;&#x2F;em&gt; writing &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;keats.github.io&#x2F;tera&quot;&gt;Tera&lt;&#x2F;a&gt; templates and this was one of the issues that made me procrastinate the switch to an off-the-shelf generator, but honestly, they&#x27;re fine. The one thing I dislike is that they&#x27;re orthogonal to the text they produce, so a few times during the template writing process I ended up with invalid HTML. I&#x27;m not happy about that, but sometimes you just have to compromise.&lt;&#x2F;p&gt;
&lt;p&gt;I took some inspiration from &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;wiki.xxiivv.com&#x2F;site&#x2F;home.html&quot;&gt;Devine Lu Linvega&#x27;s website&lt;&#x2F;a&gt; for the top bar. You can find the fonts that I used on the &lt;a href=&quot;&#x2F;meta&#x2F;&quot;&gt;meta&lt;&#x2F;a&gt; page: I think they look nice.&lt;&#x2F;p&gt;
&lt;p&gt;There are some things that I&#x27;d like to implement in the future:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Footnotes in the sidebar, as I previously mentioned.&lt;&#x2F;li&gt;
&lt;li&gt;Comments? I wouldn&#x27;t want to use a third-party thing, especially not after I read that &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;ryansouthgate.com&#x2F;goodbye-disqus&#x2F;&quot;&gt;Disqus injects tons of ads into your page&lt;&#x2F;a&gt;, so maybe I&#x27;ll hack some custom solution together.&lt;&#x2F;li&gt;
&lt;li&gt;I had this idea that I could publish this blog as an &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;anil.recoil.org&#x2F;notes&#x2F;atproto-for-fun-and-blogging&quot;&gt;AT proto&lt;&#x2F;a&gt; thing just because I think it would be cool, but that probably involves changing the whole stack again. Something to think about for the future.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In conclusion, hats off to Zola for making the migration process very painless, and I hope this improved process will make me more likely to write posts on here.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The masculine urge to reinvent the wheel</title>
          <pubDate>Tue, 30 Sep 2025 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/masculine-libraries/</link>
          <guid>https://sgt.hootr.club/blog/masculine-libraries/</guid>
          <description xml:base="https://sgt.hootr.club/blog/masculine-libraries/">&lt;p&gt;So I&#x27;ve been playing &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.babystepsgame.com&#x2F;&quot;&gt;Baby Steps&lt;&#x2F;a&gt;. It&#x27;s a game about walking up a mountain by putting one foot in front of the other, and in the process it explores masculinity and how hard it is to accept help from others. I find that I have a lot more in common with Nate, the game&#x27;s coveralls-wearing manchild protagonist and player character, than I would like to admit.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m a self-taught programmer. After failing to get into the product design course at the Politecnico di Torino I enrolled into CS at UniTo because I&#x27;d been dabbling in computers since I was a kid, and in programming in the years leading up to my graduation from a linguistic lyceum. I did not attend a single course of the CS program. I don&#x27;t even know what the CS department looks like because I never set foot into it. I&#x27;d planned a trip to, of all places, an anime convention with some friends I had made on the internet, and the trip just happened to overlap with the first few days of courses at the university. After I came back from the convention I figured that ship had sailed and I would not be able to bear the shame of missing the first day of uni. In the mornings I would pretend to take the train to uni, and instead go walking in the woods around my town. I was not in a good place mentally.&lt;&#x2F;p&gt;
&lt;p&gt;Nevertheless, I kept programming. Eventually my father figured out that I was not going to uni. He let me stay home for the rest of the year until I figured things out. I learned Elm and OCaml. The sabbatical year turned into two. I learned TypeScript and React and made some small websites. Now I had to find a job. I rewrote the imageboard that kickstarted my interest in programming in Ur&#x2F;Web. On the cusp of the third year, I got hired at a consultancy agency by some kind people who were sort of amused by the way I told them about these projects.&lt;&#x2F;p&gt;
&lt;p&gt;During this time I developed a narrative for myself to cling on to. That&#x27;s what you have to do when you&#x27;re in a bad spot; you tell yourself whatever horseshit you need to keep yourself sane. I convinced myself that I was better than the average programmer, and certainly better than the people who came out of uni having only taken a cursory glance at Java and C++. Not only that but I&#x27;d come all this way by myself, so I didn&#x27;t need help from others. I was &lt;em&gt;proud&lt;&#x2F;em&gt; of my skills and my self-reliance.&lt;&#x2F;p&gt;
&lt;p&gt;That pride begat a lot of traits that I carry with me to this day. Sometimes I find it hard to ask for help, particularly when I&#x27;m already knee-deep into a problem. I refuse to use project templates. If a library gives me a problem I&#x27;d rather reimplement it myself from first principles than figuring out how to fix it upstream. I generally don&#x27;t like working in other people&#x27;s code. If I have to choose between using a library that would be completely fine for my purposes and reimplementing the parts I need myself, I&#x27;ll choose the shortest route to yak shaving any day of the week.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m a competitive yak shaver. I&#x27;ve been wanting to revamp this blog for a while, but I always get stuck on the part where I have to reimplement the markdown parser. The production-level ones are clearly not up to the task because they don&#x27;t produce the HTML output that I want, and I think I could do better anyway. I burn out on my own projects on a regular basis. Thank fuck I can turn to videogames when that happens.&lt;&#x2F;p&gt;
&lt;p&gt;Oh, right, videogames. Baby Steps. Where were we?&lt;&#x2F;p&gt;
&lt;p&gt;Baby Steps is partly about learning to accept help. It&#x27;s not easy to do that; you have to swallow your pride and accept that the help you&#x27;re getting may not be in the form that you envisioned. It took me a long time to convince myself to use &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt; to set up this blog when I don&#x27;t really like the templating language it uses and I know that I could just as well write my own static site generator with a principled templating language and blackjack and hookers, given enough time.&lt;&#x2F;p&gt;
&lt;p&gt;But that&#x27;s just the thing: time. I work a full-time job and I have many other things to think about other than implementing the perfect templating language. I don&#x27;t have the energy to do all of that. And Zola is pretty damn good anyway.&lt;&#x2F;p&gt;
&lt;p&gt;I think I need to learn to accept that compromises are good, actually. That I can&#x27;t do everything myself. That I don&#x27;t need to do everything myself. That other people&#x27;s code is often better than my own, and that flawed code that exists is infinitely better than better code that doesn&#x27;t. That I should be getting help in any way I can.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Simple Python devshells with Nix and direnv</title>
          <pubDate>Sat, 08 Jun 2024 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/python-nix-shells/</link>
          <guid>https://sgt.hootr.club/blog/python-nix-shells/</guid>
          <description xml:base="https://sgt.hootr.club/blog/python-nix-shells/">&lt;p&gt;In my last post, I mentioned I&#x27;m currently working at a Python shop. I&#x27;m also a huge fan of Nix, and I believe that a day may come when we can get rid of the messy deployment setup we use now and replace it with Nix, but it is not this day. Today I&#x27;m just gonna talk about how I stealthily manage my development environments for the Python projects at work with Nix and direnv, and how you could easily do the same.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;who-is-this-for&quot;&gt;Who is this for?&lt;&#x2F;h2&gt;
&lt;p&gt;You&#x27;re running NixOS or nix-darwin or home-manager on your computer of choice or maybe you&#x27;re just trying Nix out, and you just want to get things done at your job.&lt;&#x2F;p&gt;
&lt;p&gt;You don&#x27;t particularly care about building a reproducible derivation or an image that you can deploy with Nix.&lt;&#x2F;p&gt;
&lt;p&gt;You don&#x27;t want to spend too much time figuring out why psycopg or some other Python library you don&#x27;t want to pick a fight with is not building in your current checkout of nixpkgs.&lt;&#x2F;p&gt;
&lt;p&gt;You don&#x27;t want to install multiple versions of Python on your machine or mess with the tooling that should take care of that for you.&lt;&#x2F;p&gt;
&lt;p&gt;You want to keep things clean.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-do-we-want&quot;&gt;What do we want?&lt;&#x2F;h2&gt;
&lt;p&gt;Since this is a work project, let&#x27;s talk requirements!&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We shouldn&#x27;t make any changes to the repositories; we don&#x27;t want to go around dropping files in every team&#x27;s repos if they don&#x27;t want to buy into the tooling.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;We don&#x27;t have to go all-in on Nix. Debugging a failing build of a Python package for a repo we don&#x27;t touch all that often sucks and it adds too much overhead.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Related to the previous point, the setup should be very similar to the one your team members are using.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In short: don&#x27;t rock the boat. There&#x27;s a time and a place for introducing tooling, and it is not now. We just want a working setup that succeeds in providing a Python development environment, without littering our global environment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-flake&quot;&gt;The flake&lt;&#x2F;h2&gt;
&lt;p&gt;The flake where I keep my dev environments looks kind of like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  inputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;github:NixOS&#x2F;nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    flake-utils&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;github:numtide&#x2F;flake-utils&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  outputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; flake-utils&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  	flake-utils&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;eachDefaultSystem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;system&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      let&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;          inherit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; system&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;        }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        mkPythonShell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; python&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mkShell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;            name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;python-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;python&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;            buildInputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pyright&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ruff&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;poetry&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;              (&lt;&#x2F;span&gt;&lt;span&gt;python&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;withPackages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;ps&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                ps&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;black&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                ps&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;flake8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;              ]))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;            ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;          }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        devShells&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;          python39&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mkPythonshell pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;python39&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;          python310&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mkPythonShell pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;python310&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;          python311&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mkPythonShell pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;python311&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;          python312&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mkPythonShell pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;python312&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;        }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I keep one devShell for each version of Python, and only install tools that are required for Python development but are not specified in the project dependencies so they don&#x27;t litter my &lt;code&gt;$PATH&lt;&#x2F;code&gt; when I&#x27;m not using them. I prefer to keep this package set small so that it breaks less often.&lt;&#x2F;p&gt;
&lt;p&gt;To use it, you&#x27;re gonna have to create a new git repository, dump the code above in a &lt;code&gt;flake.nix&lt;&#x2F;code&gt;, and then run &lt;code&gt;nix flake lock&lt;&#x2F;code&gt;. You can also try running one of the shells with &lt;code&gt;nix develop .#python312&lt;&#x2F;code&gt; to make sure that everything is working correctly.&lt;&#x2F;p&gt;
&lt;p&gt;This flake is freestanding and lives outside of any existing repos, and if you manage to convert some coworkers to Nix it can be pushed to the company&#x27;s git forge of choice.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-envrc&quot;&gt;The .envrc&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;direnv.net&#x2F;&quot;&gt;direnv&lt;&#x2F;a&gt; is a wonderful tool and if you&#x27;re not using it yet, you should check it out. It installs a hook inside your shell that runs when you change directories, and when it detects that an &lt;code&gt;.envrc&lt;&#x2F;code&gt; file is present in the current directory or further up the tree, it runs the commands specified in that &lt;code&gt;.envrc&lt;&#x2F;code&gt; file and updates the env variables accordingly. This means that you can set env variables, add executables to your &lt;code&gt;$PATH&lt;&#x2F;code&gt; and, crucially, set your Python venv automatically.&lt;&#x2F;p&gt;
&lt;p&gt;This &lt;code&gt;.envrc&lt;&#x2F;code&gt; is a simple shell script that is executed with some predefined utility commands (the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;direnv.net&#x2F;man&#x2F;direnv-stdlib.1.html&quot;&gt;stdlib&lt;&#x2F;a&gt;). These include loading &lt;code&gt;.env&lt;&#x2F;code&gt; files with &lt;code&gt;dotenv&lt;&#x2F;code&gt; and automatically setting the correct interpreter version and installing dependencies for several languages. We don&#x27;t need most of that though, because the Nix dev environment manages the Python version for us.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re using &lt;code&gt;home-manager&lt;&#x2F;code&gt;, the installation is as easy as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  programs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;direnv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    enable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    nix-direnv&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;enable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;nix-direnv&lt;&#x2F;code&gt; is absolutely required when you&#x27;re working with flakes. Without it, it may take several seconds to &lt;code&gt;cd&lt;&#x2F;code&gt; into the project directory. With &lt;code&gt;nix-direnv&lt;&#x2F;code&gt;, loading previously cached flakes will take less than a second.&lt;&#x2F;p&gt;
&lt;p&gt;To drop into one of the Python shells we created in our flakes, create an &lt;code&gt;.envrc&lt;&#x2F;code&gt; file in the root of a repository containing this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; flake ..&#x2F;path&#x2F;to&#x2F;flake#python310&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(The path&#x2F;to&#x2F;flake can be &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nix.dev&#x2F;manual&#x2F;nix&#x2F;2.22&#x2F;command-ref&#x2F;new-cli&#x2F;nix3-flake.html#examples&quot;&gt;anything that is accepted as a flake URL&lt;&#x2F;a&gt;: a relative path from your current directory, a git repository on github or your employer&#x27;s git forge, or a URL that points to a tarball.)&lt;&#x2F;p&gt;
&lt;p&gt;Then close the file, run &lt;code&gt;direnv allow&lt;&#x2F;code&gt;, and you should see Nix preparing a dev environment containing the Python 3.11 interpreter and all the tools we specified in the flake. This might take a while the first time you do it, but after the initial setup it&#x27;ll be instantaneous. Try it out!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ cd project&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;direnv: loading project&#x2F;.envrc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;direnv: using flake ..&#x2F;path&#x2F;to&#x2F;flake#python310&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;direnv: nix-direnv: using cached dev shell&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;direnv: export +AR +AS +CONFIG_SHELL ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ black --version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;black, 24.4.2 (compiled: yes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Python (CPython) 3.10.13&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ python -V&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Python 3.10.13&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ cd ..&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;direnv: unloading&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ black --version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;bash: black: command not found&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ python -V&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Python 3.9.6&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;.envrc&lt;&#x2F;code&gt; file should live at the root of your project, but if you don&#x27;t want to check it into git or add it into &lt;code&gt;.gitignore&lt;&#x2F;code&gt; you can sneakily add an ignore for it inside your project&#x27;s &lt;code&gt;.git&#x2F;info&#x2F;exclude&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.direnv&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.envrc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now if you run &lt;code&gt;git status&lt;&#x2F;code&gt;, you won&#x27;t see any added files!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-venv&quot;&gt;The venv&lt;&#x2F;h2&gt;
&lt;p&gt;For reasons I won&#x27;t go into here, we&#x27;re still using &lt;code&gt;pip&lt;&#x2F;code&gt; and good old &lt;code&gt;requirements.txt&lt;&#x2F;code&gt; files to specify dependencies rather than &lt;code&gt;poetry&lt;&#x2F;code&gt; or any of the fancy new Python tooling. To simplify this dev environment setup and keep it close to our coworkers&#x27;, we&#x27;re gonna use normal Python tooling to set up the virtual env and install dependencies.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First of all, create the venv.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;python&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -m venv venv&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Then add these lines to your &lt;code&gt;.envrc&lt;&#x2F;code&gt; to make &lt;code&gt;direnv&lt;&#x2F;code&gt; load the venv. (I added the &lt;code&gt;VIRTUAL_ENV_DISABLE_PROMPT&lt;&#x2F;code&gt; bit because it messes up my Starship prompt, but you may want to keep it.)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export&lt;&#x2F;span&gt;&lt;span&gt; VIRTUAL_ENV_DISABLE_PROMPT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;source&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; venv&#x2F;bin&#x2F;activate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Enter the venv by allowing the changes you made to your &lt;code&gt;.envrc&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;direnv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; allow&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Now that you&#x27;re inside the venv, upgrade &lt;code&gt;pip&lt;&#x2F;code&gt; and &lt;code&gt;setuptools&lt;&#x2F;code&gt; and install &lt;code&gt;wheel&lt;&#x2F;code&gt;. (Imma keep it real with you: this is just cargo culting. I don&#x27;t actually know how much of this is needed. Feel free to @ me for this one.)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;pip3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; install --upgrade pip setuptools&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;pip&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; install wheel&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Let&#x27;s get installing!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;pip&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; install -r requirements.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;the-end&quot;&gt;The end&lt;&#x2F;h2&gt;
&lt;p&gt;Repeat this for every Python repository you might have at your company. That&#x27;s it! You can start working on the actual tasks in your sprint now.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Do I not like Ruby anymore?</title>
          <pubDate>Tue, 28 May 2024 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/maybe-i-like-python-now/</link>
          <guid>https://sgt.hootr.club/blog/maybe-i-like-python-now/</guid>
          <description xml:base="https://sgt.hootr.club/blog/maybe-i-like-python-now/">&lt;p&gt;I recently started working at a Python shop. The reasons behind this choice of employment are very much unrelated to the technology stack. Python is &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Euphemism&quot;&gt;not my favorite programming language&lt;&#x2F;a&gt;. In fact, allow me to drop the euphemism and express my pure, unadulterated thoughts about it: I never liked Python, I see it as a huge red flag and I think the world would be a better place if we all decided to finally move on from it.&lt;&#x2F;p&gt;
&lt;p&gt;With that out of the way, let&#x27;s talk about how I&#x27;ve recently started to come around to Python and actually kind of like it in some aspects?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-used-to-love-ruby&quot;&gt;I (used to) love Ruby&lt;&#x2F;h2&gt;
&lt;p&gt;Ruby was my first love as a programmer. It is a playful, concise, elegant, expressive language that is built out of a handful of simple concepts with a good serving of syntax sugar on top.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;There&#x27;s no distinction between objects and primitives; everything is &lt;em&gt;actually&lt;&#x2F;em&gt; an object, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;ruby-doc.org&#x2F;3.3.1&#x2F;NilClass.html&quot;&gt;even &lt;code&gt;nil&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;!&lt;&#x2F;li&gt;
&lt;li&gt;You can &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;yehudakatz.com&#x2F;2009&#x2F;10&#x2F;04&#x2F;emulating-smalltalks-conditionals-in-ruby&quot;&gt;reimplement &lt;code&gt;if&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; using blocks and two additional methods on &lt;code&gt;NilClass&lt;&#x2F;code&gt; and &lt;code&gt;FalseClass&lt;&#x2F;code&gt; if you want!&lt;&#x2F;li&gt;
&lt;li&gt;Method calls are just &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;ruby-doc.org&#x2F;3.3.1&#x2F;BasicObject.html#method-i-__send__&quot;&gt;syntax sugar for &lt;code&gt;send&lt;&#x2F;code&gt;ing messages to objects&lt;&#x2F;a&gt;!&lt;&#x2F;li&gt;
&lt;li&gt;You can define new methods on an object at call time &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;ruby-doc.org&#x2F;3.3.1&#x2F;BasicObject.html#method-i-method_missing&quot;&gt;using &lt;code&gt;method_missing&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Ruby was clearly &lt;em&gt;designed&lt;&#x2F;em&gt; taking inspiration from such language designer&#x27;s languages as Smalltalk and Lisp, and as a budding Schemer with an interest in programming language design, that inspired me a lot.&lt;&#x2F;p&gt;
&lt;p&gt;Now, Python and Ruby were the two most popular &quot;scripting&quot; languages at the time. Ruby exploded thanks to Rails, and Python saw a lot of success as a language for data science and a better choice than Perl for command line tools and scripts.&lt;&#x2F;p&gt;
&lt;p&gt;The two languages were often compared and contrasted, and of course I, as a fan of Ruby, had a lot of opinions about Python.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;python-as-a-worse-ruby-and-an-even-worse-scheme&quot;&gt;Python as a worse Ruby (and an even worse Scheme)&lt;&#x2F;h2&gt;
&lt;p&gt;I kind of lied earlier when I said that Ruby was my first love as a programmer. The first time I started to really &lt;em&gt;grok&lt;&#x2F;em&gt; programming was when I learned a little bit of Scheme. I learned recursion before &lt;code&gt;for&lt;&#x2F;code&gt; loops, and I learned immutability before mutability.&lt;&#x2F;p&gt;
&lt;p&gt;As I said in the beginning of this post, I didn&#x27;t like Python. My dislike for it was best exemplified by its choice to make &lt;code&gt;if&lt;&#x2F;code&gt; a &lt;em&gt;statement&lt;&#x2F;em&gt; rather than an expression. If you want to assign a variable conditionally in Python you have to &lt;em&gt;declare&lt;&#x2F;em&gt; it first, and then &lt;em&gt;mutate&lt;&#x2F;em&gt; it from inside the &lt;code&gt;if&lt;&#x2F;code&gt; statement, and this just didn&#x27;t sit right with me.&lt;&#x2F;p&gt;
&lt;p&gt;(Yes, you can also use the &lt;code&gt;&amp;lt;then-expression&amp;gt; if &amp;lt;condition&amp;gt; else &amp;lt;else-expression&amp;gt;&lt;&#x2F;code&gt; inline conditional, but that looks weird to me even now.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;lambda&lt;&#x2F;code&gt;s, my bread and butter as a Rubyist and Schemer, are replaced by horrible twisted versions of themselves that don&#x27;t allow statements. Even &lt;code&gt;print&lt;&#x2F;code&gt; was a statement before Python 3.0, so you couldn&#x27;t use it inside of a &lt;code&gt;lambda&lt;&#x2F;code&gt;. The horror!&lt;&#x2F;p&gt;
&lt;p&gt;In summary, Python to me just felt &lt;em&gt;unpleasant&lt;&#x2F;em&gt; to use. It&#x27;s a language that prides itself on having only one way to do things, and that way was usually not the one I wanted to use.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;type-systems-for-the-untypable&quot;&gt;Type systems for the untypable&lt;&#x2F;h2&gt;
&lt;p&gt;At some point I found myself writing frontend code. JavaScript is not my favorite language, but TypeScript tried &lt;em&gt;very hard&lt;&#x2F;em&gt; to get me to love it.&lt;&#x2F;p&gt;
&lt;p&gt;I consider TypeScript to be the gold standard when it comes to type systems on top of dynamic languages. It is powerful enough to model almost all Real World JS, and while this approach introduces a lot of complexity, it also brought the language a lot of success.&lt;&#x2F;p&gt;
&lt;p&gt;TypeScript does a &lt;em&gt;bit&lt;&#x2F;em&gt; more work than your classic ML (as in &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ML_(programming_language)&quot;&gt;Meta Language&lt;&#x2F;a&gt;) type system. TypeScript can:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;2&#x2F;narrowing.html&quot;&gt;Narrow&lt;&#x2F;a&gt; a variable&#x27;s type based on the return type of a function you call on it!&lt;&#x2F;li&gt;
&lt;li&gt;Manipulate types by &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;2&#x2F;keyof-types.html&quot;&gt;destructuring&lt;&#x2F;a&gt; and &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;2&#x2F;mapped-types.html&quot;&gt;constructing&lt;&#x2F;a&gt; them!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;2&#x2F;conditional-types.html&quot;&gt;Make choices&lt;&#x2F;a&gt; while constructing a type based on subtyping rules!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The last two features in particular unlock some incredible type-level programming potential. TypeScript is one of the few type systems in which you can &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cassiozen&#x2F;TDungeon&quot;&gt;play a text adventure&lt;&#x2F;a&gt; and &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;codemix&#x2F;ts-sql&quot;&gt;query a database&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;On top of being the most complex (and fun) type system of most languages out there, let alone those topping the TIOBE Index, TypeScript certainly makes JavaScript&#x27;s flaws a lot more bearable. It almost made me &lt;em&gt;enjoy&lt;&#x2F;em&gt; writing frontend code for a living.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-changed&quot;&gt;I changed&lt;&#x2F;h2&gt;
&lt;p&gt;One thing I learned while writing TypeScript was that bad language features can be excused by some static analysis. &lt;em&gt;Maybe&lt;&#x2F;em&gt; not having &lt;code&gt;match&lt;&#x2F;code&gt; is ok when you have type narrowing based on control flow and unions. &lt;em&gt;Maybe&lt;&#x2F;em&gt; not having &lt;code&gt;if&lt;&#x2F;code&gt; expressions is ok when you can statically check that a variable was initialized after an &lt;code&gt;if&lt;&#x2F;code&gt; statement. &lt;em&gt;Maybe&lt;&#x2F;em&gt; &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.hanselman.com&#x2F;blog&#x2F;stringly-typed-vs-strongly-typed&quot;&gt;stringly typed&lt;&#x2F;a&gt; variables are ok when you can statically enumerate the magic strings and ensure they are constructed correctly.&lt;&#x2F;p&gt;
&lt;p&gt;I also started writing quite a bit of Rust, which is a great language to show your functional programmer friends when you want to tell them that mutability is &lt;em&gt;actually fine&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;python-changed&quot;&gt;Python changed&lt;&#x2F;h2&gt;
&lt;p&gt;Python is not the same language it used to be. Now it supports type hints! And &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;tutorial&#x2F;controlflow.html#match-statements&quot;&gt;&lt;code&gt;match&lt;&#x2F;code&gt; statements&lt;&#x2F;a&gt; with destructuring! Even &lt;code&gt;print&lt;&#x2F;code&gt; got turned into a normal function!&lt;&#x2F;p&gt;
&lt;p&gt;The type hints are easily my favorite feature. Not only do they provide type information to a good ecosystem of type checkers, but they can also be used by libraries to &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.pydantic.dev&#x2F;latest&#x2F;&quot;&gt;validate schemas&lt;&#x2F;a&gt; and &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;fastapi.tiangolo.com&#x2F;&quot;&gt;simplify defining web APIs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I think they&#x27;re a great case study for integrating types in an existing ecosystem.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They are built into the language, so unlike TypeScript where you need to insert a separate build step, there is no cost of adoption.&lt;&#x2F;li&gt;
&lt;li&gt;They are orthogonal to type checking and inspectable from within the language, so libraries like Pydantic can leverage them to bring benefits even to users who don&#x27;t run a type checker. Everybody wins!&lt;&#x2F;li&gt;
&lt;li&gt;The aforementioned libraries can serve as a gateway drug into the magical world of types ✨&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And here are some features of Python that I like which are unrelated to types:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Keyword arguments. You can call any function using the argument names as keywords without any ceremony in the function definition. I wish every language had this feature!&lt;&#x2F;li&gt;
&lt;li&gt;It has namespaces, which are pretty good.&lt;&#x2F;li&gt;
&lt;li&gt;The lambdas are bad, but comprehensions and generator functions are neat. They remind me of F#&#x27;s sequence expressions.&lt;&#x2F;li&gt;
&lt;li&gt;Having preferably only one way to do things is a good feature when you&#x27;re working with many people on a project.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;ruby-didn-t-change&quot;&gt;Ruby didn&#x27;t change&lt;&#x2F;h2&gt;
&lt;p&gt;So where does that leave Ruby, my former favorite language? Well, Ruby didn&#x27;t change as much in the last 10 years. A bunch of performance work to benefit big applications like Rails, a couple of interesting features that didn&#x27;t seem to catch on, and a handful of new syntax additions that don&#x27;t amount to much. Nothing quite as game-changing as type hints. Matz doesn&#x27;t seem to care for them.&lt;&#x2F;p&gt;
&lt;p&gt;I still use Ruby for some scripts because I know it like the back of my hand, but... it just doesn&#x27;t feel the same. I get just a bit more irked by its quirks. I miss features from other languages. I long for keyword arguments, type hints, namespaces, I long for... Python!?&lt;&#x2F;p&gt;
&lt;p&gt;Maybe this is a sign that I&#x27;ve changed too much for Ruby. We had a lot of fun together, but it&#x27;s time to leave it behind. Goodbye Ruby, and thanks for all the chunky bacon.&lt;&#x2F;p&gt;
&lt;p&gt;And to Python I say: good job! You can have one of my midnight chicken nuggets. You deserve it.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Solving Advent of Code 2021 Day 13 with a linear equation</title>
          <pubDate>Mon, 13 Dec 2021 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/aoc2021-day13-linear-equation/</link>
          <guid>https://sgt.hootr.club/blog/aoc2021-day13-linear-equation/</guid>
          <description xml:base="https://sgt.hootr.club/blog/aoc2021-day13-linear-equation/">&lt;p&gt;This year I&#x27;m doing Advent of Code in Erlang. The data structures it provides are all immutable, so I have to be kinda clever if I don&#x27;t want my solutions to run too slowly. This is my solution for &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;adventofcode.com&#x2F;2021&#x2F;day&#x2F;13&quot;&gt;Day 13: Transparent Origami&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing I noticed when I got the input was that all the folds are right down the middle, so I figured that instead of doing all the folds iteratively you could get the final position of a point on the paper just knowing the X and Y of the point and the X and Y offsets of the last folds.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine that you have a 15x11 piece of paper.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...............&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And that the instructions tell you to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;fold along y=5&lt;&#x2F;li&gt;
&lt;li&gt;fold along x=7&lt;&#x2F;li&gt;
&lt;li&gt;fold along y=2&lt;&#x2F;li&gt;
&lt;li&gt;fold along x=3&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you were to fold that piece of paper following the instructions and then unfold it, you&#x27;d get a grid of 3x2 rectangles with a line of crease between each of them. Notice that the width and height of the rectangles are the same as the last x and y the paper was folded along.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...|...|...|...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...|...|...|...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---+---+---+---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...|...|...|...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...|...|...|...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---+---+---+---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...|...|...|...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...|...|...|...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---+---+---+---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...|...|...|...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...|...|...|...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The key thing here is understanding how these rectangles will end up looking when the whole piece of paper is folded. The top left rectangle is unchanged, but the next one on the right will be &lt;em&gt;flipped horizontally&lt;&#x2F;em&gt;, and the next one on the right will be unchanged again. The first rectangle down from the top left will be &lt;em&gt;flipped vertically&lt;&#x2F;em&gt;, and the one on its right will end up &lt;em&gt;rotated by 180 degrees&lt;&#x2F;em&gt;, because it&#x27;ll be flipped both horizontally and vertically.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a visualization, where u = unchanged, h = flipped horizontally, v = flipped vertically and r = flipped both directions.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;uuu|hhh|uuu|hhh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;uuu|hhh|uuu|hhh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---+---+---+---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;vvv|rrr|vvv|rrr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;vvv|rrr|vvv|rrr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---+---+---+---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;uuu|hhh|uuu|hhh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;uuu|hhh|uuu|hhh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---+---+---+---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;vvv|rrr|vvv|rrr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;vvv|rrr|vvv|rrr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So if you want to get the final X coordinate of a point in the 3rd rectangle right from the top left (the one at 2,0, if the first rectangle is 0,0), for example 10,0, you&#x27;d have to subtract the number of creases between that and the first rectangle, in this case 2, and then modulo by the width of the rectangle, which is 3, which gives you 2.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x = 10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;width = 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;creases = 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;final_x = (x - creases) % width&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you want to get the final X coordinate of a point in the 2nd rectangle though, for example 0,4, you&#x27;d have to perform the same operations as above and then subtract it from the width of the rectangle minus 1, since the rectangle is flipped horizontally.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;final_x = width - ((x - creases) % width) - 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What I did at this point was go on Wolfram Alpha to try and figure out a formula for getting the number of creases given a certain X. I&#x27;m sure you can find that out somehow. I didn&#x27;t, but what I noticed was that the function I described above formed a certain shape...&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; | 2 2     2 2     2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |1   1   1   1   1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-0-----0-0-----0-0--&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you plot the input X as the X of the graph and the expected output numbers as the Y, you get this funny looking graph as a result. If you want to know what position a certain number will end up in, just walk the X that many times and find the number at that position.&lt;&#x2F;p&gt;
&lt;p&gt;Some X positions are empty because that&#x27;s where the creases are. If you put some pointy tips on those positions you&#x27;d end up with a proper &lt;strong&gt;triangle wave&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |  ^       ^       ^&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; | &#x2F; \     &#x2F; \     &#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |&#x2F;   \   &#x2F;   \   &#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&#x2F;-----\-&#x2F;-----\-&#x2F;---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;v|      v       v&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So how do we use this knowledge to solve Day 13 of Advent of Code 2021? Well, you could look at the explanation on Wikipedia and translate the general formula into your favourite language, but that implies you can read math notation. Or you could google &quot;triangle wave function programming&quot; and find &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;1073606&#x2F;is-there-a-one-line-function-that-generates-a-triangle-wave&#x2F;22400799#22400799&quot;&gt;this StackOverflow answer&lt;&#x2F;a&gt; and then copy and paste that in your code.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;y = (A&#x2F;P) * (P - abs(x % (2*P) - P))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since the amplitude and the period of the wave are the same (we always advance by 1) we can get rid of the first division. You&#x27;ll also notice that the graph of the function for the width of the rectangle goes from -1 to 3 (an interval of 4), so we&#x27;ll have to add 1 to both the width and the X to use this, and in the end we&#x27;ll have to subtract 1 from the output to return it to the 0..2 interval we need.&lt;&#x2F;p&gt;
&lt;p&gt;This is what I ended up with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x += 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;width += 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;final_x = (width - abs(x % (2 * width) - width)) - 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It runs pretty quickly &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;code-challenges&#x2F;blob&#x2F;master&#x2F;advent-of-code-2021&#x2F;day_13.erl&quot;&gt;even in Erlang&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Types of parser combinators</title>
          <pubDate>Tue, 16 Nov 2021 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/types-of-parser-combinators/</link>
          <guid>https://sgt.hootr.club/blog/types-of-parser-combinators/</guid>
          <description xml:base="https://sgt.hootr.club/blog/types-of-parser-combinators/">&lt;p&gt;I&#x27;ve been trying to write this post for a long while. Parser combinators to me are a Leitmotiv that plays whenever I need to parse something and the times look dire. Every time I almost forget they exist, and every time a light will go off inside my head and I&#x27;ll think, &quot;I know! I&#x27;ll use parser combinators!&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Parser combinators are a universally useful concept. They&#x27;re like parsley, because they go well with any sort of data that you might wanna throw at them. And yes, parser combinators &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;blog.plover.com&#x2F;prog&#x2F;burritos.html&quot;&gt;&lt;em&gt;are&lt;&#x2F;em&gt; like burritos&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let me explain myself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-have-a-parsing-problem&quot;&gt;You have a parsing problem&lt;&#x2F;h2&gt;
&lt;p&gt;As you do, because what programs do is transform data, and you only have one hammer, and so all programs are compilers. Usually this involves consuming less-structured input and producing more-structured output. &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;lexi-lambda.github.io&#x2F;blog&#x2F;2019&#x2F;11&#x2F;05&#x2F;parse-don-t-validate&#x2F;&quot;&gt;This is called parsing&lt;&#x2F;a&gt;, and this is why you have a parsing problem.&lt;&#x2F;p&gt;
&lt;p&gt;This input could be a &lt;em&gt;stream&lt;&#x2F;em&gt; of things, be it characters, bytes, or tokens, or it could be a &lt;em&gt;tree&lt;&#x2F;em&gt; of things, be it a hash table, the AST of a program, JSON, HTML, s-expressions, or any sort of arborescent structure you might think of. What a parser does, then, is to take that structure, or maybe just part of it, and make sense of it. Sometimes it doesn&#x27;t make sense, and in that case we want to return an error. We can turn this into a type:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Output&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  rest&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Input&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Parser&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Input&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In other words, a parser is a function that takes some input data and returns either some more-structured data with or without the rest of the input depending on the parser, or an error, if the data doesn&#x27;t make sense.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hot-parsers-in-your-area&quot;&gt;Hot parsers in your area&lt;&#x2F;h2&gt;
&lt;p&gt;So what are some things that are parsers? &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Global_Objects&#x2F;JSON&#x2F;parse&quot;&gt;&lt;code&gt;JSON.parse&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is an obvious parser. It fully consumes its input and throws an error if it doesn&#x27;t make sense, or if there&#x27;s trailing data.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; JSON.parse(&amp;quot;123&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- 123&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; JSON.parse(&amp;quot;&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- Uncaught SyntaxError: ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; JSON.parse(&amp;quot;123asd&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- Uncaught SyntaxError: ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Global_Objects&#x2F;parseInt&quot;&gt;&lt;code&gt;parseInt&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is another obvious parser, one with confusing semantics and a few footguns. &lt;code&gt;parseInt&lt;&#x2F;code&gt; accepts trailing data and returns &lt;code&gt;NaN&lt;&#x2F;code&gt; when the input doesn&#x27;t make sense.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; parseInt(&amp;quot;123&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- 123&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; parseInt(&amp;quot;0x7b&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- 123&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; parseInt(&amp;quot;123asd&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- 123&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; parseInt(&amp;quot;asd&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- NaN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Global_Objects&#x2F;RegExp&#x2F;exec&quot;&gt;&lt;code&gt;RegExp#exec&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is also a parser. This one can return multiple outputs, and the match also doesn&#x27;t have to start right at the beginning of the input.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; const matchB = (str) =&amp;gt; str.match(&#x2F;b&#x2F;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; const match = matchB(&amp;quot;abc&amp;quot;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- Array [&amp;quot;b&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; match.index&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But we can go deeper. Indexing an array can also be called a parser, one that doesn&#x27;t return the rest of the input and returns &lt;code&gt;undefined&lt;&#x2F;code&gt; on error.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; const first = (array) =&amp;gt; array[0];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; first([1, 2, 3])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; first([])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- undefined&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By this logic, &lt;code&gt;Array#find&lt;&#x2F;code&gt; could also be used to construct a parser. This one also doesn&#x27;t return the rest of its input and returns &lt;code&gt;undefined&lt;&#x2F;code&gt; on error.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; const findEven = (array) =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  array.find((n) =&amp;gt; n % 2 === 0);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; findEven([1, 2, 3, 4])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; findEven([1, 3, 5])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- undefined&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; findEven([])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- undefined&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But it&#x27;s not only lists of data that can be parsed. Object indexing is also parsing!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; const getNestedItem = (o) =&amp;gt; o?.a?.b?.c;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; getNestedItem({ a: { b: { c: 1 } } })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt; getNestedItem({})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;- undefined&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Other examples could be &lt;code&gt;String#slice&lt;&#x2F;code&gt;, &lt;code&gt;Set#has&lt;&#x2F;code&gt;... The list could go on.&lt;&#x2F;p&gt;
&lt;p&gt;These functions don&#x27;t exactly match the signature of the &lt;code&gt;Parser&lt;&#x2F;code&gt; we defined above, but they match the &lt;em&gt;spirit&lt;&#x2F;em&gt;. Take something, apply some logic to make sense of it, and return either an output that tells you something about that input, or an error.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ok-so&quot;&gt;Ok, so?&lt;&#x2F;h2&gt;
&lt;p&gt;You&#x27;ve done well to make it this far, but I&#x27;m sure you, hypothetical reader #1 who lurks all the colored websites and has already seen countless posts about parser combinators, know all about parser combinators. You know the &lt;code&gt;char&lt;&#x2F;code&gt; parser and you know the &lt;code&gt;string&lt;&#x2F;code&gt; parser and you might even know the &lt;code&gt;regexp&lt;&#x2F;code&gt; parser. You might also know some variation of the &lt;code&gt;token&lt;&#x2F;code&gt; parser and the &lt;code&gt;byte&lt;&#x2F;code&gt; parser and maybe you&#x27;ve even given &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;nom&#x2F;5.0.0&#x2F;nom&#x2F;&quot;&gt;nom&lt;&#x2F;a&gt; a try.&lt;&#x2F;p&gt;
&lt;p&gt;And I&#x27;m sure you, hypothetical reader #2 who has just read the paragraph above and has no idea what I&#x27;m talking about, have some questions. To you I recommend reading &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.theorangeduck.com&#x2F;page&#x2F;you-could-have-invented-parser-combinators&quot;&gt;You could have invented parser combinators&lt;&#x2F;a&gt;, an excellent post that might give you an intuition for understanding parser combinators, and then coming back to this post.&lt;&#x2F;p&gt;
&lt;p&gt;Knowing all that you might be wondering, what&#x27;s the point of this post? Do we really need &lt;em&gt;yet another&lt;&#x2F;em&gt; parser combinator post? Searching &quot;parser combinators&quot; yields tens, even hundreds of posts, tutorials, books, libraries, and so on. I&#x27;m not sure there&#x27;s as many as there are monad tutorials, but it looks close enough to me.&lt;&#x2F;p&gt;
&lt;p&gt;I would be inclined to agree with you; I&#x27;ve read a few dozen of these posts, and I see them pop up on the colored websites every now and then. I don&#x27;t even click on them anymore because they mostly say the same things in a slightly different sauce.&lt;&#x2F;p&gt;
&lt;p&gt;However, I would still urge you to read on, firstly because this is &lt;em&gt;my&lt;&#x2F;em&gt; parser combinators blog post and there are many like it but this is mine, and secondly because I don&#x27;t think I&#x27;ve seen this specific bit of insight in any of the aforementioned blog posts. Ready?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;parser-combinators-can-parse-much-more-than-strings-and-bytes&quot;&gt;Parser combinators can parse much more than strings and bytes&lt;&#x2F;h2&gt;
&lt;p&gt;You might have noticed that I added object indexing as a form of parsing. This is one of the key components of Elm&#x27;s &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;package.elm-lang.org&#x2F;packages&#x2F;elm&#x2F;json&#x2F;latest&#x2F;Json-Decode&quot;&gt;Json.Decode&lt;&#x2F;a&gt; module. Rescript also has &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nkrkv&#x2F;jzon&quot;&gt;a similar library&lt;&#x2F;a&gt;. That&#x27;s right, you can parse JSON with parser combinators, and I don&#x27;t mean deserializing a string into a JSON tree; I mean turning a JSON tree into typed data that you can actually work with.&lt;&#x2F;p&gt;
&lt;p&gt;This is not much of an issue in TypeScript because &lt;code&gt;JSON.parse&lt;&#x2F;code&gt; returns &lt;code&gt;any&lt;&#x2F;code&gt; and that means it will just trust you that whatever data it just parsed has the shape you told it would. But that&#x27;s not really parsing, that&#x27;s an unsafe type cast!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt; aims to be a language with no runtime exceptions, and it achieves that goal by not trusting anything that comes from the outside world; if you want to query an external API that speaks JSON, you&#x27;ll have to &lt;em&gt;parse&lt;&#x2F;em&gt; that untrusted JSON data into an Elm record using a &lt;code&gt;Decoder&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is what a decoder for a simple JSON object with two fields looks like.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;elm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type alias User =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  {&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; : String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  ,&lt;&#x2F;span&gt;&lt;span&gt; age&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; : Int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;userDecoder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; : Decoder Info&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;userDecoder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  map2 User&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;field&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;field&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;age&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Elm doesn&#x27;t outright call them that because it&#x27;s afraid of complex terminology, but &lt;code&gt;map2&lt;&#x2F;code&gt; and &lt;code&gt;field&lt;&#x2F;code&gt; are parser combinators. They&#x27;re functions that take parsers as arguments and return another parser that combines them in some way. If you take a careful look at the &lt;code&gt;Json.Decode&lt;&#x2F;code&gt; documentation you&#x27;ll find all the usual suspects: list, dict, field, maybe, oneOf, all the map functions, andThen, etc. are all names you&#x27;re probably familiar with if you&#x27;ve worked with parser combinators.&lt;&#x2F;p&gt;
&lt;p&gt;So what&#x27;s the difference between this and most other parser combinator libraries out there? Well, the combinators are mostly the same, but the building blocks are different. JSON contains simple fields like booleans, numbers, and strings, but also ordered collections (arrays) and unordered key-value collections (objects), so the simple &lt;code&gt;char&lt;&#x2F;code&gt; parser won&#x27;t help much here.&lt;&#x2F;p&gt;
&lt;p&gt;To parse all this nonsense, &lt;code&gt;Json.Decode&lt;&#x2F;code&gt; provides different base parsers. There&#x27;s one for each of the JSON data types: &lt;code&gt;string&lt;&#x2F;code&gt;, &lt;code&gt;bool&lt;&#x2F;code&gt;, &lt;code&gt;int&lt;&#x2F;code&gt;, &lt;code&gt;float&lt;&#x2F;code&gt;, &lt;code&gt;list&lt;&#x2F;code&gt;, and &lt;code&gt;dict&lt;&#x2F;code&gt;. Then it provides &lt;code&gt;field&lt;&#x2F;code&gt;, which pulls a value out of an object by name, and &lt;code&gt;index&lt;&#x2F;code&gt;, which pulls an item out of an array by index.&lt;&#x2F;p&gt;
&lt;p&gt;The rest of the combinators are mostly the same as in stream-based parser combinators, which leads me to another one of my points.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;there-can-be-different-types-of-parsers&quot;&gt;There can be different types of parsers&lt;&#x2F;h2&gt;
&lt;p&gt;I think the main difference between a parser that consumes streams of items, such as a string, and a parser that consumes an unordered key-value collection, such as a JSON object, comes down to how you answer this question: &lt;em&gt;how do you pull a value out of that collection?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The answer varies depending on a few factors:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Is the collection ordered or unordered?&lt;&#x2F;li&gt;
&lt;li&gt;Can the collection be indexed by a key?&lt;&#x2F;li&gt;
&lt;li&gt;Can the keys be enumerated?&lt;&#x2F;li&gt;
&lt;li&gt;Can the keys be sorted?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let&#x27;s see how some basic data structures fare with these questions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Streams&lt;&#x2F;strong&gt; are ordered but cannot be indexed, so to pull out a value we can only &lt;em&gt;advance&lt;&#x2F;em&gt; them.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Sets&lt;&#x2F;strong&gt; are unordered and can be indexed, but the keys can&#x27;t be enumerated, so to pull out a value we can only &lt;em&gt;index&lt;&#x2F;em&gt; them using a key.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Structs&lt;&#x2F;strong&gt; (or objects) are unordered but can be indexed, so we can &lt;em&gt;index&lt;&#x2F;em&gt; them easily, but since the keys can be enumerated and they can be sorted we could also come up with a way to &lt;em&gt;advance&lt;&#x2F;em&gt; them.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Arrays&lt;&#x2F;strong&gt;, &lt;strong&gt;strings&lt;&#x2F;strong&gt; and &lt;strong&gt;tuples&lt;&#x2F;strong&gt; are ordered, they can be indexed, and keys can be enumerated since they have a fixed length, so we can either &lt;em&gt;advance&lt;&#x2F;em&gt; or &lt;em&gt;index&lt;&#x2F;em&gt; them.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Tagged unions&lt;&#x2F;strong&gt;, or variants, discriminated unions, sum types or whatever you might wanna call them, are similar to streams but they only contain one element, so technically we can advance them, but we can only do it once and there&#x27;s gonna be several functions we can use to advance, typically one for each case of the union like in Elm&#x27;s &lt;code&gt;Json.Decode&lt;&#x2F;code&gt;. I&#x27;m calling this action &lt;em&gt;consume&lt;&#x2F;em&gt;, but we could also view it as a special case of &lt;em&gt;advance&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To sum up, these are the different types of basic parsers we have identified:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Consume&lt;&#x2F;strong&gt;-type parsers operate on the full input. The pull function takes the full input and only returns an output. Some examples are JavaScript&#x27;s &lt;code&gt;JSON.parse&lt;&#x2F;code&gt;, or the basic parsers in Elm&#x27;s &lt;code&gt;Json.Decode&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Advance&lt;&#x2F;strong&gt;-type parsers operate on a stream of data. The pull function takes the input and returns both the output and the next input. If the input is &lt;em&gt;fully consumed&lt;&#x2F;em&gt;, the next input is empty. The basic parsers in most parser combinator libaries fall under this category.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Index&lt;&#x2F;strong&gt;-type parsers operate on a collection of data that can be indexed. The pull function takes both the input and an index as arguments, and returns the output. These kinds of parsers can decide whether they want to consume the input and return the next input along with the output, or leave the input untouched. Some examples are indexing an array or an object, or the &lt;code&gt;field&lt;&#x2F;code&gt; parser in Elm&#x27;s &lt;code&gt;Json.Decode&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Depending on the type of parser you&#x27;re using, some combinators may or may not be applicable. For example, sequencing combinators don&#x27;t make sense in consume- and index-type parsers. Some basic parsers such as &lt;code&gt;any&lt;&#x2F;code&gt; also cannot be implemented in index-type parsers because the pull function requires an index to be passed to it.&lt;&#x2F;p&gt;
&lt;p&gt;Looking back at Elm&#x27;s &lt;code&gt;Json.Decode&lt;&#x2F;code&gt; we can see that it uses both consume- and index-type parsers, one for parsing single JSON values and the other for parsing JSON objects, so all of these kinds of parsers can coexist with each other when parsing complex nested data.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thoughts&quot;&gt;Thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;I decided to finally write this post because I&#x27;ve been working on parsing the game files of the iOS version of Kamaitachi no Yoru, one of the first &quot;sound novels&quot;, which is a fancy name for a text CYOA game with some graphics and sound and music. The files are just HTML and JS, and I &lt;em&gt;could&lt;&#x2F;em&gt; just slap them into a web browser and do some hacks to get them working (which I sort of did already), but it&#x27;s much more interesting to parse them into a declarative format and do some fancy analysis on them. I could do some nice stuff like preload the next page&#x27;s background image and music, and some fancy stuff like adding an in-game flowchart like some of the other ports of Kamaitachi no Yoru did. I was getting a bit bored writing a recursive descent parser for this and then I remembered about parser combinators, so there you have it.&lt;&#x2F;p&gt;
&lt;p&gt;This post is a brain dump of all the stuff I&#x27;ve been thinking about while writing other parser combinators in the past. I wrote &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;xobl&#x2F;blob&#x2F;main&#x2F;patche&#x2F;xml.ml&quot;&gt;one that consumes streaming XML&lt;&#x2F;a&gt; and &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;nene&#x2F;blob&#x2F;master&#x2F;patche&#x2F;json.ml&quot;&gt;another one for JSON&lt;&#x2F;a&gt; and I thought they turned out pretty well, but figuring out how to classify and compose them required some more thinking, and this is it.&lt;&#x2F;p&gt;
&lt;p&gt;If you were expecting an implementation of all these things I just wrote about, too bad! Maybe I&#x27;ll write another post about it when I&#x27;m done writing the parser for these game files.&lt;&#x2F;p&gt;
&lt;p&gt;If you were expecting the announcement of a library of sorts, too bad! I don&#x27;t know if I have it in me to write a generally useful implementation of this. I&#x27;ll probably just write what I need for parsing those game files and forget about it. I&#x27;m not a library-writing kinda person.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m sure somebody else already thought of this stuff but I haven&#x27;t seen much about it on the internet, do tell me if you know of anything else. I&#x27;ve seen that F#&#x27;s &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;xparsec.corsis.tech&#x2F;&quot;&gt;XParsec&lt;&#x2F;a&gt; already implements some of this stuff but that&#x27;s about it. &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.cs.nott.ac.uk&#x2F;~pszgmh&#x2F;monparsing.pdf&quot;&gt;Hutton and Meijer (1996)&lt;&#x2F;a&gt; also mentions that &lt;em&gt;&quot;One could go further (as in (&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;www.cs.nott.ac.uk&#x2F;~pszgmh&#x2F;parsing.pdf&quot;&gt;Hutton, 1992&lt;&#x2F;a&gt;), for example) and abstract upon the type String of tokens&quot;&lt;&#x2F;em&gt;, I haven&#x27;t read Hutton 1992 yet but I guess I should.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>NixOps is easier than I thought</title>
          <pubDate>Thu, 11 Nov 2021 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/nixops-on-the-pi/</link>
          <guid>https://sgt.hootr.club/blog/nixops-on-the-pi/</guid>
          <description xml:base="https://sgt.hootr.club/blog/nixops-on-the-pi/">&lt;p&gt;TL;DR: if you have an underpowered machine or two in your house or a small server that you&#x27;re already managing using NixOS, and you find that running &lt;code&gt;nixos-rebuild&lt;&#x2F;code&gt; on it takes too long, you can easily keep your current configuration untouched and let a more beefy machine build it. Jump to the end of this post for a sample NixOps specification and instructions on how to use it.&lt;&#x2F;p&gt;
&lt;p&gt;Also, NixOps apparently has some &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=20324608&quot;&gt;issues&lt;&#x2F;a&gt; involving local state that make it hard to share a deployment with other machines. I just read about a tool called &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DBCDK&#x2F;morph&quot;&gt;morph&lt;&#x2F;a&gt; that is almost a drop-in replacement for NixOps and doesn&#x27;t share these issues. Sadly it is 3AM here and this here setup works well enough for me so I really can&#x27;t be bothered to check it out right now, but maybe at some point I will and maybe you might want to do it upfront. &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;christine.website&#x2F;blog&#x2F;morph-setup-2021-04-25&quot;&gt;This&lt;&#x2F;a&gt; is a good post about it.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I have a Raspberry Pi 3B+ sitting next to my router. I use it to host a couple things to my local network. Here&#x27;s a photo.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;nixops-on-the-pi&#x2F;pi.gif&quot; alt=&quot;The Pi 3 in its natural habitat.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That hunk of metal I just got also acts as a heatsink and it brought down the CPU temperature by 10°C. That&#x27;s pretty good!&lt;&#x2F;p&gt;
&lt;p&gt;You might remember it from &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;molten-matter&#x2F;nix-distributed-builds&#x2F;&quot;&gt;a previous post&lt;&#x2F;a&gt;, in which I talked about my experience with NixOS&#x27; distributed builds. It&#x27;s a slow machine and you really don&#x27;t want to build things directly on it, so at the time I reached for distributed builds to make the experience of rebuilding my configuration a little less painful. That worked out alright, but we can do better.&lt;&#x2F;p&gt;
&lt;p&gt;While distributed builds &lt;em&gt;do&lt;&#x2F;em&gt; make executing a &lt;code&gt;nixos-rebuild&lt;&#x2F;code&gt; much faster, the Nix expression describing the whole system is still evaluated on the Pi itself, which in the best case results in a virtually nonfunctional system for a couple minutes, and in the worst a slow death as swap fills out. I usually pull the plug when that happens because I can&#x27;t stand watching the poor thing suffer.&lt;&#x2F;p&gt;
&lt;p&gt;But! There is a fourth solution to this issue that I failed to consider on the previous post though: NixOps! The &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;releases.nixos.org&#x2F;nixops&#x2F;latest&#x2F;manual&#x2F;manual.html#chap-introduction&quot;&gt;NixOps user manual&lt;&#x2F;a&gt; describes it as:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;[...] a tool for deploying NixOS machines in a network or cloud.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This description initially put me off from using it. It makes it sound like something you&#x27;d only use when you have a bunch of servers, and that just using it for managing one machine would be overkill. It also makes it sound like there&#x27;s a big learning curve, I mean there&#x27;s a big one page html manual about it and ain&#x27;t nobody got the time to read all that. Figuring out Nix already took me long enough.&lt;&#x2F;p&gt;
&lt;p&gt;And surely I couldn&#x27;t just &lt;code&gt;import&lt;&#x2F;code&gt; the configuration I was using for the Pi and expect it to work with NixOps, right? I&#x27;d seen a couple posts about NixOps but they usually involved creating a new server with a new configuration that I didn&#x27;t really care about, so maybe I&#x27;d have to make some changes.&lt;&#x2F;p&gt;
&lt;p&gt;...Or &lt;em&gt;would I?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This might be a gross oversimplification, but all NixOps does is evaluate a system configuration on your machine, build it, copy the results on one or more target machines and make them switch to that configuration.&lt;&#x2F;p&gt;
&lt;p&gt;In other words, it&#x27;s like it runs &lt;code&gt;nixos-rebuild&lt;&#x2F;code&gt; using the Pi&#x27;s &lt;code&gt;&#x2F;etc&#x2F;nixos&#x2F;configuration.nix&lt;&#x2F;code&gt; but on another computer, and all the Pi has to do is download the results of the rebuild and run it. In yet other words, it does &lt;em&gt;exactly&lt;&#x2F;em&gt; what I needed.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I was initially worried that I&#x27;d have to make some changes to the Pi&#x27;s system configuration to deploy it with NixOps, but all I had to do was write a nix expression telling NixOps where to deploy the configuration and some details about the machine architecture, as described in the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;releases.nixos.org&#x2F;nixops&#x2F;latest&#x2F;manual&#x2F;manual.html#sec-deploying-to-physical-nixos&quot;&gt;user manual&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re following this at home, make sure to include &lt;em&gt;all&lt;&#x2F;em&gt; your configuration files when you import your existing one, including the &lt;code&gt;hardware-configuration.nix&lt;&#x2F;code&gt; file!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  network&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Raspberry Pi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  pi3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; lib&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;, ...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    deployment&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;targetHost&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;192.168.1.x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;localSystem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      system&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;aarch64-linux&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;aarch64-unknown-linux-gnu&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    imports&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      .&#x2F;pi-configuration.nix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To build an arm64 configuration on an x86_64 system you have to enable arm64 emulation, but I already had that set up from when I was using it for distributed builds, I think I got it &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;NixOS_on_ARM#Compiling_through_QEMU&quot;&gt;from here&lt;&#x2F;a&gt;. In any case, this is all you need to add to your &lt;em&gt;build machine&lt;&#x2F;em&gt;&#x27;s configuration.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  boot&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;binfmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;emulatedSystems&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;aarch64-linux&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then you have to allow your build machine to log into the target machine via SSH as root, install the &lt;code&gt;nixops&lt;&#x2F;code&gt; command and it&#x27;s just a couple of &lt;code&gt;nixops&lt;&#x2F;code&gt; commands to create the deployment and send it to the Pi.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; nixops create .&#x2F;rpi.nix -d rpi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; nixops deploy -d rpi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These commands will create a new deployment called &lt;code&gt;rpi&lt;&#x2F;code&gt; using the specification file above and deploy it. Not that hard after all!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Summing up, NixOS is really nice and you, hypothetical reader who isn&#x27;t using it, should try it. &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;christine.website&#x2F;talks&#x2F;nixos-pain-2021-11-10&quot;&gt;All issues with documentation and tooling notwithstanding&lt;&#x2F;a&gt;. Here&#x27;s a couple things that are tangentially related to this post.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Thanks to &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;christine.website&#x2F;&quot;&gt;@cadey&lt;&#x2F;a&gt; and probably a couple others I forget for posting about Nix stuff on Lobsters a lot, those posts got me to try NixOps out.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;kirarin.hootr.club&#x2F;git&#x2F;steinuil&#x2F;nixos-config&quot;&gt;This is my NixOS configuration&lt;&#x2F;a&gt; if you &lt;em&gt;really&lt;&#x2F;em&gt; want to look at it. The Gitea server it&#x27;s hosted on is defined in one of the configurations in there. Very meta. Maybe one day I&#x27;ll switch it to NixOps.&lt;&#x2F;li&gt;
&lt;li&gt;When I wrote &quot;It&#x27;s a slow machine&quot;, it reminded me of the similar line from Penny Lane by The Beatles and the song started playing in my head and it wouldn&#x27;t stop. Sadly I couldn&#x27;t think of a way to work it into the post.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Good night.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Hellpoint breach map</title>
          <pubDate>Sun, 20 Jun 2021 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/hellpoint-breach-map/</link>
          <guid>https://sgt.hootr.club/blog/hellpoint-breach-map/</guid>
          <description xml:base="https://sgt.hootr.club/blog/hellpoint-breach-map/">&lt;p&gt;I just finished playing Hellpoint, an indie soulslike which was surprisingly really good. It has a very deeply interconnected world which I loved exploring, but some areas are kinda confusing to navigate and I quickly realized I needed a map and the ones that were available online were... less than satisfactory. So I made one with Graphviz.&lt;&#x2F;p&gt;
&lt;p&gt;I did explore the game thoroughly but I didn&#x27;t manage to get the true ending due to a bug with an involved NPC, so it might be missing some things or not be entirely accurate (for example some of the edges between the areas are actually one-way, but I didn&#x27;t want to rewrite it into a digraph), but if you wanna know how to get to a specific breach you might find it useful.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;hellpoint-breach-map&#x2F;hellpoint.svg&quot; alt=&quot;Hellpoint breach map&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;graph &amp;quot;Hellpoint breach map&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph cluster_Arcology {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Arcology&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Breaches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Funicular [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Customs Bridge&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Celestial Workshop&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;West Square&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Condo Ruins&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Lifts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subgraph cluster_lift0 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l0f1 -- l0f2;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l0f2 [label = &amp;quot;Floor 2&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l0f1 [label = &amp;quot;Floor 1&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subgraph cluster_lift1 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l1f1 -- l1f2 -- l1f3;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l1f3 [label = &amp;quot;Floor 3&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l1f2 [label = &amp;quot;Floor 2&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l1f1 [label = &amp;quot;Floor 1&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Links&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l0f1 -- Funicular -- &amp;quot;Customs Bridge&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Customs Bridge&amp;quot; -- l1f2 -- &amp;quot;Celestial Workshop&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Cosmic theurgist&amp;quot; -- &amp;quot;Celestial Workshop&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Eye codes&amp;quot; -- l1f3;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Customs Bridge&amp;quot; -- &amp;quot;West Square&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;West Square&amp;quot; -- &amp;quot;Condo Ruins&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph &amp;quot;cluster_Alma Mater Atrium&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Alma Mater Atrium&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Breaches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Otsego Mansion&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Shanty Entrance&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Western Skybridge&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Tenements&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Union Park&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Athaneum&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Cabaret Cellar&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Lifts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subgraph cluster_lift3 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l3f1 -- l3f2 -- l3f3;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l3f3 [label = &amp;quot;Floor 3&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l3f2 [label = &amp;quot;Floor 2&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l3f1 [label = &amp;quot;Floor 1&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subgraph cluster_lift6 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l6f2 -- l6f1;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l6f2 [label = &amp;quot;Floor 2&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l6f1 [label = &amp;quot;Floor 1&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subgraph cluster_Lift9 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l9f1 -- l9f2 -- l9f3;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l9f3 [label = &amp;quot;Floor 3&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l9f2 [label = &amp;quot;Floor 2&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l9f1 [label = &amp;quot;Floor 1&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Links&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l3f3 -- &amp;quot;Shanty Entrance&amp;quot; -- &amp;quot;Western Skybridge&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l3f2 -- &amp;quot;Otsego Mansion&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l6f2 -- &amp;quot;Tenements&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l6f1 -- &amp;quot;Union Park&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l9f2 -- &amp;quot;Otsego Mansion&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l9f2 -- &amp;quot;Cabaret Cellar&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Union Park&amp;quot; -- &amp;quot;Otsego Mansion&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Union Park&amp;quot; -- &amp;quot;Athaneum&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph cluster_lift2 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l2f1 -- l2f2;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l2f2 [label = &amp;quot;Floor 2&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l2f1 [label = &amp;quot;Floor 1&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph &amp;quot;cluster_Arcology Underside&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Arcology Underside&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Breaches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Central Mall&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;SAS&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Outside Platform&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Lifts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subgraph cluster_lift7 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l7f1 -- l7f2;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l7f2 [label = &amp;quot;Floor 2&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l7f1 [label = &amp;quot;Floor 1&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Central Mall&amp;quot; -- l7f2;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l7f1 -- &amp;quot;SAS&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;SAS&amp;quot; -- &amp;quot;Outside Platform&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph &amp;quot;cluster_Alma Mater&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Alma Mater&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Breaches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Belvedere&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Offices&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Lobby&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Uthos&amp;#39;s Agora&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Lifts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subgraph cluster_lift4 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l4f1 -- l4f2;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l4f2 [label = &amp;quot;Floor 2&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l4f1 [label = &amp;quot;Floor 1&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subgraph cluster_lift5 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l5f1 -- l5f2;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l5f2 [label = &amp;quot;Floor 2&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l5f1 [label = &amp;quot;Floor 1&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Links&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l4f1 -- &amp;quot;Belvedere&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Offices&amp;quot; -- &amp;quot;Belvedere&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l4f2 -- &amp;quot;Uthos&amp;#39;s Agora&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l5f2 -- &amp;quot;Offices&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l5f1 -- &amp;quot;Lobby&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph &amp;quot;cluster_Sohn District&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Sohn District&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Breaches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Corpse Pit&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Balcony&amp;quot; [shape = diamond]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Control Center&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Links&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Corpse Pit&amp;quot; -- &amp;quot;Control Center&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Corpse Pit&amp;quot; -- &amp;quot;Balcony&amp;quot; -- &amp;quot;Control Center&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph &amp;quot;cluster_Port Issoudun&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Port Issoudun&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Breaches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Docking Bay&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Silos&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Ozy&amp;#39;s Pit&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Links&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Docking Bay&amp;quot; -- &amp;quot;Silos&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Silos&amp;quot; -- &amp;quot;Ozy&amp;#39;s Pit&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph cluster_Observatory {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Observatory&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Braches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Podium&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph cluster_Embassy {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Embassy&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Breaches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Great Halls&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Pond&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Admission&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    j0 [shape = point];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Links&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Great Halls&amp;quot; -- j0 -- &amp;quot;Pond&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Great Halls&amp;quot; -- &amp;quot;Admission&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph &amp;quot;cluster_Arisen Dominion&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Arisen Dominion&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Breaches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Solar Promenade&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Hall of Remembrance&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Grand Gallery&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Tram Station&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Mine Mausoleum&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Undisturbed Defas Nemundis&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Lifts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subgraph cluster_lift8 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      label = &amp;quot;Lift&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l8f1 -- l8f2 -- l8f3 -- l8f4;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l8f4 [label = &amp;quot;Ministry Audience Chamber&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l8f3 [label = &amp;quot;Grand Gallery&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l8f2 [label = &amp;quot;Promenade&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      l8f1 [label = &amp;quot;Mausoleum&amp;quot;, shape = none];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Links&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Solar Promenade&amp;quot; -- &amp;quot;Hall of Remembrance&amp;quot; -- &amp;quot;Grand Gallery&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Tram Station&amp;quot; -- &amp;quot;Grand Gallery&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Grand Gallery&amp;quot; -- l8f3;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Mine Mausoleum&amp;quot; -- l8f1;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    l8f4 -- &amp;quot;Undisturbed Defas Nemundis&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subgraph &amp;quot;cluster_Ikari Walkways&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    label = &amp;quot;Ikari Walkways&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Breaches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;East Square&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Crashed Tram&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;Underail&amp;quot; [shape = diamond];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;&#x2F; Links&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;East Square&amp;quot; -- &amp;quot;Crashed Tram&amp;quot; -- &amp;quot;Underail&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  j0 -- &amp;quot;Solar Promenade&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Customs Bridge&amp;quot; -- &amp;quot;East Square&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Tenements&amp;quot; -- &amp;quot;Tram Station&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  l2f1 -- &amp;quot;Central Mall&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  l2f2 -- &amp;quot;Otsego Mansion&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  l3f1 -- &amp;quot;Central Mall&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Otsego Mansion&amp;quot; -- &amp;quot;Celestial Workshop&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Western Skybridge&amp;quot; -- l4f1;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Offices&amp;quot; -- &amp;quot;Tenements&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Lobby&amp;quot; -- &amp;quot;Otsego Mansion&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Podium&amp;quot; -- &amp;quot;Great Halls&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Podium&amp;quot; -- &amp;quot;Docking Bay&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Podium&amp;quot; -- l0f2;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Podium&amp;quot; -- &amp;quot;Outside Platform&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Control Center&amp;quot; -- &amp;quot;Docking Bay&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Control Center&amp;quot; -- l1f1;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Condo Ruins&amp;quot; -- &amp;quot;Corpse Pit&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Underail&amp;quot; -- &amp;quot;Central Mall&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Underail&amp;quot; -- l9f2;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Underail&amp;quot; -- &amp;quot;Cabaret Cellar&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;Ozy&amp;#39;s Pit&amp;quot; -- &amp;quot;Pond&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</description>
      </item>
      <item>
          <title>Offloading NixOS builds to a faster machine</title>
          <pubDate>Sun, 24 Jan 2021 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/nix-distributed-builds/</link>
          <guid>https://sgt.hootr.club/blog/nix-distributed-builds/</guid>
          <description xml:base="https://sgt.hootr.club/blog/nix-distributed-builds/">&lt;p&gt;I have a Raspberry Pi 3 sitting next to my router, from which I host a couple things to my local network. I installed NixOS on it and I love the experience: it completely removes the absolute nightmare that is configuring a Linux machine, and I find that incredibly liberating.&lt;&#x2F;p&gt;
&lt;p&gt;But there&#x27;s a catch: the Pi is slow and it doesn&#x27;t have a lot of memory. Just evaluating the configuration with &lt;code&gt;nixos-rebuild&lt;&#x2F;code&gt; takes about a minute even when there haven&#x27;t been any changes, and compiling anything substantial is usually a recipe for Death By Swap.&lt;&#x2F;p&gt;
&lt;p&gt;The other day I tried updating the main &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Nix_channels&quot;&gt;channel&lt;&#x2F;a&gt; (the package repository) and upgrading my packages with &lt;code&gt;nixos-rebuild switch --upgrade&lt;&#x2F;code&gt; and found out that one of the packages I was using wasn&#x27;t available on the main &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Binary_Cache&quot;&gt;binary cache&lt;&#x2F;a&gt; at &lt;code&gt;cache.nixos.org&lt;&#x2F;code&gt; anymore, so it had to be built locally. The program in question is written in Rust and has a pretty sizeable dependency graph. I left it to build overnight. The morning after it was still stuck on building one of the sub-packages, and I couldn&#x27;t even open a new ssh connection to the Pi.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a couple solutions to this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Build the program on another machine and add a new package that just fetches this binary and patches it&lt;&#x2F;li&gt;
&lt;li&gt;Setting up a binary cache on another machine&lt;&#x2F;li&gt;
&lt;li&gt;Setting up distributed builds&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The first one is an ugly hack (in Nix terms, at least) and would probably end up being more trouble than it&#x27;s worth.&lt;&#x2F;p&gt;
&lt;p&gt;The second option, setting up another binary cache, is definitely better.  The &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Binary_Cache&quot;&gt;wiki page&lt;&#x2F;a&gt; does a good job of explaining how to set up the server, and on the client you just need to add a couple lines to your &lt;code&gt;configuration.nix&lt;&#x2F;code&gt; to enable it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    binaryCaches&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;http:&#x2F;&#x2F;&amp;lt;server url&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    binaryCachePublicKeys&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;lt;the cache&amp;#39;s public key&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The downside is that you have to know beforehand which packages you need to build, and not being very well-versed in Nix I couldn&#x27;t figure out how to build the specific versions I needed.&lt;&#x2F;p&gt;
&lt;p&gt;So we&#x27;re left with the third option, distributed builds. The &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Distributed_build&quot;&gt;wiki page&lt;&#x2F;a&gt; opens with this paragraph:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sometimes you want to use a faster machine for building a nix derivation you want to use on a slower one. If you have ssh access to a machine where Nix (not necessarily NixOS) is installed, then you can offload building to this machine.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Which seems to be exactly what I&#x27;m looking for.&lt;&#x2F;p&gt;
&lt;p&gt;The wiki mentions a couple options to add in &lt;code&gt;configuration.nix&lt;&#x2F;code&gt; to enable offloading builds to the client. I used these on the Pi:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    distributedBuilds&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    buildMachines&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        hostName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;builder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        systems&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;x86_64-linux&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;aarch64-linux&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        maxJobs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        speedFactor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;        supportedFeatures&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;nixos-test&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;benchmark&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;big-parallel&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;kvm&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  programs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;ssh&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;extraConfig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    Host builder&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      HostName &amp;lt;url of the host&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      Port 2222&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      User builder&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      IdentitiesOnly yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      IdentityFile &#x2F;root&#x2F;.ssh&#x2F;id_builder&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I created the key in &lt;code&gt;&#x2F;root&#x2F;.ssh&#x2F;id_builder&lt;&#x2F;code&gt; using the options recommended in &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;security.stackexchange.com&#x2F;a&#x2F;144044&quot;&gt;this Stack Exchange answer&lt;&#x2F;a&gt; (not that it matters since it&#x27;s all on my local network):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; ssh-keygen -t ed25519 -a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; -f &#x2F;root&#x2F;.ssh&#x2F;id_builder&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For the host machine I had to go with emulation, since I don&#x27;t have another ARM64 machine lying around. My beefy desktop runs Windows so I created a VirtualBox machine with 4 cores, 8GB of memory, and a couple GB of hard drive space to store the build results. Setting up emulation was incredibly easy after finding &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;NixOS_on_ARM#Compiling_through_QEMU&quot;&gt;the NixOS on ARM wiki page&lt;&#x2F;a&gt;, all it takes is one line in the VM&#x27;s &lt;code&gt;configuration.nix&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  boot&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;binfmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;emulatedSystems&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;aarch64-linux&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then I had to activate OpenSSH with &lt;code&gt;services.openssh.enable = true;&lt;&#x2F;code&gt;, add the Pi&#x27;s public key to &lt;code&gt;&#x2F;home&#x2F;builder&#x2F;.ssh&#x2F;authorized_keys&lt;&#x2F;code&gt;, forward the SSH port on the VM to 2222, and open that port in the Windows firewall.&lt;&#x2F;p&gt;
&lt;p&gt;To test that it&#x27;s working you can try building a package on the client with the &lt;code&gt;max-jobs&lt;&#x2F;code&gt; option set to 0.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; nix-build -j0 --expr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;with import &amp;lt;nixpkgs&amp;gt; {}; callPackage .&#x2F;default.nix {}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;m really surprised by how simple all of that was. Setting up a VM that automatically builds packages for another machine while emulating another architecture sounds like a nightmare, but with NixOS it&#x27;s a couple lines of configuration.&lt;&#x2F;p&gt;
&lt;p&gt;And in case you&#x27;re wondering, yes, it worked beautifully, and even though the emulation slows things down quite a bit it&#x27;s still much faster than building things directly on the Pi.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>require_so: DRYer StackOverflow copying</title>
          <pubDate>Wed, 20 May 2020 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/announcing-require-so/</link>
          <guid>https://sgt.hootr.club/blog/announcing-require-so/</guid>
          <description xml:base="https://sgt.hootr.club/blog/announcing-require-so/">&lt;p&gt;Today I was reading &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.blog&#x2F;2020&#x2F;05&#x2F;20&#x2F;good-coders-borrow-great-coders-steal&#x2F;?cb=1&quot;&gt;a post on the StackOverflow blog&lt;&#x2F;a&gt;
when I was struck by this passage:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Copying code from Stack Overflow is a form of code cloning&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Code &lt;strong&gt;cloning&lt;&#x2F;strong&gt;, you say? In &lt;strong&gt;my&lt;&#x2F;strong&gt; DRY Ruby codebase? Not if &lt;em&gt;I&lt;&#x2F;em&gt; can help it!&lt;&#x2F;p&gt;
&lt;p&gt;And so I got to work. After extensive research, I concluded that deleting the snippet from StackOverflow
to make my version the canonical one was not a viable option.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;announcing-require-so&#x2F;how-to-delete.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So I flipped the problem on its head: why not make the StackOverflow version the canonical one?
And thus, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;require_so&quot;&gt;require_so&lt;&#x2F;a&gt; was born.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-do-i-use-it&quot;&gt;How do I use it?&lt;&#x2F;h2&gt;
&lt;p&gt;Locate the code snippet&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;announcing-require-so&#x2F;answer.jpg&quot; alt=&quot;It&amp;#39;s right there&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Copy the short permalink below the StackOverflow answer&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;announcing-require-so&#x2F;share.jpg&quot; alt=&quot;Copy that shit&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Paste it into your code&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;require_so&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;require_so &lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;61879644&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And voilà, the methods defined in the snippet will be brought into scope.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;fast_next_smaller&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;907&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; #=&amp;gt; 790&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No more code cloning!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Announcing SatouinDp</title>
          <pubDate>Tue, 05 May 2020 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/announcing-satouindp/</link>
          <guid>https://sgt.hootr.club/blog/announcing-satouindp/</guid>
          <description xml:base="https://sgt.hootr.club/blog/announcing-satouindp/">&lt;p&gt;My desktop is connected to two monitors and one TV, and for various reasons I find myself needing to switch my primary display more often than most people.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;announcing-satouindp&#x2F;battlestation.jpg&quot; alt=&quot;My battlestation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is my monitor setup. There&#x27;s also a TV behind me, which I use to watch movies and play videogames. My laptop is also connected to the central monitor.&lt;&#x2F;p&gt;
&lt;p&gt;When I&#x27;m working on my laptop I usually have my central monitor rotated by 90° (code looks better on a tall screen) and a music player open on the right monitor, so either I bend my neck 90° every time I need to do something on the central monitor, or I switch the primary display to the right. If I want to play videogames on the TV I need to switch the primary display to the TV, because most games don&#x27;t have an option to change the display they go full screen on.&lt;&#x2F;p&gt;
&lt;p&gt;To change primary displays on Windows you have to go on a certain control panel section whose name I can never reliably recall when I&#x27;m searching for it from the start menu. (Doesn&#x27;t help that they change the names of the control panel sections every other update.) Then you have to click on the display you want to make primary, scroll down, and click on a checkbox. That&#x27;s usually enough to put me off playing any videogames on the TV.&lt;&#x2F;p&gt;
&lt;p&gt;I needed something to do this more quickly.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s probably a bunch of tools that already do this, but I don&#x27;t think any of them are open source. If this was Linux I could easily write a script with xrandr, but this is Windows. Stuff here goes clickety click instead of clickety clack, and opening a terminal breaks the harmony.&lt;&#x2F;p&gt;
&lt;p&gt;The other day I finally decided to scratch this itch. A couple hours later I emerged with &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;SatouinDp&quot;&gt;SatouinDp&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;announcing-satouindp&#x2F;satouin.jpg&quot; alt=&quot;SatouinDp in action&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s simply a taskbar icon that shows the list of displays, and when you click on one it sets that one as the primary display. It&#x27;s written in F# and it calls directly to the Win32 API to change the display settings. Maybe I&#x27;ll write a post about how to do that too some other day.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s been very useful so far.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;further-reading&quot;&gt;Further reading&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20040411202042&#x2F;http:&#x2F;&#x2F;www.shirky.com&#x2F;writings&#x2F;situated_software.html&quot;&gt;Situated software&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;195267&#x2F;use-windows-api-from-c-sharp-to-set-primary-monitor&quot;&gt;Use Windows API from C# to set primary monitor&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>The journey of packaging a .NET app on Nix</title>
          <pubDate>Wed, 01 Apr 2020 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/dotnet-on-nix/</link>
          <guid>https://sgt.hootr.club/blog/dotnet-on-nix/</guid>
          <description xml:base="https://sgt.hootr.club/blog/dotnet-on-nix/">&lt;p&gt;Me and a few friends have a Discord server where we all gather once a week (or even twice lately, due to recent events) to watch a movie together. It&#x27;s been going on for about three and a half years and we thought it&#x27;d be about time to make a bot that handles voting and backlog and times for us, so we started writing one, in F#.&lt;&#x2F;p&gt;
&lt;p&gt;My Raspberry Pi running NixOS is on 24&#x2F;7, so I thought I could run the bot from there and learn something about packaging on Nix while doing so.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;&#x2F;strong&gt;: I have very little experience with packaging in Nix. If you spot any mistakes please tell me and I&#x27;ll correct them!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;First I thought I could cross-compile the bot and then just run the compiled version, so I wouldn&#x27;t have to bother with packaging the dependencies. Cross-compiling a .NET Core program using the &lt;code&gt;dotnet&lt;&#x2F;code&gt; cli is very easy, you just have to specify the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-us&#x2F;dotnet&#x2F;core&#x2F;rid-catalog&quot;&gt;runtime identifier&lt;&#x2F;a&gt; and use the &lt;code&gt;--self-contained&lt;&#x2F;code&gt; flag so the target machine doesn&#x27;t need to have the .NET runtime installed to run it.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; dotnet publish --self-contained -r linux-arm64 -c Release&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I sent the output to the pi and inspected it with &lt;code&gt;ldd&lt;&#x2F;code&gt;. Running binaries on NixOS is &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Packaging&#x2F;Binaries&quot;&gt;not as easy&lt;&#x2F;a&gt; as on other Linux distros, because the paths to the dynamically loaded libraries are not predictable, so those hardcoded in the source are usually wrong.&lt;&#x2F;p&gt;
&lt;p&gt;I tried to patch the binary with &lt;code&gt;patchelf&lt;&#x2F;code&gt; but didn&#x27;t have much luck; even when I did manage to make it run it just printed this message:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;No usable version of libssl was found&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then dumped core. Later I learned that I could probably have avoided almost everything below this point, but at the moment I didn&#x27;t so I decided to just do it the hard way.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-first-derivation&quot;&gt;The first derivation&lt;&#x2F;h2&gt;
&lt;p&gt;I cloned the repository on the Pi and quickly discovered that the &lt;code&gt;dotnet-sdk&lt;&#x2F;code&gt; package did not support arm64. This was easy enough to fix; I downloaded the .nix file and modified the URL and the hash to point to the &lt;code&gt;linux-arm64&lt;&#x2F;code&gt; version of the SDK. (I promise I&#x27;ll upstream support for arm64 eventually, but for the moment it&#x27;s just on my machine.)&lt;&#x2F;p&gt;
&lt;p&gt;It worked well enough; I could build the bot. So I tried to make a simple derivation for it.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt; stdenv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; libunwind&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; openssl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; icu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; libuuid&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; zlib&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; callPackage&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; dotnet-sdk&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  rpath&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; stdenv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;makeLibraryPath&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    stdenv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;cc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;cc libunwind libuuid icu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    openssl zlib curl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  dynamicLinker&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; stdenv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;cc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;bintools&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;dynamicLinker&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; stdenv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mkDerivation&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; rec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  pname&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;kino-bot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;2020-03-29&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  src&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = builtins.&lt;&#x2F;span&gt;&lt;span&gt;fetchGit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;pname&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;-git&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;KinoBot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;master&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    rev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;275ae0447ab1ab21cba76bb673f00559ed5d9251&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  buildInputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;span&gt; dotnet-sdk&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  buildPhase&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    export DOTNET_CLI_TELEMETRY_OPTOUT=1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    export HOME=&amp;quot;$(mktemp -d)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    dotnet publish --nologo \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -r linux-arm64 --self-contained \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -c Release -o out&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  installPhase&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    runHook preInstall&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    mkdir -p $out&#x2F;bin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    cp -r .&#x2F;out&#x2F;* $out&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    ln -s $out&#x2F;KinoBot $out&#x2F;bin&#x2F;KinoBot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    runHook postInstall&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  dontPatchELF&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  postFixup&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    patchelf --set-interpreter &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;dynamicLinker&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      --set-rpath &amp;#39;$ORIGIN:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;rpath&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;#39; $out&#x2F;KinoBot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    find $out -type f -name &amp;quot;*.so&amp;quot; -exec \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      patchelf --set-rpath &amp;#39;$ORIGIN:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;rpath&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&amp;#39; {} &amp;#39;;&amp;#39;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  meta&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = with&lt;&#x2F;span&gt;&lt;span&gt; stdenv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;lib;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    homepage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;KinoBot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    platforms&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;aarch64-linux&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    license&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; licenses&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;isc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(I took inspiration from &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;jb55&#x2F;b8b49893e18b61fb8c3ea3c924358278&quot;&gt;this gist&lt;&#x2F;a&gt; and a few other derivations I found to come up with this.)&lt;&#x2F;p&gt;
&lt;p&gt;So I tried to run it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ nix repl &amp;#39;&amp;lt;nixpkgs&amp;gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Welcome to Nix version 2.3.3. Type :? for help.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Loading &amp;#39;&amp;lt;nixpkgs&amp;gt;&amp;#39;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Added 10863 variables.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;nix-repl&amp;gt; kino-bot = callPackage (import .&#x2F;kino-bot.nix) {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;nix-repl&amp;gt; :b kino-bot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But it got stuck on the &lt;code&gt;dotnet restore&lt;&#x2F;code&gt; step of the build. I discovered that external connections are not allowed during the build step of a Nix derivation, so I had to fetch the dependencies &lt;em&gt;through Nix&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;packaging-the-dependencies-and-a-digression-on-base32-hashes&quot;&gt;Packaging the dependencies and a digression on base32 hashes&lt;&#x2F;h2&gt;
&lt;p&gt;It turns out the &lt;code&gt;dotnet&lt;&#x2F;code&gt; command takes a &lt;code&gt;--source&lt;&#x2F;code&gt; argument which lets you specify a folder containing the NuGet packages.  I started by copying the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;jb55&#x2F;b8b49893e18b61fb8c3ea3c924358278&quot;&gt;aforementioned gist&lt;&#x2F;a&gt;, which got the list of all direct and transitive dependencies from the &lt;code&gt;obj&#x2F;project.assets.json&lt;&#x2F;code&gt; file. I didn&#x27;t want to install Node though, so I rewrote the script in F#.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a problem with the script though: the dependencies file specifies a base64-encoded sha512 hash which doesn&#x27;t correspond to the hash of the zip file, and probably not to the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.mankier.com&#x2F;1&#x2F;nix-hash&quot;&gt;Nix serialization of the path&lt;&#x2F;a&gt; either.&lt;&#x2F;p&gt;
&lt;p&gt;The hashes that Nix uses are also not at all like the ones you see in the wild; they use too many characters for hex, but also too few for base64. In fact Nix uses its own version of base32, more compact than base16 but still only containing ASCII digits and lowercase letters (except e o u t, which were chosen &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;discourse.nixos.org&#x2F;t&#x2F;no-hashes-starting-with-e-t-o-or-u-in-nix-store&#x2F;4906&#x2F;7&quot;&gt;to reduce the chance of the hash containing swearwords&lt;&#x2F;a&gt;). The implementation is specified in &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nix&#x2F;blob&#x2F;master&#x2F;src&#x2F;libutil&#x2F;hash.cc#L76&quot;&gt;src&#x2F;libutil&#x2F;hash.cc&lt;&#x2F;a&gt; and it&#x27;s very compact and easily ported to other languages. This is my F# implementation:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt; Base32&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; chars&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;0123456789abcdfghijklmnpqrsvwxyz&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; length size &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;size &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;) &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; fromBytes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;bytes &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; byte&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;[]) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    seq {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        for&lt;&#x2F;span&gt;&lt;span&gt; n &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; length bytes.Length &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; downto&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; n &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; b &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; b &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;            yield&lt;&#x2F;span&gt;&lt;span&gt; int bytes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.[&lt;&#x2F;span&gt;&lt;span&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;] &amp;gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; j &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;||| if&lt;&#x2F;span&gt;&lt;span&gt; i &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;=&lt;&#x2F;span&gt;&lt;span&gt; bytes.Length &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; then&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; int bytes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.[&lt;&#x2F;span&gt;&lt;span&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;] &amp;lt;&amp;lt;&amp;lt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Seq.map &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;(fun&lt;&#x2F;span&gt;&lt;span&gt; c &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; chars&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.[&lt;&#x2F;span&gt;&lt;span&gt;c &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;amp;&amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0x1f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Seq.toArray&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; System.String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You don&#x27;t really have to use base32, as Nix also supports base16-encoded hashes, but I thought it&#x27;d be fun to try implementing it on my own.&lt;&#x2F;p&gt;
&lt;p&gt;But let&#x27;s go back to the dependencies. After some head scratching because &lt;code&gt;nix-hash&lt;&#x2F;code&gt; apparently returned a different hash for a dependency downloaded through curl than for one downloaded through &lt;code&gt;nix-prefetch-url&lt;&#x2F;code&gt; I figured I just had to pass the &lt;code&gt;-L&lt;&#x2F;code&gt; flag to curl to follow the redirect, and then the hashes were identical.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; nix-prefetch-url https:&#x2F;&#x2F;www.nuget.org&#x2F;api&#x2F;v2&#x2F;package&#x2F;Argu&#x2F;6.0.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt; MiB DL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; is&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;nix&#x2F;store&#x2F;rn0qb89ibmn3xv7ay28309r0wj3xaf5q-6.0.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;1zybqx0ka89s2cxp7y2bc9bfiy9mm3jn8l3593f58z6nshwh3f2j&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;# WRONG&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; curl -o Argu.6.0.0.zip https:&#x2F;&#x2F;www.nuget.org&#x2F;api&#x2F;v2&#x2F;package&#x2F;Argu&#x2F;6.0.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; nix-hash --type sha256 --flat --base32 .&#x2F;Argu.6.0.0.zip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;04w8jx2wzss3y2c9bx6dm6lxib03v2jnr89iakcgk93zippfxb0w&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; curl -L -o Argu.6.0.0.zip https:&#x2F;&#x2F;www.nuget.org&#x2F;api&#x2F;v2&#x2F;package&#x2F;Argu&#x2F;6.0.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; nix-hash --type sha256 --flat --base32 .&#x2F;Argu.6.0.0.zip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;1zybqx0ka89s2cxp7y2bc9bfiy9mm3jn8l3593f58z6nshwh3f2j&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And there I was with my newly created &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;discourse.nixos.org&#x2F;&quot;&gt;discourse.nixos.org&lt;&#x2F;a&gt; account ready to send a post demanding explanations. Oh well!&lt;&#x2F;p&gt;
&lt;p&gt;I had to also write my own version of &lt;code&gt;fetchNuGet&lt;&#x2F;code&gt; because the default one tried to build the artifacts again for some reason, didn&#x27;t support sha512, and used mono which I&#x27;m not using. I used the same gist as above for inspiration.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt; baseName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; sha512&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; nupkgName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; lib&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;strings&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;toLower&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;baseName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.nupkg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;; in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  stdenvNoCC&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mkDerivation&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;baseName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    src&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; fetchurl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      inherit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; sha512&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;https:&#x2F;&#x2F;www.nuget.org&#x2F;api&#x2F;v2&#x2F;package&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;baseName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;baseName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.zip&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    sourceRoot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    buildInputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;span&gt; unzip&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    dontStrip&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    installPhase&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      mkdir -p $out&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      chmod +r *.nuspec&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      cp *.nuspec $out&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      cp $src $out&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;nupkgName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;nuget2nix&lt;&#x2F;code&gt; script generates a file that looks roughly like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; rec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; linkFarm&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;-nuget-pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; packages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  packages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Argu&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      path&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; fetchNuGet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;       baseName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;Argu&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;       version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;6.0.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;       sha512&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;1kiqh4zpasydq5vx4wn5mal5v8c2bdalczja5za9phvq8n9c3s453lj5kmrqar1rfp3504kakb5csxflj7dwy2aas04d0jjw9dhm9g2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    # ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;linkFarm&lt;&#x2F;code&gt; function, which is only documented in a &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;blob&#x2F;master&#x2F;pkgs&#x2F;build-support&#x2F;trivial-builders.nix#L303&quot;&gt;comment in the source&lt;&#x2F;a&gt; (Nix has a recurring problem with documentation, yes) takes every derivation in its second arguments and links it as a subdirectory into a derivation named after the first, which is exactly what I needed for the &lt;code&gt;--source&lt;&#x2F;code&gt; directory in the restore step of the build.&lt;&#x2F;p&gt;
&lt;p&gt;Before going back to the main file, a few protips about the packages, because these things got me stuck for a while:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If you source the packages from your &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;devblogs.microsoft.com&#x2F;nuget&#x2F;enable-repeatable-package-restores-using-a-lock-file&#x2F;&quot;&gt;lockfile&lt;&#x2F;a&gt; you will have to set the &lt;code&gt;RuntimeIdentifiers&lt;&#x2F;code&gt; property in your &lt;code&gt;.[cf]sproj&lt;&#x2F;code&gt;, or else you&#x27;ll be missing some platform-specific ones like &lt;code&gt;runtime.native.System.Security.Cryptography.OpenSsl&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Make sure you don&#x27;t download the same dependency twice, or you&#x27;ll get an error in the &lt;code&gt;ln&lt;&#x2F;code&gt; phase of the &lt;code&gt;linkFarm&lt;&#x2F;code&gt; derivation saying &quot;Permission denied&quot;.&lt;&#x2F;li&gt;
&lt;li&gt;There&#x27;s a few dependencies listed under &lt;code&gt;project.frameworks.&amp;lt;yourTargetFramework&amp;gt;.downloadDependencies&lt;&#x2F;code&gt; in the &lt;code&gt;obj&#x2F;project.assets.json&lt;&#x2F;code&gt; file that you&#x27;ll also have to include in the build. These are the ones called like &lt;code&gt;Microsoft.NETCore.App.Runtime.linux-arm64&lt;&#x2F;code&gt; and so on.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;finally-running-the-bot&quot;&gt;Finally running the bot&lt;&#x2F;h2&gt;
&lt;p&gt;Nothing much has changed in the main derivation. I just added the link farm derivation to the list of dependencies and set it as source in the &lt;code&gt;dotnet publish&lt;&#x2F;code&gt; command.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  rpath&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  nugetPkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; callPackage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; .&#x2F;kino-bot-nuget.nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {} &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;kino-bot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; stdenv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mkDerivation&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; rec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;  # ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  buildInputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;span&gt; dotnet-sdk nugetPkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  buildPhase&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    export DOTNET_CLI_TELEMETRY_OPTOUT=1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    export HOME=&amp;quot;$(mktemp -d)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    dotnet publish --nologo \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      -r linux-arm64 --self-contained \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;      --source #{nugetPkgs.cache} -c Release -o out&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;  # ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After all this I could finally build the package locally, but when I tried to run it I got the same libssl error as in the beginning. Was this all for naught? (Maybe it was.)&lt;&#x2F;p&gt;
&lt;p&gt;Turns out .NET Core &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;51901359&#x2F;net-core-2-1-sdk-linux-x64-no-usable-version-of-the-libssl-was-found&quot;&gt;only supports version 1.0 of openssl&lt;&#x2F;a&gt;, and the version packaged by Nix is 1.1. This is easily fixed by importing &lt;code&gt;openssl_1_0_2&lt;&#x2F;code&gt; instead of &lt;code&gt;openssl&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   rpath = stdenv.lib.makeLibraryPath [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     stdenv.cc.cc libunwind libuuid icu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;    openssl_1_0_2 zlib curl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BF616A;&quot;&gt;    openssl zlib curl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;adding-the-package-to-your-system&quot;&gt;Adding the package to your system&lt;&#x2F;h2&gt;
&lt;p&gt;Now that I got it running I had to add it to the system, and to do this you need &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Overlays&quot;&gt;overlays&lt;&#x2F;a&gt;. An overlay is just a function that takes two arguments, named self and super, and returns a set of packages. This is what mine looks like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; super&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  dotnet-sdk&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; super&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;callPackage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; .&#x2F;pkgs&#x2F;dotnet-sdk.nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  kino-bot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; super&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;callPackage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; .&#x2F;pkgs&#x2F;kino-bot.nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then I imported it to the main &lt;code&gt;configuration.nix&lt;&#x2F;code&gt; file. (Note the parenthesis around the import: Nix will throw &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;discourse.nixos.org&#x2F;t&#x2F;infinite-recursion-encountered-at-undefined-position&#x2F;3039&#x2F;13?u=steinuil&quot;&gt;a cryptic infinite recursion error&lt;&#x2F;a&gt; with no stack trace if you forget them!)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;overlays&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; .&#x2F;my-overlay.nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  environment&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;systemPackages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = with&lt;&#x2F;span&gt;&lt;span&gt; pkgs;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;    # ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    kino-bot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also added a systemd service to start it automatically.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  systemd&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;kino-bot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    enable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    after&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;network.target&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    wantedBy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [ &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;multi-user.target&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    serviceConfig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      Restart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;on-failure&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;      ExecStart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;kino-bot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;&#x2F;bin&#x2F;KinoBot --token &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;secrets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;kinoBotToken&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;This took me a few days to get working, and I had to rebuild everything dozens of times to get it working. I omitted several dumb mistakes I made and only kept in those that I had the most trouble with because I thought they could help others.&lt;&#x2F;p&gt;
&lt;p&gt;Nonetheless, I really like NixOS and I&#x27;ll definitely be using it more and package more things in the future. It&#x27;s already my main Linux OS on my (personal) laptop and my Raspberry Pi (I use Windows on my desktop and work laptop, sadly) and this was a good occasion to learn more about how its packages work. I&#x27;ll probably be migrating my server to it too in the future.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;steinuil&#x2F;11c7565253e4af03658f59c9c331d268&quot;&gt;Code for this post here&lt;&#x2F;a&gt;. &lt;em&gt;Note: there&#x27;s many hacks specific to my use-case left in the code and it&#x27;s probably not usable as-is.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;nixos&#x2F;nix-pills&#x2F;index.html&quot;&gt;Nix pills&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;nixpkgs&#x2F;manual&#x2F;&quot;&gt;Nixpkgs manual&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Thoughts on Suspense for data fetching</title>
          <pubDate>Wed, 30 Oct 2019 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/thoughts-on-suspense/</link>
          <guid>https://sgt.hootr.club/blog/thoughts-on-suspense/</guid>
          <description xml:base="https://sgt.hootr.club/blog/thoughts-on-suspense/">&lt;p&gt;React&#x27;s &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;docs&#x2F;concurrent-mode-intro.html&quot;&gt;Concurrent Mode&lt;&#x2F;a&gt; has finally
landed in React&#x27;s experimental builds, and it just so happens that I have to start writing this
new app at work to be used internally, so what better way to get my hands dirty with this new
shiny stuff!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;molten-matter&#x2F;data-fetching-react&#x2F;&quot;&gt;In another post&lt;&#x2F;a&gt; I expressed some disconcert at what little
I remembered of Suspense&#x27;s API (kinda missed that .read() thingy in the fetch request), and that...
is still there after seeing the full API. I think you&#x27;ll see what I mean.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;exceptions-for-control-flow&quot;&gt;Exceptions for control flow&lt;&#x2F;h2&gt;
&lt;p&gt;I happen to have here a sort of minimal working example of &quot;Suspense for data fetching&quot;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Hello&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ({&lt;&#x2F;span&gt;&lt;span&gt; resource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; })&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;span&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Hello &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&amp;lt;&#x2F;span&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; SuspendTest&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span&gt; resource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; suspendPromise&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;from inside a shell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Suspense fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;loading...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Hello resource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Suspense&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;This will display &quot;loading...&quot; for 3 seconds and then show &quot;Hello from inside a shell&quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;sleep&lt;&#x2F;code&gt; is &lt;code&gt;(ms) =&amp;gt; new Promise((resolve) =&amp;gt; setTimeout(resolve, ms))&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ll call &quot;resource&quot; the thing that provides our &lt;code&gt;Hello&lt;&#x2F;code&gt; component with the data it needs to render&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The resource is clearly a promise but you don&#x27;t have to await it; you just call it as if you already
had the data inside it. How the hell does this work? The title of this section kinda spoiled it,
but here it is anyway:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; suspendPromise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;thunk&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Promise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; status&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;PENDING&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span&gt; pendingPromise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    try&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; status&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;SUCCESS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; thunk&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (e)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; status&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;ERROR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; e&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;)()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    switch&lt;&#x2F;span&gt;&lt;span&gt; (state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;status)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      case&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;PENDING&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        throw&lt;&#x2F;span&gt;&lt;span&gt; pendingPromise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      case&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;ERROR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        throw&lt;&#x2F;span&gt;&lt;span&gt; state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      case&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;SUCCESS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It fires the promise and then returns a function that will fetch the data, if it&#x27;s available.
And if it&#x27;s not, it just &lt;em&gt;throws the promise&lt;&#x2F;em&gt;. That&#x27;s it, that&#x27;s the magic. &lt;code&gt;Suspense&lt;&#x2F;code&gt; then will
act like an &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;docs&#x2F;error-boundaries.html&quot;&gt;error boundary&lt;&#x2F;a&gt; and catch the promise
and then do whatever it is that it does to promises. Presumably await them and then retry with
the rendering roughly when they&#x27;re done.&lt;&#x2F;p&gt;
&lt;p&gt;This is the part where I pretend to know what algebraic effects are and say, gosh, this looks a lot
like algebraic effects.&lt;&#x2F;p&gt;
&lt;p&gt;I grew to like fetching in useEffect, but I won&#x27;t deny it&#x27;s always looked a bit awkward, so I&#x27;m glad
this deprecates it. I&#x27;m also glad this makes the API synchronous, because it means that error
boundaries are finally useful. To be frank, I haven&#x27;t implemented error handling in the data-fetching
component I use in another app because if the request returns an error it can only mean the server
is down, and that&#x27;s a scenario I don&#x27;t really want to entertain. That and the API wouldn&#x27;t make
it very pleasant to deal with errors.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t know what to make of &lt;code&gt;useTransition&lt;&#x2F;code&gt; yet, but I think I&#x27;ll write another post about it when
I figure it out. This is just a braindump of what I gathered so far.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;another-thought&quot;&gt;Another thought&lt;&#x2F;h2&gt;
&lt;p&gt;One thing I&#x27;ve learnt is that you &lt;em&gt;have&lt;&#x2F;em&gt; to read from the resource in a different component than
the one you use &lt;code&gt;Suspense&lt;&#x2F;code&gt; in, so you can&#x27;t inline Hello into SuspendTest.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; SuspendTest&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span&gt; resource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; suspendPromise&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;from inside a shell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Suspense fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;loading...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      &amp;lt;span&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Hello &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&amp;lt;&#x2F;span&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;&#x2F; Bad!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Suspense&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And I&#x27;m wondering how they will enforce this. Probably just with some error messages and
a slap on the wrist like &quot;the rules of hooks&quot;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;The full code for this post is available on &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;codesandbox.io&#x2F;s&#x2F;modest-rosalind-xdpgx&quot;&gt;CodeSandbox&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Config constraints in the type system where they belong</title>
          <pubDate>Tue, 17 Sep 2019 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/type-level-conf/</link>
          <guid>https://sgt.hootr.club/blog/type-level-conf/</guid>
          <description xml:base="https://sgt.hootr.club/blog/type-level-conf/">&lt;p&gt;Suppose we&#x27;re maintaining a frontend application. This application has to work
across different sites, each identified by a triplet of name, language and country.
Let us have a type for that.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Our coworker Homer passes by and looks at our code.&lt;&#x2F;p&gt;
&lt;p&gt;This is good enough, he might say, and we know he&#x27;s wrong. We have a list of all the
site names and supported locales ready to be used. What the hell is a string?, we ask him.
Is the empty string a valid site name? Is &quot;dddsdsddddssddsdsd&quot; a valid language?
Is Wales a country? Does the pope shit in the woods?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;el&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Only there&#x27;s a catch: not all sites support all locales, and not all locales are valid.
In fact we have a list of all the possible permutations of these three parameters.
I have it on good authority that people in Italy don&#x27;t speak Greek, and yet the type of
&lt;code&gt;Site&lt;&#x2F;code&gt; says otherwise. How can we sleep at night knowing that one day Homer could just
wake up and go add a site with locale &lt;code&gt;el_IT&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;el&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; L&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; L&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;el&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we have an issue: if we were to add a new language, say &quot;fr&quot;, we&#x27;d have to add it
in two places, and too much typing is bad for our wrists. Let us reduce the risk
of carpal tunnel.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;el&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;fr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Much better. Our wrists rejoice.&lt;&#x2F;p&gt;
&lt;p&gt;Now suppose we have a feature that we only want to see on scylla, so we only want to handle
the locales that are supported on scylla. TS defines a utility types that does just that
called &lt;code&gt;Extract&lt;&#x2F;code&gt;, so let us define some utility types for that.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SitesByName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Extract&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, {&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; _1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SitesByName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; &#x2F;&#x2F; &amp;quot;IT&amp;quot; | &amp;quot;GR&amp;quot; | &amp;quot;HR&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We also want to have a string representation of each site, so we can use it as key in
an object for features that have a different behavior on each sites. This is a bit boilerplatey,
but sadly necessary.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|it_IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|el_GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|hr_HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|de_CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|fr_CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|it_CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|hr_HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We also need some function to convert a &lt;code&gt;Site&lt;&#x2F;code&gt; to and from a &lt;code&gt;SiteString&lt;&#x2F;code&gt;, but if we
were to do it with just these types we&#x27;d be losing precious type information in the
process! We surely don&#x27;t want that. We need some sort of conversion table.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|it_IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|el_GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;el&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|hr_HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|de_CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|fr_CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;fr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|it_CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|hr_HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = keyof&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; _2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|hr_HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; &#x2F;&#x2F; S&amp;lt;&amp;quot;scylla&amp;quot;, &amp;quot;hr&amp;quot;, &amp;quot;HR&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once again we can derive the union we wrote above from this table to save us a bit of typing.
We still need to be very careful to keep &lt;code&gt;Site&lt;&#x2F;code&gt; and &lt;code&gt;SiteOfString&lt;&#x2F;code&gt; in sync, because
debugging a type error deriving from one of those could easily get confusing.&lt;&#x2F;p&gt;
&lt;p&gt;Now let us implement the function to parse a &lt;code&gt;SiteString&lt;&#x2F;code&gt; into a &lt;code&gt;Site&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Content warning: unsafe type assertions&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; parseSiteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  siteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SS&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SS&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [,&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; siteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    &#x2F;([&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;a-z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;a-z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;A-Z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;! as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;parseSiteString&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|hr_HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; &#x2F;&#x2F; S&amp;lt;&amp;quot;scylla&amp;quot;, &amp;quot;hr&amp;quot;, &amp;quot;HR&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The two assertions make us sick to the stomach, but after adding a few tests we feel better enough
to move onwards. Seeing the function convert the string with 0 type information loss really is its
own reward.&lt;&#x2F;p&gt;
&lt;p&gt;The reverse is a bit trickier, but fortunately the very nice &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gcanti&#x2F;typelevel-ts&quot;&gt;typelevel-ts&lt;&#x2F;a&gt;
library already has a similar type we can look up to help us on our journey to enlightenment, namely &lt;code&gt;KeysOfType&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;KeysOfType&lt;&#x2F;code&gt;: Picks only the keys of a certain type&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export type KeysOfType&amp;lt;A extends object, B&amp;gt; = { [K in keyof A]-?: A[K] extends B ? K : never }[keyof A]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let us adapt it for our use case.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; StringOfSite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteString&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; K&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SiteString&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; _3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; StringOfSite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; &amp;quot;scylla|it_IT&amp;quot; | &amp;quot;scylla|el_GR&amp;quot; | &amp;quot;scylla|hr_HR&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But that only returns the &lt;code&gt;SiteString&lt;&#x2F;code&gt;s with the same name, we hear a voice crying behind us.
Patience, Homer. Design is an iterative process, and so let us iterate on the result
of this first type with the other two parameters.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; StringOfSite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteString&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; K&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SiteString&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; serializeSite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  country&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; `${&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}`&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; StringOfSite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;serializeSite&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; &amp;quot;charybdis|de_CH&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;ll do, pig. That&#x27;ll do.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;You probably shouldn&#x27;t use this kind of type-level hackery on a production application.
But you might get away with it if you use a type-level testing library like
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Microsoft&#x2F;dtslint&quot;&gt;dtslint&lt;&#x2F;a&gt; or
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dsherret&#x2F;conditional-type-checks&quot;&gt;conditional-type-checks&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the full source code, ready to be pasted in your editor or on
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;play&#x2F;index.html&quot;&gt;TypeScript&#x27;s playground&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;el&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;fr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SitesByName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Extract&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;, {&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|it_IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;IT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|el_GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;el&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;GR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla|hr_HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;scylla&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|de_CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|fr_CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;fr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|it_CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;CH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis|hr_HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;charybdis&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;hr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;HR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = keyof&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; parseSiteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  siteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SS&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SS&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [,&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; siteString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    &#x2F;([&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;a-z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;\|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;a-z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;A-Z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;! as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; StringOfSite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteString&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; SiteOfString&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; K&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; never&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;SiteString&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; serializeSite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Site&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  country&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; `${&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;language&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;country&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}`&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; StringOfSite&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</description>
      </item>
      <item>
          <title>A brief history of JAVAScript</title>
          <pubDate>Fri, 30 Aug 2019 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/history-of-javas/</link>
          <guid>https://sgt.hootr.club/blog/history-of-javas/</guid>
          <description xml:base="https://sgt.hootr.club/blog/history-of-javas/">&lt;p&gt;JAVAScript is often described as a superset of JSon. This is only half the truth.&lt;&#x2F;p&gt;
&lt;p&gt;Back in 1996 when the Internet was being designed by Tim Bernard-Lee, he reportedly received a visit from St. Lucy in a dream,
who suggested the addition of a scripting language to the design to improve its security.
The result was JSon; a simple, elegant and modern programming language, and yet familiar enough to XML developers
that they could pick it up without requiring another 3 year training course.&lt;&#x2F;p&gt;
&lt;p&gt;(Fun fact: Larry Brim, the author of the language spec, wanted to name the language after his son J.
Unfortunately the name was already taken on npm, and so he opted to call it &quot;J, Son&quot;, which was later shortened to JSon by the
marketing team.)&lt;&#x2F;p&gt;
&lt;p&gt;The addition of JSon to the design of the Internet delayed its release by a few months. Its implementation was named V, and the
foundations it lay still form the backbone of today&#x27;s Internet.&lt;&#x2F;p&gt;
&lt;p&gt;When the Internet was finally released and made accessible to the public through Chrome in 1998,
the response to JSon was lukewarm. Larry Brim&#x27;s decision to leave out row polymorphism and instead opt for delimited continuations
was highly controversial. The general opinion was that JSon was too &quot;low-level&quot; to be used directly by developers.&lt;&#x2F;p&gt;
&lt;p&gt;This led to the development of several languages which compiled to JSon, the most notable being Yaml (Oracle, 2002),
a very powerful and expressive language whose primary features included significant whitespace and arbitrary code execution,
and Java (Comcast, 2003), a light superset of JSon which brought back row polymorphism and enabled the development of Applets.&lt;&#x2F;p&gt;
&lt;p&gt;Tight collaboration between the creators of these new languages and the V development team prompted the addition of a lot of
new constructs and instructions that enabled the creators to optimize common workflows, and in an interview from 2008, 10 years after its
initial release, Larry Brim declared that the specification of JSon was growing to the point where he may not be able to contain it.&lt;&#x2F;p&gt;
&lt;p&gt;Version 7 of V, often abbreviated to V7, would be the last version to support JSon as compilation target. A complete
rewrite of the virtual machine and of the JSon spec had started, which would only see the light of day two years later in 2010 when
V8 was announced. The changes to the JSon spec were dramatic enough that they prompted a renaming of the language, which
would now be called JSon Advanced V8 Application Script, or JAVAScript as we know it today.&lt;&#x2F;p&gt;
&lt;p&gt;JAVAScript was well received by the community, and has largely replaced JSon as the Internet&#x27;s leading scripting language. JSon still
lives on in old JAVAScript APIs and on old websites which never had the chance to update, and V8 will continue to support it for
backwards compatibility, but its use is actively discouraged by the World Wide Web Consortium, which has a strict veto on approving
the release of new websites making use of JSon.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A data-fetching component in React</title>
          <pubDate>Fri, 10 May 2019 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/data-fetching-react/</link>
          <guid>https://sgt.hootr.club/blog/data-fetching-react/</guid>
          <description xml:base="https://sgt.hootr.club/blog/data-fetching-react/">&lt;p&gt;Remember last year&#x27;s &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=6g3g0Q_XVb4&quot;&gt;talk&lt;&#x2F;a&gt; about Suspense by Dan Abramov?
I was still mostly learning React when that was uploaded to youtube, and
seeing him talk about just this issue and presenting this magic API that
seemed to solve it without much effort got me really hooked.
But then &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;docs&#x2F;code-splitting.html#suspense&quot;&gt;Suspense&lt;&#x2F;a&gt; actually comes out and (even an year
later) it&#x27;s just about code splitting. What gives?&lt;&#x2F;p&gt;
&lt;p&gt;Fast forward one year, I was refactoring parts of a smallish React app and
taking the opportunity to learn hooks, and I noticed I had a lot of components
that shared roughly this structure (plus some unrelated UI state sprinkled in
for good measure):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; CookieCutter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; React&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    hasLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; false&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  componentDidMount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;props&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;fetchData&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;setState&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        hasLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  render&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; hasLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; = this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; hasLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      &amp;lt;div&amp;gt;{&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&amp;lt;&#x2F;div&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      &amp;lt;div&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Loading...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;div&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And since I had just spent all day decomposing old monstrous class components
and otherwise wasting time polishing parts of the app like an art project,
I started thinking about how I could extract this pattern so it would look like
the vague memory I had of Suspense. For reference, this (part of) Suspense&#x27;s
API:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Suspense fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&amp;lt;div&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Loading...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;div&amp;gt;}&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;NewComfort&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Suspense&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Something kind of bothers me about this API: where&#x27;s the data? How does
&lt;code&gt;Suspense&lt;&#x2F;code&gt; know that whatever data &lt;code&gt;NewComfort&lt;&#x2F;code&gt; needs has finished loading?
It looks like the data flows downwards from &lt;code&gt;NewComfort&lt;&#x2F;code&gt;, but also upwards
towards &lt;code&gt;Suspense&lt;&#x2F;code&gt;? I guess I&#x27;m just not used to this stuff, but I thought
it&#x27;d be better to be a bit more explicit, even if it increased verbosity.
I&#x27;m also not a huge fan of higher-order components, so instead I decided to
rely on the good old trick of &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;docs&#x2F;render-props.html&quot;&gt;render props&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  provider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;fetchData&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&amp;lt;div&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Loading...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;div&amp;gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;ChocolateMatter data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;LazyLoaded&lt;&#x2F;code&gt; is probably not a very good name, but I decided to go with that.
I was still on the hooks hype train so I went out of my way to implement it
using hooks:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Props&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  provider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Promise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;?:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; JSX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Element&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; JSX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Element&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; LazyLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  provider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Props&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; setData&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; React&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;useState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  React&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;useLayoutEffect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    setData&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    provider&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;      setData&lt;&#x2F;span&gt;&lt;span&gt;(data)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  },&lt;&#x2F;span&gt;&lt;span&gt; [provider])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; children&lt;&#x2F;span&gt;&lt;span&gt;(data)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; || null;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And this worked well enough. At first I implemented it using &lt;code&gt;useEffect&lt;&#x2F;code&gt;, but I
noticed that &lt;code&gt;useLayoutEffect&lt;&#x2F;code&gt; would actually skip the split-second load that
occurred when the &lt;code&gt;provider&lt;&#x2F;code&gt; resolved immediately if the data was already in
the cache.&lt;&#x2F;p&gt;
&lt;p&gt;But there&#x27;s a big issue with this implementation. Do you see it? I didn&#x27;t until
I actually tested the component on the real page. Consider this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;VELOCITY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;DESIGN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;COMFORT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; Main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; ({&lt;&#x2F;span&gt;&lt;span&gt; cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; })&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;span&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; React&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;useState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;VELOCITY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;div&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      {&lt;&#x2F;span&gt;&lt;span&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;VELOCITY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;          provider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;getTekka&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;          fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&amp;lt;div&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Loading tekka...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;div&amp;gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;          {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Velocity data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;DESIGN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;          provider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;getDsco&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;          fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&amp;lt;div&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Loading dsco...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;div&amp;gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;          {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Design data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Comfort setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    &amp;lt;&#x2F;div&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We land on&lt;code&gt;&#x27;VELOCITY&#x27;&lt;&#x2F;code&gt; and switch to &#x27;&lt;code&gt;COMFORT&#x27;&lt;&#x2F;code&gt; and then to &lt;code&gt;&#x27;DESIGN&#x27;&lt;&#x2F;code&gt; and
everything is good. But then from &lt;code&gt;&#x27;DESIGN&#x27;&lt;&#x2F;code&gt; we switch the page back to
&lt;code&gt;&#x27;VELOCITY&#x27;&lt;&#x2F;code&gt; and everything breaks. What just happened?&lt;&#x2F;p&gt;
&lt;p&gt;To understand this, you should know a bit about what React calls
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;docs&#x2F;reconciliation.html&quot;&gt;reconciliation&lt;&#x2F;a&gt;. To ensure that the whole page isn&#x27;t
unmounted and remounted every time something changes, React will look at the
component tree and do some diffing to make sure that only what actually needs
to be updated will be.&lt;&#x2F;p&gt;
&lt;p&gt;In particular, React will look at the &quot;type&quot; of component, and if it remains
the same between two updates it will avoid unmounting and remounting it. In
this case this is pretty bad news, because when we switch between the two
pages above that have a LazyLoaded component at the same level of the render
tree, &lt;code&gt;LazyLoaded&lt;&#x2F;code&gt; will not &lt;em&gt;not&lt;&#x2F;em&gt; get unmounted. In the first paint we end up
with an invalid state in which the &lt;code&gt;children&lt;&#x2F;code&gt; function is the new one from
&lt;code&gt;&#x27;VELOCITY&#x27;&lt;&#x2F;code&gt;, but the &lt;code&gt;data&lt;&#x2F;code&gt; is the old one we fetched in &lt;code&gt;&#x27;DESIGN&#x27;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So how do we fix it? The first and dumbest thing that came into mind was make
sure that &lt;code&gt;LazyLoaded&lt;&#x2F;code&gt; is unmounted whenever we change the page. React
provides a default prop on all components that just about does this this
called &lt;code&gt;key&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;VELOCITY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;VELOCITY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    provider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;getTekka&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&amp;lt;div&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Loading tekka...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;div&amp;gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Velocity data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;DESIGN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;DESIGN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    provider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;getDsco&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&amp;lt;div&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Loading dsco...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;lt;&#x2F;div&amp;gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Design data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;LazyLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Comfort setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;={&lt;&#x2F;span&gt;&lt;span&gt;setPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;} &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By making &lt;code&gt;key&lt;&#x2F;code&gt; required in &lt;code&gt;LazyLoaded&lt;&#x2F;code&gt;&#x27;s prop type and adding a threatening
doc comment I more or less ensured that users of this component would always
add a &quot;reasonably unique&quot; &lt;code&gt;key&lt;&#x2F;code&gt; for the part of the render tree &lt;code&gt;LazyLoaded&lt;&#x2F;code&gt;
would be used in. And this worked well and things were good.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s kind of a hack though, so the other day I came back to this component and
deleted both the key from the prop type and the threatening comment.
Considering the relationship between all the bits that form the state of the
component, it becomes obvious that a certain &lt;code&gt;data&lt;&#x2F;code&gt; can only be associated
with the &lt;code&gt;children&lt;&#x2F;code&gt; function from the render from which we also got the data&#x27;s
&lt;code&gt;provider&lt;&#x2F;code&gt;. With that in mind, we should modify the component a bit:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; State&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;  childrenSync&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; JSX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Element&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;export function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; LazyLoaded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  provider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Props&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; [&lt;&#x2F;span&gt;&lt;span&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; setState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    React&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;useState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  React&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;useLayoutEffect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    setState&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;    provider&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;      setState&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        childrenSync&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;      }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  },&lt;&#x2F;span&gt;&lt;span&gt; [provider])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; state&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    ?&lt;&#x2F;span&gt;&lt;span&gt; state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;childrenSync&lt;&#x2F;span&gt;&lt;span&gt;(state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;data)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;    :&lt;&#x2F;span&gt;&lt;span&gt; fallback&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; || &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Loading&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &#x2F;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Remember that, being a closure, the useLayoutEffect callback will capture the
value of &lt;code&gt;children&lt;&#x2F;code&gt; at the time it is defined, so since we defined our types
correctly we can be sure that the value returned from the &lt;code&gt;provider&lt;&#x2F;code&gt; will
always match the one expected from the &lt;code&gt;children&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;And now everything is good again. (The error handling and cancellation in this
component are left as an exercise to the reader.)&lt;&#x2F;p&gt;
&lt;p&gt;I really like this pattern, but I haven&#x27;t found anything similar to it online
when I searched. Maybe I&#x27;m just looking in the wrong places? Or maybe other
people that came up with this thought it was too simple to warrant writing
about it? Nevertheless, I thought it was worth sharing because it&#x27;s nice and
presented a problem with a non-obvious solution, unless you&#x27;re familiar with
some of React&#x27;s internal workings. Maybe somebody will get a kick out of this.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Reading Ur&#x2F;Web signatures, part 1</title>
          <pubDate>Mon, 07 Jan 2019 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/urweb-sig/</link>
          <guid>https://sgt.hootr.club/blog/urweb-sig/</guid>
          <description xml:base="https://sgt.hootr.club/blog/urweb-sig/">&lt;p&gt;So you stumbled upon Ur&#x2F;Web and you rather like what it&#x27;s about, but after
trudging through the tutorials and the examples in the few blog posts you&#x27;ve
seen around you can&#x27;t seem to find your own footing. The compiler errors are
long and life is short and you&#x27;re about to throw your computer out the window.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;blog&#x2F;urweb&#x2F;&quot;&gt;I understand.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I&#x27;ll walk you through the signatures of a few functions from the
standard library, hopefully providing you with enough context to make it
through the rest on your own. You might want to grab &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;urweb&#x2F;urweb&#x2F;tree&#x2F;master&#x2F;lib&#x2F;ur&quot;&gt;a copy&lt;&#x2F;a&gt; or search
for the one on your hard drive so you can follow along.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to be frank: given the current state of the ecosystem and of the
documentation, you have close to no chance of learning Ur&#x2F;Web if you don&#x27;t
already know some OCaml&#x2F;ReasonML or another language in the ML family, so
if you don&#x27;t you might want to get well acquainted with one first.
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt; is a good starting point.&lt;&#x2F;p&gt;
&lt;p&gt;Open up &lt;code&gt;string.urs&lt;&#x2F;code&gt; and have a look around. I&#x27;m going to assume you can read
these signatures:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; length&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; append&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; all&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;char&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; bool&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Still here? Good, let&#x27;s introduce some new syntax.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; index&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; char&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; option&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you come from OCaml or SML you might notice that the argument of the type
constructor &lt;code&gt;option&lt;&#x2F;code&gt; is to the right of the constructor, as in normal function
application. This unification of function and type constructor application is
not just a matter of syntax like in &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;reasonml.github.io&#x2F;docs&#x2F;en&#x2F;comparison-to-ocaml#type-parameters&quot;&gt;ReasonML&lt;&#x2F;a&gt;; put this in the
back of your mind for the moment, we&#x27;ll come back to it later.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; substring&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;Start &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Len &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Much like SML, Ur&#x2F;Web makes up for the lack of labeled arguments by using
anonymous records as function arguments. Also, record field names must start
with a capital letter like the members of a variant.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;generics&quot;&gt;Generics&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s kick things up a notch. Open up &lt;code&gt;list.urs&lt;&#x2F;code&gt; and you&#x27;ll be greeted by
something like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; rev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :::&lt;&#x2F;span&gt;&lt;span&gt; Type &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you might have guessed, &lt;code&gt;List.rev&lt;&#x2F;code&gt; is a function that takes a list of
elements and returns another list with the elements of the first, in reverse
order. &lt;code&gt;rev&lt;&#x2F;code&gt; can reverse lists that contain any element, so we say that it is
&lt;em&gt;polymorphic&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;a&lt;&#x2F;code&gt; is the type of the values contained in the input and output list. The
argument &lt;code&gt;a ::: Type&lt;&#x2F;code&gt; is just a way of saying that we don&#x27;t know what type &lt;code&gt;a&lt;&#x2F;code&gt;
will be when we declare the function; it&#x27;s up to the caller to bind it to a
valid type. The triple colon (&lt;code&gt;:::&lt;&#x2F;code&gt;) means that this type parameter is
implicit, so the compiler will take care of inserting the correct type when
calling it.&lt;&#x2F;p&gt;
&lt;p&gt;OCaml and most other languages don&#x27;t require you to explicitly declare these
type parameter, but &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;blog.janestreet.com&#x2F;ensuring-that-a-function-is-polymorphic-in-ocaml-3-12&#x2F;&quot;&gt;sometimes it is useful&lt;&#x2F;a&gt; to ensure the
well-typedness of a polymorphic function.&lt;&#x2F;p&gt;
&lt;p&gt;Quoting from the &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;www.impredicative.com&#x2F;ur&#x2F;tutorial&#x2F;intro.html&quot;&gt;tutorial&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Unlike in ML and Haskell, polymorphic functions in Ur&#x2F;Web often require full
type annotations. That is because more advanced features make Ur type
inference undecidable.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Let&#x27;s pull up the implementation for a moment (found in &lt;code&gt;list.ur&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fun rev [a] (ls : list a) = ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;a&lt;&#x2F;code&gt; in square brackets here corresponds to &lt;code&gt;a ::: Type&lt;&#x2F;code&gt; in the signature
above. We could also write it like &lt;code&gt;[a ::: Type]&lt;&#x2F;code&gt; if we wanted to be more
explicit.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s look at &lt;code&gt;List.mp&lt;&#x2F;code&gt;. (Which is just the &lt;code&gt;List.map&lt;&#x2F;code&gt; function, but it
can&#x27;t be called &lt;code&gt;map&lt;&#x2F;code&gt; because &lt;code&gt;map&lt;&#x2F;code&gt; is a keyword in Ur&#x2F;Web. More on that later.)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; mp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :::&lt;&#x2F;span&gt;&lt;span&gt; Type &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :::&lt;&#x2F;span&gt;&lt;span&gt; Type &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; b&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;mp&lt;&#x2F;code&gt; has two polymorphic type parameters, so they are both made explicit in
the signature.&lt;&#x2F;p&gt;
&lt;p&gt;Interestingly, we can write a function so that the type parameter has to be
passed &lt;em&gt;explicitly&lt;&#x2F;em&gt; by replacing &lt;code&gt;:::&lt;&#x2F;code&gt; with a double colon (&lt;code&gt;::&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;(*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-style: italic;&quot;&gt; id.urs &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;*)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; ::&lt;&#x2F;span&gt;&lt;span&gt; Type &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;(*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-style: italic;&quot;&gt; id.ur &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;*)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fun &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt; [a :: Type]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;font-style: italic;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; a)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;val x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; id [int] 451&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;:::&lt;&#x2F;code&gt; indicates a type parameter that may be inferred by the compiler, while
&lt;code&gt;::&lt;&#x2F;code&gt; indicates one that has to be passed explicitly. The compiler will be able
to infer a type parameter by itself most of the time, but in some cases which
we&#x27;ll see later you&#x27;ll have to be explicit and use the double colon.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;basics-of-type-constructors&quot;&gt;Basics of type constructors&lt;&#x2F;h2&gt;
&lt;p&gt;At this point I should introduce Ur&#x2F;Web&#x27;s type constructors, because they&#x27;re
a lot more powerful than those in most other languages. Open up &lt;code&gt;json.ur&lt;&#x2F;code&gt;
(not &lt;code&gt;json.urs&lt;&#x2F;code&gt;) and the first thing you&#x27;ll see will be this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;con json a = {ToJson : a -&amp;gt; string,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              FromJson : string -&amp;gt; a * string}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While the &lt;code&gt;con&lt;&#x2F;code&gt; keyword might throw you off, you might recognize this as a
simple type declaration. Ur&#x2F;Web actually makes a distinction between simple
aliases, like the one we encountered at the top of &lt;code&gt;string.urs&lt;&#x2F;code&gt;, and type
constructors, which take one (or more!) arguments, and have to be declared
with the keyword &lt;code&gt;con&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;json&lt;&#x2F;code&gt; type constructor is simply a record with an encoder function which
takes an &lt;code&gt;a&lt;&#x2F;code&gt; and returns a JSON string, and a decoder function which takes a
JSON string and returns an &lt;code&gt;a&lt;&#x2F;code&gt; and the remaining JSON string.&lt;&#x2F;p&gt;
&lt;p&gt;Remember that thing earlier about unifying function application and type
constructor application syntax? The two are actually very closely related:
just as normal functions are functions from values to values, type constructors
can be thought of as &lt;strong&gt;type-level functions from types to types&lt;&#x2F;strong&gt;, and the
purpose of this unification is just to make the similarity more apparent.&lt;&#x2F;p&gt;
&lt;p&gt;This insight might not net you much in OCaml or SML because type constructors
have a lot of limitations compared to functions: they can&#x27;t be curried, and
you can perform very few operations inside them.&lt;&#x2F;p&gt;
&lt;p&gt;Ur&#x2F;Web&#x27;s type constructors are much more interesting. The &lt;code&gt;json&lt;&#x2F;code&gt; declaration
above is actually syntactic sugar for a type-level function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;con json = fn (a :: Type) =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  {ToJson : a -&amp;gt; string,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   FromJson : string -&amp;gt; a * string}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can define a &lt;strong&gt;curried&lt;&#x2F;strong&gt; constructor that takes two types and returns the
type of a 2-tuple:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;con pair a b = a * b&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;con intAnd :: Type -&amp;gt; Type = pair int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; intAnd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;451&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt; &amp;quot;what a shame&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can also perform various useful operations on record types, as we&#x27;ll see
later.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;type-classes&quot;&gt;Type classes&lt;&#x2F;h2&gt;
&lt;p&gt;Ur&#x2F;Web&#x27;s &lt;code&gt;=&lt;&#x2F;code&gt; (equals) operator works just like you&#x27;d expect it to for types
provived by the standard library: &lt;code&gt;1 = 1&lt;&#x2F;code&gt;, &lt;code&gt;&quot;line&quot; = &quot;line&quot;&lt;&#x2F;code&gt;,
and &lt;code&gt;Some &quot;just&quot; = Some &quot;just&quot;&lt;&#x2F;code&gt;. So is it implemented like in &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;blog.janestreet.com&#x2F;the-perils-of-polymorphic-compare&#x2F;&quot;&gt;OCaml&lt;&#x2F;a&gt;,
using a &quot;magic&quot; internal function that structurally compares record fields
and variant members? Not quite.&lt;&#x2F;p&gt;
&lt;p&gt;If we try to compare two records, we&#x27;ll get a surprisingly helpful error
message:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;(*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-style: italic;&quot;&gt; test.ur &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;*)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; ok&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; = {&lt;&#x2F;span&gt;&lt;span&gt; A &lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; A &lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;(*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-style: italic;&quot;&gt; test.ur:1:5: (to 1:26) Can&amp;#39;t resolve type class instance&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #EBCB8B;font-style: italic;&quot;&gt;   Class constraint: eq {A : int} &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;*)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s take a look into &lt;code&gt;basis.urs&lt;&#x2F;code&gt;. At line 26, you&#x27;ll see these declarations:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; eq&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; eq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :::&lt;&#x2F;span&gt;&lt;span&gt; Type &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; eq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The signature of the &lt;code&gt;eq&lt;&#x2F;code&gt; function looks familiar enough. We still don&#x27;t know
what &lt;code&gt;class eq&lt;&#x2F;code&gt; means, but by the way it&#x27;s used in the function we can infer
that it&#x27;s a constructor that takes one argument.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, &lt;code&gt;eq&lt;&#x2F;code&gt; is just an &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;caml.inria.fr&#x2F;pub&#x2F;docs&#x2F;manual-ocaml&#x2F;moduleexamples.html#sec20&quot;&gt;abstract type&lt;&#x2F;a&gt;, i.e. a type whose
implementation isn&#x27;t specified in its signature so that only the underlying
module can access it. If you don&#x27;t know what that is, you can think of it as an
opaque pointer in C.&lt;&#x2F;p&gt;
&lt;p&gt;In this case we can&#x27;t look at its actual implementation because &lt;code&gt;Basis&lt;&#x2F;code&gt; is
implemented directly in C, but it would look somewhat like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;con eq t = t -&amp;gt; t -&amp;gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So now we should have all the pieces to understand the &lt;code&gt;eq&lt;&#x2F;code&gt; function above.
Or do we?&lt;&#x2F;p&gt;
&lt;p&gt;If you were to define your own &lt;code&gt;eq&lt;&#x2F;code&gt; constructor and your own &lt;code&gt;eq&lt;&#x2F;code&gt; function,
you&#x27;d always have to pass a function of type &lt;code&gt;eq t&lt;&#x2F;code&gt; as first argument.
(This kind of function can also be called &lt;strong&gt;witness&lt;&#x2F;strong&gt;.)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;con eq&amp;#39; t = t -&amp;gt; t -&amp;gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fun eq&amp;#39; [t] (cmp : eq&amp;#39; t) (a : t) (b : t) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  cmp a b&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fun eq&amp;#39;_bool (a : bool) (b : bool) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  case (a, b) of&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  | (True, True) =&amp;gt; True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  | (False, False) =&amp;gt; True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  | _ =&amp;gt; False&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; test&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; eq&amp;#39; eq&amp;#39;_bool&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; True False&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But if we were to do the same with eq, we would get a compiler error.&lt;&#x2F;p&gt;
&lt;p&gt;Turns out that the &lt;code&gt;eq&lt;&#x2F;code&gt; function is just the desugared name of the &lt;code&gt;=&lt;&#x2F;code&gt;
operator, and as we&#x27;ve seen above, we can use it transparently without having
to worry about the witness function.
This is where the &lt;code&gt;class&lt;&#x2F;code&gt; keyword comes into play.&lt;&#x2F;p&gt;
&lt;p&gt;When we mark &lt;code&gt;eq&lt;&#x2F;code&gt; with the &lt;code&gt;class&lt;&#x2F;code&gt; keyword in a signature file, the compiler
will automatically search for a fitting implementation of &lt;code&gt;eq t&lt;&#x2F;code&gt; every time we
call &lt;code&gt;=&lt;&#x2F;code&gt; with a given &lt;code&gt;t&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;option&lt;&#x2F;code&gt; constructor also defines an &lt;code&gt;eq&lt;&#x2F;code&gt; witness in  the &lt;code&gt;Option&lt;&#x2F;code&gt; module.
This is its signature:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; eq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; :::&lt;&#x2F;span&gt;&lt;span&gt; Type &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; eq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; eq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;option&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-weight: bold;&quot;&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This should be straightforward by now. &lt;code&gt;Option.eq&lt;&#x2F;code&gt; implicitly takes a witness
&lt;code&gt;eq a&lt;&#x2F;code&gt; and maps it to the value stored inside the option, if any. Let&#x27;s take
a look at its implementation.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fun eq [a] (_ : eq a) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mkEq (fn x y =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;             case (x, y) of&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                 (None, None) =&amp;gt; True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;               | (Some x, Some y) =&amp;gt; x = y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;               | _ =&amp;gt; False)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The wildcard corresponds to the witness argument, even though the function
doesn&#x27;t use it directly. In a way, the witness argument is just there to
&lt;strong&gt;constrain&lt;&#x2F;strong&gt; the types we can call &lt;code&gt;Option.eq&lt;&#x2F;code&gt; with to those for which there
exists an implementation of &lt;code&gt;eq&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now the error message should make sense: the compiler is telling us that this
invocation of &lt;code&gt;=&lt;&#x2F;code&gt; has a constraint of type &lt;code&gt;eq {A : int}&lt;&#x2F;code&gt; on its arguments,
so we need to implement a witness of &lt;code&gt;eq&lt;&#x2F;code&gt; for &lt;code&gt;{A : int}&lt;&#x2F;code&gt;. We&#x27;ll have to use
the &lt;code&gt;mkEq&lt;&#x2F;code&gt; function to do this.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;ocaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;font-style: italic;&quot;&gt; eq_a_int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mkEq&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  (&lt;&#x2F;span&gt;&lt;span&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;A &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;) (&lt;&#x2F;span&gt;&lt;span&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;B &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-weight: bold;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;font-weight: bold;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;A = &lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;font-weight: bold;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;A)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;(*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;font-style: italic;&quot;&gt; this will compile now &lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt;*)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;val ok&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; A &lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBCB8B;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; A &lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;font-weight: bold;&quot;&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;to-be-continued&quot;&gt;To be continued...&lt;&#x2F;h2&gt;
&lt;p&gt;This post is getting pretty long, so I&#x27;ll wrap it up here for this week.
If you already knew most of the things I covered here, don&#x27;t worry, the next
one is gonna cover some of the most foreign parts of the type system.&lt;&#x2F;p&gt;
&lt;p&gt;Watch this space for part 2! (I promise I&#x27;ll implement an RSS feed soon.)
In the meantime, you might want to brush up on &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;blog.haberkucharsky.com&#x2F;technology&#x2F;2015&#x2F;07&#x2F;21&#x2F;more-monads-in-ocaml.html&quot;&gt;monads&lt;&#x2F;a&gt;,
or take a look at the more dense &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;impredicative.com&#x2F;ur&#x2F;tutorial&#x2F;&quot;&gt;official tutorial&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Overthinking cash in TypeScript</title>
          <pubDate>Sun, 11 Nov 2018 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/overthinking-cash/</link>
          <guid>https://sgt.hootr.club/blog/overthinking-cash/</guid>
          <description xml:base="https://sgt.hootr.club/blog/overthinking-cash/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;overthinking-cash&#x2F;shells.jpg&quot; alt=&quot;cicada shells on a tree near a beach in Marina di Cecina, Italy&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Part of my current project at work deals with monetary values in the form of
cash, making change, and displaying the bills and coins on the screen.&lt;&#x2F;p&gt;
&lt;p&gt;Since I recently started adding types to certain parts of the codebase with
TypeScript, I thought it&#x27;d be fun to see how elaborately I could model the
types of the values and of the functions using TypeScript&#x27;s more advanced
features, and while I was at it, how easy it would be to parametrize the types
based on the currency you&#x27;re dealing with.&lt;&#x2F;p&gt;
&lt;p&gt;First of all, I&#x27;m gonna define what I mean by &quot;cash&quot;. Since I need to show
the bills and coins on the screen at scale, I need to know their color and
their dimensions, and here&#x27;s the issue: coins only need a width, but banknotes
also need a height, so I can&#x27;t use the same type for both.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Coin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  color&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; number&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Banknote&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  color&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; number&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  height&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; number&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now I have to store these values somewhere. Using two objects, one for the
coins and one for the banknotes, would be the simplest solution, but it would
complicate things at the call site when we don&#x27;t know whether the amount is
a coin or a banknote.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; cashInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; CashAmount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;: ??? =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 200&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  ?&lt;&#x2F;span&gt;&lt;span&gt; banknotesByAmount&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  :&lt;&#x2F;span&gt;&lt;span&gt; coinsByAmount&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)[amount]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is not a very elegant piece of code, and TypeScript doesn&#x27;t like it much
either.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sgt.hootr.club&#x2F;blog&#x2F;overthinking-cash&#x2F;cash-money-of-you.png&quot; alt=&quot;not even Sayaka likes it&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dependent-kinda-types-to-the-rescue&quot;&gt;Dependent (kinda) types to the rescue!&lt;&#x2F;h2&gt;
&lt;p&gt;TypeScript 2.8 introduced &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;release-notes&#x2F;typescript-2-8.html&quot;&gt;conditional types&lt;&#x2F;a&gt; which (as I understand it)
are a light form of dependent types, like the ones in Coq or Idris. You can use
them to implement basic arithmetic with peano numbers, which is cool as hell,
but unfortunately not very useful in real code (at least at the moment).&lt;&#x2F;p&gt;
&lt;p&gt;Now I can define a type which returns either &lt;code&gt;Coin&lt;&#x2F;code&gt; or &lt;code&gt;Banknote&lt;&#x2F;code&gt; depending
on the type of its argument:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; ByAmount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;CoinAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; BanknoteAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  Amt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; CoinAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; BanknoteAmt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Amt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; CoinAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;     ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Coin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Amt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Amt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; BanknoteAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Banknote&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Amt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; never&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that I&#x27;m still parametrizing over the denomination to make it possible
to use different currencies easily, so the signature still looks a bit awful,
but it&#x27;ll only need one type parameter when &quot;instantiated&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Defining the type of the object is now a breeze, thanks to mapped types.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; CashMap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  CoinAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; number&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  BanknoteAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; number&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  Amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; CoinAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; BanknoteAmt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  readonly&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;A&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; Amount&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;    Readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;ByAmount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;CoinAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; BanknoteAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; A&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we&#x27;re ready to instantiate these types with a currency.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;plugging-in-the-eurodollars&quot;&gt;Plugging in the Eurodollars&lt;&#x2F;h2&gt;
&lt;p&gt;TypeScript doesn&#x27;t have &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;dev.realworldocaml.org&#x2F;variants.html#scrollNav-4&quot;&gt;polymorphic variants&lt;&#x2F;a&gt;, but it does have
&lt;strong&gt;numeric literal types&lt;&#x2F;strong&gt;, which are just as good for our purposes.
Let&#x27;s define the denominations.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; EuroCoinAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;  1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 20&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 50&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 200&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; EuroBanknoteAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;  500&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 2000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 5000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 10000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 20000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 50000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; EuroAmount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  EuroCoinAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; EuroBanknoteAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now I can fill in the maps.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; EuroMap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; CashMap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;  EuroCoinAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; EuroBanknoteAmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; EuroAmount&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; euroByAmount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt; EuroMap&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;  1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    color&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;#b87333&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 16&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;  2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    color&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;#b87333&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 18.5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;  &#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;  500&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 500&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    color&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;: &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;grey&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;#39;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    width&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 120&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    height&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 62&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;  &#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since this is all static data, the compiler is able to verify that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I didn&#x27;t forget nor invent any coin or banknote&lt;&#x2F;li&gt;
&lt;li&gt;objects indexed by a &lt;code&gt;BanknoteAmount&lt;&#x2F;code&gt; effectively have a &lt;code&gt;height&lt;&#x2F;code&gt; field,
and that those indexed by a &lt;code&gt;CoinAmount&lt;&#x2F;code&gt; don&#x27;t&lt;&#x2F;li&gt;
&lt;li&gt;the indexing key matches the &lt;code&gt;amount&lt;&#x2F;code&gt; field&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When I try indexing the object, the compiler will infer the correct type
based on the type of its argument, and complain when trying to index a value
that does not exist (though the error message for that is a bit confusing).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;euroByAmount[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; &#x2F;&#x2F; Readonly&amp;lt;Banknote&amp;lt;500&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;euroByAmount[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;   &#x2F;&#x2F; Readonly&amp;lt;Coin&amp;lt;2&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; error: Element implicitly has an &amp;#39;any&amp;#39; type&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; because type &amp;#39;CashMap&amp;lt;EuroCoinAmt, EuroBanknoteAmt, EuroAmount&amp;gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;&#x2F;&#x2F; has no index signature.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;euroByAmount[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The advantage of keeping the more general types around is that we can now
differentiate between functions that work the same regardless of the currency,
and functions that might require a different implementation for each currency.&lt;&#x2F;p&gt;
&lt;p&gt;In my project I have a function that returns a few reasonable change suggestions
given an amount of money, in which the optimal solution is calculated using a
simple greedy algorithm. This works for euros, where the greedy algorithm does
indeed return an optimal solution, but it might not be a good fit for other
currencies with different denominations, so in this particular case it makes
sense to use the specific types for euros, rather than the more general ones.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;&#x2F;h2&gt;
&lt;p&gt;TypeScript has a rather quirky type system with many features that vaguely
resemble those found in other languages but not quite, like literal types, and
some weird features taken straight from niche almost-research languages, like
conditional and mapped types, but somehow they all fit together rather nicely
to model the sort of JS code you&#x27;d write normally.&lt;&#x2F;p&gt;
&lt;p&gt;This is TypeScript&#x27;s biggest strength, in my opinion: I barely had to change
my code when adding types to this module, and it would still make sense if you
took the types out, even if I had written this in TS to begin with.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;frontstuff.io&#x2F;how-to-handle-monetary-values-in-javascript&quot;&gt;How to Handle Monetary Values in JavaScript&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;ren.zone&#x2F;articles&#x2F;safe-money&quot;&gt;Money in the type system where it belongs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sarahdayan&#x2F;dinero.js&quot;&gt;Dinero.js&lt;&#x2F;a&gt; - An immutable library to create, calculate and format money.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>I survived Ur&#x2F;Web</title>
          <pubDate>Mon, 22 Jan 2018 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/urweb/</link>
          <guid>https://sgt.hootr.club/blog/urweb/</guid>
          <description xml:base="https://sgt.hootr.club/blog/urweb/">&lt;p&gt;I don&#x27;t remember when it was that I first tried &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;www.impredicative.com&#x2F;ur&#x2F;&quot;&gt;Ur&#x2F;Web&lt;&#x2F;a&gt;, but I&#x27;m sure I
didn&#x27;t last long with it. As soon as I strayed a bit from the examples provided
on the website (seemingly the only documentation available), I would hit a brick
wall, in the form of unreadable compiler errors. I got so frustrated that I
deleted all the files I was working on and the compiler with them, and I didn&#x27;t
touch the language for a while.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d try again after a few months, once again lured in by the
ludicrous promises listed on the front page of the website: no more complex
ORMs, no more crappy templating languages, no more brittle internal APIs, and
all of this packaged up in a neat ML-like language with a solid and powerful
type system that compiles down to very efficient C code. It was too good to be
true, and I wanted into it. Soon enough I&#x27;d hit another brick wall and the
cycle would repeat itself once again, until I got to the point where I was
confident enough in the language that I could write it for a few hours without
getting stuck too much.&lt;&#x2F;p&gt;
&lt;p&gt;As it turns out, my frustration with it is one of the language&#x27;s many features.
In the age of languages like Elm and Go, which jeopardize sophistication to
appeal to the newcomers, Ur&#x2F;Web takes Haskell&#x27;s motto of avoiding success at
all costs and runs with it. &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;www.impredicative.com&#x2F;pipermail&#x2F;ur&#x2F;2010-December&#x2F;000329.html&quot;&gt;In the words of the language&#x27;s creator&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I also want to emphasize that I&#x27;m not trying to maximize adoption of
Ur&#x2F;Web.  Rather, I&#x27;m trying to maximize the effectiveness of people who
do choose to use it.  This means that I&#x27;m completely happy if basic
features of Ur&#x2F;Web mean that 90% of programmers will never be able to
use it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Seen from this perspective, everything starts falling into place. The homepage
straight from the late 90s, the few examples and the TeX-formatted PDF manual,
the lack of any sort of documentation to the standard library that doesn&#x27;t
involve digging through the scantly commented signature files. &lt;strong&gt;It&#x27;s all
intended.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Jokes aside, while I do find the approach to language design admirable, I think
the ecosystem could use a lot of improvement; at the very least some more
comments in the stdlib signature files and possibly some way to generate an
HTML page out of them, in the style of &lt;code&gt;ocamldoc&lt;&#x2F;code&gt;. I&#x27;ve spent enough time
trying to differentiate &lt;code&gt;queryL&lt;&#x2F;code&gt; and &lt;code&gt;queryL1&lt;&#x2F;code&gt; that I&#x27;ve developed a feel for
it, but I&#x27;d much rather have a thorough explaination of why they&#x27;re named like
that.&lt;&#x2F;p&gt;
&lt;p&gt;The compiler also outputs errors which range from completely unhelpful parsing
errors to dozens and dozens of desugared XML and SQL statements which rival C++
template error messages in succintness and readability. As with the standard
library functions, the error messages are there to give you a feel for what the
compiler wants to see rather than to provide a useful explaination of what
happened.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-good-parts&quot;&gt;The good parts&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve only bashed the language up to now, so it&#x27;s about time that I start
mentioning its good parts. Ur&#x2F;Web is amazing! Part of the reason why I&#x27;m
writing this is in the hopes of getting even one other person interested in the
project, because I think it deserves more users.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s a long and winding road to get there, but Ur&#x2F;Web &lt;em&gt;does&lt;&#x2F;em&gt; deliver on its
promises of speeding up application development. Once you start working with
the compiler rather than against it, you&#x27;ll find that you only have to worry
about the parts of your application that matter rather than getting entangled
in busywork. The compiler will not only check for mismatches in the types of
every function and value in the program like in a normal statically typed
language, but it will also check them against the database tables, cookies,
form fields and any other kind of client-server interaction; your program
simply won&#x27;t compile if you query a column that doesn&#x27;t exist.&lt;&#x2F;p&gt;
&lt;p&gt;Defining tables, queries, cookies and RPC becomes pretty much effortless, and
you can achieve a much higher level of &quot;separation of concerns&quot; through
thoughtful use of the signature files. At first I tried going for a MVC-like
approach by putting all my tables and queries in a module and all pages in
another, but after a while I found that it makes more sense to define tables
and cookies right in the modules that need them, so as not to needlessly expose
them to modules that don&#x27;t. Database tables and cookies in Ur&#x2F;Web are somewhat
like normal types, so you can keep them as an implementation detail of a module
or expose them as needed.&lt;&#x2F;p&gt;
&lt;p&gt;Ur&#x2F;Web also compiles the client-side portions of your code to Javascript, so
you can write client code directly &lt;em&gt;in the page handler&lt;&#x2F;em&gt; and run the same
functions both on the server and the client (as long as they don&#x27;t use any
server- or client-specific features). Reactive page generation &lt;em&gt;à la React&lt;&#x2F;em&gt; is
also supported through the &lt;code&gt;&amp;lt;dyn&#x2F;&amp;gt;&lt;&#x2F;code&gt; tag, which lets you subscribe to a &lt;code&gt;source&lt;&#x2F;code&gt;
(basically a mutable cell, similar to &lt;code&gt;ref&lt;&#x2F;code&gt; in OCaml) and automatically reacts
when the source changes.  You can also push data asynchronously to a client with
&lt;code&gt;channel&lt;&#x2F;code&gt; and call functions that need server features without reloading the
page with &lt;code&gt;rpc&lt;&#x2F;code&gt; (which unfortunately doesn&#x27;t support file inputs, but I&#x27;m
working on an AJAX library that will let you do that).&lt;&#x2F;p&gt;
&lt;p&gt;The language works with a transactional model which marks every function that
will have a different output even with the same inputs (e.g. a random number
generator or a database query) with the type &lt;code&gt;transaction&lt;&#x2F;code&gt;, and undoes any
changes that might have been made in case of an error. I had a bit of trouble
understanding the model, mostly because &lt;code&gt;transaction&lt;&#x2F;code&gt; is a monad and I wasn&#x27;t
acquainted with the concept when I first tried the language, but after a while
it became useful and natural to wall off the effectful functions from the rest
of the code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-future&quot;&gt;The future?&lt;&#x2F;h2&gt;
&lt;p&gt;Ur&#x2F;Web is far from perfect; even with a better ecosystem, documentation and
compiler messages, there&#x27;s lots of things that annoy me (the &quot;end&quot; keyword
in &lt;code&gt;let .. in .. end&lt;&#x2F;code&gt; blocks, the lack of a buffer type to make string
manipulation less painful for the allocator, the lack of support for
interacting with data types more complex than strings and integers in C
bindings, ...), but it still feels like working with a language from the
future. A future where frameworks are actually compilers aware of the
application domain, and will check non-trivial properties of a program for
correctness.&lt;&#x2F;p&gt;
&lt;p&gt;That future, I think, is (or was) the end goal of the language&#x27;s author, Adam
Chlipala. He seems to have envisioned Ur as a language that allows syntactic
and compiler extensions to fit any sort of application domain in a similar
manner. He looks more interested in other projects these days (though he&#x27;s
still actively developing Ur&#x2F;Web) so I doubt this will ever come to fruition,
but it might be a goal worth pursuing.&lt;&#x2F;p&gt;
&lt;p&gt;Eduardo Julian, the author of the
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;LuxLang&#x2F;lux&quot;&gt;Lux programming language&lt;&#x2F;a&gt;, gave a
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=T-BZvBWiamU&quot;&gt;talk at StrangeLoop&lt;&#x2F;a&gt; last year
where he talked with the fervor of a madman about a similar dream of letting
users of his language implement domain-specific optimizations, core features
and compilation targets without having to change the language itself.
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;racket-lang.org&#x2F;&quot;&gt;Racket&lt;&#x2F;a&gt;&#x27;s goal is to be both a general-purpose
language and a language platform, with which users can implement their own
domain-specific syntax and let it interact with normal Racket code or other
domain-specific languages. There&#x27;s probably other projects with similar goals
that I don&#x27;t know of, so perhaps we&#x27;ll see more of this in the future.&lt;&#x2F;p&gt;
&lt;p&gt;Getting back to Ur&#x2F;Web, if you like functional languages, hate the current
state of web development, can bear with the lack of documentation and
StackOverflow support, and already know a bit of Haskell&#x2F;OCaml&#x2F;SML, I
recommend you try it. It takes a while for it to click, but when it does it&#x27;s a
wonderful experience, and you&#x27;ll certainly learn something about types, MLs or
even web development in the process.&lt;&#x2F;p&gt;
&lt;p&gt;For my part, I&#x27;ll try to contribute some documentation to the project to make
it a bit easier to get into the language.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;blog.ezyang.com&#x2F;2012&#x2F;07&#x2F;polymorphic-variants-in-urweb&#x2F;&quot;&gt;Polymorphic variants in Ur&#x2F;Web&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>What the hell did I do this week, anyway?</title>
          <pubDate>Mon, 22 Jan 2018 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/week-001/</link>
          <guid>https://sgt.hootr.club/blog/week-001/</guid>
          <description xml:base="https://sgt.hootr.club/blog/week-001/">&lt;p&gt;Welcome to the first installation of &lt;strong&gt;What The Hell Did I Do This Week Anyway&lt;&#x2F;strong&gt;,
the low-effort blog series where I write about random stuff that I did in the
last few days.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;emacs-strikes-again-or-the-stages-of-software-grief&quot;&gt;Emacs strikes again, or the stages of software grief&lt;&#x2F;h2&gt;
&lt;p&gt;This week I watched a bunch of Jonathan Blow&#x27;s &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;user&#x2F;jblow888&#x2F;videos&quot;&gt;screencasts&lt;&#x2F;a&gt;,
where he talks about the language he&#x27;s writing and all that business. It looks
nice. Lately he&#x27;s working on libraries, and I wish he&#x27;d take a hint from ML
functors when designing modules and module options, as he&#x27;s talked about in
the libraries discussion stream. Expose a fucking module for configuration and
make it return the full thing, using strings for that just feels painful.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, he uses emacs for all the programming in his stream, save for debugging
a bunch of stuff in C++, and looking at emacs for all this time has reawakened
the little bug in my brain that tells me to start using emacs already. I use
nvim for everything, but I&#x27;m kind of horrified by the whole vimscript business
and at the very least elisp is a saner language.&lt;br &#x2F;&gt;
Screw learning all those keybinds though. I&#x27;ve spent years on vim and I think
the modal editing business is a much better fit to editing code than the big
mess of emacs keybinds.
Screw using evil too, though, because emacs is not vi and I don&#x27;t think it
should be. Screw all the minor modes that kind of mimic vi but also kind of
don&#x27;t, and screw a lot of the packages that emacs comes preloaded with.&lt;&#x2F;p&gt;
&lt;p&gt;I downloaded the emacs source and managed to compile temacs, which is the
&quot;bare&quot; version of emacs that gets filled in with all the preloaded libraries
and elisp code, and then gets &quot;dumped&quot; to the final emacs executable we all
know and fear. I recommend &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;emacshorrors.com&#x2F;posts&#x2F;unexecute.html&quot;&gt;this&lt;&#x2F;a&gt;
for the gory details on this horrifying process.
I think there might be a few steps in the build process that should happen
after building temacs but before actually loading temacs and dumping it to
emacs, because I can&#x27;t get the damn thing to work. I invoke temacs and tell it
to load &lt;code&gt;loadup.el&lt;&#x2F;code&gt; (the file that loads all the plugins and tells emacs to
dump itself) but it keeps failing with some error message or another.
First it&#x27;s pcase, then it&#x27;s require, and who knows what the next will be.
Despite its reputation for being so hackable, this part of the build process
sure is not, and if it is it&#x27;s &lt;em&gt;very&lt;&#x2F;em&gt; undocumented.&lt;&#x2F;p&gt;
&lt;p&gt;I started writing my own loadup file in hopes of getting it right, but I ran
into some files that supposedly provide Common Lisp support which for some
reason &lt;code&gt;require&lt;&#x2F;code&gt; each other. At this point I gave up, because I was already way
past the treshold of time and effort I wanted to allocate to emacs that evening.
Thanks, jwz.&lt;&#x2F;p&gt;
&lt;p&gt;The next step in the stages of software grief is starting to implement your own
version, because you&#x27;re &lt;strong&gt;so&lt;&#x2F;strong&gt; done with whatever you were using before and its
alternatives and how hard could it possibly be anyway. Which is exactly what I
did, as soon as I remembered that Racket came with a perfectly good library for
drawing stuff on the screen, on top of which they implemented a GUI library and
the very nice editor&#x2F;IDE DrRacket.
I looked up how Racket implements the editor window, and the results weren&#x27;t
exactly what I&#x27;d call readable or encouragings, after messing a bit with the
&lt;code&gt;canvas%&lt;&#x2F;code&gt; class I decided to give up for the moment.&lt;&#x2F;p&gt;
&lt;p&gt;Which is where I reached the last stage of software grief, in which I changed
my vim colorscheme to &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ajmwagar&#x2F;vim-deus&quot;&gt;deus&lt;&#x2F;a&gt; and called
it a day.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;eight-whole-bytes-of-security&quot;&gt;Eight whole bytes of security&lt;&#x2F;h2&gt;
&lt;p&gt;I was working on my Ur&#x2F;Web imageboard, when I noticed that I accidentally typed
&quot;passwords&quot; instead of &quot;password&quot; and still managed to log in. I looked up the
implementation of &lt;code&gt;crypt&lt;&#x2F;code&gt; (the only password hashing function provided in
Ur&#x2F;Web) in the runtime library and discovered that it uses OpenSSL&#x27;s
&lt;code&gt;DES_crypt&lt;&#x2F;code&gt;, which clips passwords to 8 characters (!) and has been deemed
obsolete for decades now (with good reason). I don&#x27;t know what the
production-class Ur&#x2F;Web applications do for password hashing, but thankfully
searching for &lt;code&gt;crypt&lt;&#x2F;code&gt; in both the Bazqux Reader and UPO repos doesn&#x27;t turn
up any results. Either way, nobody had bothered to write Ur&#x2F;Web bindings to a
better password hashing algorithm, so I figured I&#x27;d do it.&lt;&#x2F;p&gt;
&lt;p&gt;The result is &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;steinuil&#x2F;urweb-bcrypt&quot;&gt;this library&lt;&#x2F;a&gt;, which
uses Solar Designer&#x27;s implementation of the algorithm. It only exposes two
functions, so integrating it with my imageboard only took something like two
minutes. I&#x27;m quite satisfied with it, though it could probably use some tests.&lt;&#x2F;p&gt;
&lt;p&gt;The other result is that I&#x27;ve submitted a PR with a comment on the &lt;code&gt;crypt&lt;&#x2F;code&gt;
function in the Ur&#x2F;Web standard library, essentially explaining that it&#x27;s
insecure and you should probably consider an alternative (such as my library).&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The social issues of programming languages</title>
          <pubDate>Sun, 29 Oct 2017 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/bikeshed/</link>
          <guid>https://sgt.hootr.club/blog/bikeshed/</guid>
          <description xml:base="https://sgt.hootr.club/blog/bikeshed/">&lt;p&gt;I&#x27;m sure you know what bikeshedding means, and even if you don&#x27;t, you&#x27;ve probably
experienced a high amount of it in some way or another. It&#x27;s a folkloristic&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;
programming term that describes the endless discussion over trivial aspects of a
piece of software, such as spaces vs tabs, or which of the zillions of JS build
systems to use, or whether operator overloading should be allowed. We&#x27;ll call these
&lt;em&gt;social issues&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The great thing about social issues is that they only exist when more than one person
is working on a project. If it&#x27;s just you, you don&#x27;t have to worry about coding
style guidelines. You don&#x27;t have to worry about what paradigm you want to use, or
to get everybody comfortable with the set of macros you&#x27;re using, or with the libraries,
or the build system. In fact, you won&#x27;t have to worry about reproducing the build
environment on machines other than your own. You might even get away with not documenting
your code, even though your future self will hate you for that.&lt;&#x2F;p&gt;
&lt;p&gt;Rudolf Winestock in his famous article
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;winestockwebdesign.com&#x2F;Essays&#x2F;Lisp_Curse.html&quot;&gt;The Lisp Curse&lt;&#x2F;a&gt; argues that
&lt;em&gt;&quot;Lisp is so powerful that problems which are technical issues in other programming
languages are social issues in Lisp.&quot;&lt;&#x2F;em&gt; This makes Lisp a great language for lone
hackers, because those social issues are easily solved by oneself, but terrible
for working with other people, because you&#x27;ll have to spend so much time just defining
a common language that everybody agrees to use that you&#x27;ll never see the end of it.&lt;&#x2F;p&gt;
&lt;p&gt;This is a problem in C++, too: the joke goes that the language is so big and full
of features that you&#x27;ll end up using only 10% of it for any given task, but everybody
disagrees on what that 10% should be.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enter-go&quot;&gt;Enter Go&lt;&#x2F;h2&gt;
&lt;p&gt;Go is a pretty &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;_1GZShA1F20?t=42m13s&quot;&gt;unremarkable&lt;&#x2F;a&gt; programming
language, with a feature set that &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;cowlark.com&#x2F;2009-11-15-go&#x2F;&quot;&gt;rivals&lt;&#x2F;a&gt; that
of languages from the 60s, and it&#x27;s also one of the most popular programming languages
of the last few years. I think the reason is that whatever thought didn&#x27;t go into
making a good programming language went into solving the social issues that the
other languages suffer from, and if you ask people who have experience with many
languages what they like about Go they&#x27;ll mostly praise what you could call its
&quot;user experience&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Go tries to solve the social issues by providing solutions for them by default in
its standard installation, down to including a code formatter that the vast majority
of projects require you to run before committing any code to source control.&lt;&#x2F;p&gt;
&lt;p&gt;Go also makes lots of tradeoffs in the language itself to make compilation faster:
the type system is very limited and you don&#x27;t get type inference other than some
syntactic sugar for C++&#x27;s &lt;code&gt;auto&lt;&#x2F;code&gt; keyword, because the compiler would have to perform
more extensive type checking. Generics also don&#x27;t exist, because you&#x27;d have to generate
code for each instantiation at compile time.&lt;&#x2F;p&gt;
&lt;p&gt;I personally don&#x27;t agree that these tradeoffs are really worth it, but it seems
to be working. People love Go (i.e. its tooling), and they love how they don&#x27;t have
to think about all the things Go provides anymore, which I think raises a good point:
isn&#x27;t not worrying about the unimportant parts what programming languages are about?
And if so, &lt;strong&gt;what good is a new language that doesn&#x27;t try to solve these issues?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lessons-to-be-learned&quot;&gt;Lessons to be learned&lt;&#x2F;h2&gt;
&lt;p&gt;I think that there&#x27;s lots of better languages out there that should learn a thing
or two from Go, if they want to succeed.&lt;&#x2F;p&gt;
&lt;p&gt;Elm seems to be a step in the right direction: it takes a lot from Haskell, and
makes a lot of decisions and tradeoffs to be easier to learn for newcomers. Elm
places a tremendous amount of effort into appealing to newcomers by having a very
small core language, integrating many tools into its standard distribution like
Go, and making error messages look very friendly and easy to understand.&lt;&#x2F;p&gt;
&lt;p&gt;Then again, many of these improvements are implemented without regard for the more
experienced developers. Elm is a great language and its architecture is a really
good paradigm, but many complain that it scales poorly when project sizes increase,
and without stronger abstraction facilities like type classes you&#x27;re bound to end
up with a lot of boilerplate. Frankly, I don&#x27;t think Evan himself knows where he
wants to bring the language in the future.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;reasonml.github.io&#x2F;&quot;&gt;Reason&lt;&#x2F;a&gt; also seems to be somewhat promising, being
funded by Facebook and all. It&#x27;s nothing but a layer of paint on top of OCaml to
make it look more like Javascript, but somehow it&#x27;s managed to make people buy into
it. The Reason team also seems to be working on the OCaml compiler to make it produce
error messages that look like Elm&#x27;s. With a bit of luck, this will bring more people
over to OCaml and, over time, improve its ecosystem.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;I&#x27;ve stolen this use of the term from &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=4PaWFYm0kEw&quot;&gt;this talk&lt;&#x2F;a&gt; by Bryan Cantrill. It&#x27;s a good talk.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Continuations, Promises, and call&#x2F;cc</title>
          <pubDate>Fri, 27 Oct 2017 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/call-cc/</link>
          <guid>https://sgt.hootr.club/blog/call-cc/</guid>
          <description xml:base="https://sgt.hootr.club/blog/call-cc/">&lt;p&gt;Say you&#x27;re in the kitchen in front of the refrigerator, thinking about continuations.
You make yourself a sandwich right there and stick it on your desk.
Then you sit on your armchair and open a browser window to search for their definition.
You get a series of abstract explainations, and some examples involving fridges and
sandwiches, which leave you more puzzled than when you began.
You invoke the sandwich on your desk, and you find yourself wondering if the topic
is even worth going through all this trouble to learn about.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;promises&quot;&gt;Promises&lt;&#x2F;h2&gt;
&lt;p&gt;Javascript has a concurrency model which can be somewhat daunting at first:
it relies on asynchronous functions, which have to be handled differently from normal ones.
If you&#x27;ve ever written any JS in the last few years you must&#x27;ve used a lot of &lt;code&gt;Promise&lt;&#x2F;code&gt;s,
&lt;code&gt;async&lt;&#x2F;code&gt;s, and &lt;code&gt;await&lt;&#x2F;code&gt;s, but if you haven&#x27;t here&#x27;s a quick reminder.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8FBCBB;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The value of &lt;code&gt;Promise.resolve(2)&lt;&#x2F;code&gt; is not quite 2, but rather a &lt;em&gt;computation&lt;&#x2F;em&gt; that yields 2.
The only way to increase that 2 is by passing a function to its Promise.&lt;&#x2F;p&gt;
&lt;p&gt;Javascript does this because it runs on a single thread, and making long blocking
computations (like an AJAX request) run synchronously would make the rest of the program,
and the whole page it&#x27;s running on, lock up while it&#x27;s waiting for that computation to finish,
and you really don&#x27;t want that to happen on your website.
But try as you might, you will never be able to make that Promise return a 2,
store it in a variable and resume with your normal program flow,
like you&#x27;d do with a normal function (even though the &lt;code&gt;async&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;await&lt;&#x2F;code&gt; constructs adds a bit of
syntactic sugar to make handling Promises similar to normal code).&lt;&#x2F;p&gt;
&lt;p&gt;The only way to access Promised values you&#x27;re left with is passing a function to their Promise
with the rest of the computations that have to performed on that value.
This style of programming is called &lt;strong&gt;continuation-passing style&lt;&#x2F;strong&gt; (CPS).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;continuations&quot;&gt;Continuations&lt;&#x2F;h2&gt;
&lt;p&gt;Simply put, the continuation to a certain value is the part of the program
that needs to wait for that value to continue execution (&quot;the rest&quot; of the program).&lt;&#x2F;p&gt;
&lt;p&gt;Continuations are easily represented by functions (but not quite&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;) like the ones you
pass to a Promise.
Take a simple program &lt;code&gt;stuff() + 2&lt;&#x2F;code&gt;. The continuation of &lt;code&gt;stuff()&lt;&#x2F;code&gt; could be represented
by the function &lt;code&gt;x =&amp;gt; x + 2&lt;&#x2F;code&gt;, while the continuation of &lt;code&gt;2&lt;&#x2F;code&gt; would be &lt;code&gt;x =&amp;gt; stuff() + x&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Promises can never return a value, so the only way to do something with their result is to
&lt;em&gt;reify&lt;&#x2F;em&gt; their continuation to a function. Reifying a continuation involves a simple rewrite
that wraps the continuation in a function taking one argument, and replaces the value
with that argument.
This rewrite is applied by many compilers as an intermediate step to simplify the language.&lt;&#x2F;p&gt;
&lt;p&gt;For example, take the classic definition of the factorial function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; fact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  if&lt;&#x2F;span&gt;&lt;span&gt; (n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  else return&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; fact&lt;&#x2F;span&gt;&lt;span&gt;(n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; &#x2F;&#x2F;=&amp;gt; 6&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We could rewrite this in CPS by taking the continuation of
the tail invocation of &lt;code&gt;fact&lt;&#x2F;code&gt; and reifying it to a function,
then passing that function to &lt;code&gt;fact&lt;&#x2F;code&gt; and calling it on the result.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; factCont&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; cont&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  if&lt;&#x2F;span&gt;&lt;span&gt; (n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; cont&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;  else&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; factCont&lt;&#x2F;span&gt;&lt;span&gt;(n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt; cont&lt;&#x2F;span&gt;&lt;span&gt;(n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;factCont&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; console&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;log)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt; &#x2F;&#x2F;=&amp;gt; 6&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you pass two functions to it, one representing success and the other failure,
you have Promises! (without the asynchronicity.)&lt;&#x2F;p&gt;
&lt;p&gt;For more a more thorough explaination and some advantages of this style, I recommend
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;matt.might.net&#x2F;articles&#x2F;by-example-continuation-passing-style&#x2F;&quot;&gt;Matt Might&#x27;s&lt;&#x2F;a&gt;
&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;matt.might.net&#x2F;articles&#x2F;programming-with-continuations--exceptions-backtracking-search-threads-generators-coroutines&#x2F;&quot;&gt;posts&lt;&#x2F;a&gt;
on the topic.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;call-cc&quot;&gt;call&#x2F;cc&lt;&#x2F;h2&gt;
&lt;p&gt;The problem with CPS is that nobody in their right mind would willingly write their code
like this. Most languages offer a few constructs that have similar effects to some of
the uses of explicit continuations, such as early returns, exceptions, gotos, or async&#x2F;await.
Others, like Scheme, SML and &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;ruby-doc.org&#x2F;core-2.4.1&#x2F;Kernel.html#method-i-callcc&quot;&gt;Ruby&lt;&#x2F;a&gt;,
give you first class access to raw undelimited continuations through a
construct called &lt;em&gt;call-with-current-continuation&lt;&#x2F;em&gt;, abbreviated to &lt;code&gt;call&#x2F;cc&lt;&#x2F;code&gt;
or &lt;code&gt;callcc&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;call&#x2F;cc takes a function of one argument, and calls it with the current (relative to
where call&#x2F;cc was called) continuation as its argument (historically called &lt;code&gt;k&lt;&#x2F;code&gt;).
Invoking &lt;code&gt;k&lt;&#x2F;code&gt; with a value, also referred to as &quot;throwing&quot;, will set the
continuation to that value, thus emulating an early return.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;common-lisp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(call&#x2F;cc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (display &amp;#39;before)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (display &amp;#39;after)))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; =&amp;gt; before&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Storing the current continuation in a mutable cell allows one to return to that point in
the program from anywhere else, in a way that&#x27;s similar to gotos or C&#x27;s &lt;code&gt;setjmp&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;longjmp&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;common-lisp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(define counter #f)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; ((x &lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt; ()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     (call&#x2F;cc (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt; (k) (set! counter k)))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     (set! x (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B48EAD;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt; x))))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (display x))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; =&amp;gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(counter) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; =&amp;gt; 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(counter) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; =&amp;gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Combining these two cases, one can implement even more complex and convoluted control
flow structures, like exceptions, coroutines, or logic programming-style backtracking.
The implementations are &lt;s&gt;a pain to write so I left them out&lt;&#x2F;s&gt; left as an exercise to
the reader.&lt;&#x2F;p&gt;
&lt;p&gt;There have been a number of &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;okmij.org&#x2F;ftp&#x2F;continuations&#x2F;against-callcc.html&quot;&gt;points&lt;&#x2F;a&gt;
raised against call&#x2F;cc and first-class access to undelimited continuations, many
of which propose &lt;em&gt;delimited&lt;&#x2F;em&gt; continuations as a cleaner, less costly alternative,
but I&#x27;ll be taking a look at those in a later post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sandwiches&quot;&gt;Sandwiches&lt;&#x2F;h2&gt;
&lt;p&gt;Getting back to the sandwich example (or sand-witch, as &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;groups.google.com&#x2F;forum&#x2F;#!msg&#x2F;perl.perl6.language&#x2F;-KFNPaLL2yE&#x2F;_RzO8Fenz7AJ&quot;&gt;the author of this quote&lt;&#x2F;a&gt;
stubbornly calls it):&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Say you&#x27;re in the kitchen in front of the refrigerator, thinking about a
sandwitch.  You take a continuation right there and stick it in your
pocket.  Then you get some turkey and bread out of the refrigerator and
make yourself a sandwitch, which is now sitting on the counter.  You
invoke the continuation in your pocket, and you find yourself standing
in front of the refrigerator again, thinking about a sandwitch.  But
fortunately, there&#x27;s a sandwitch on the counter, and all the materials
used to make it are gone.  So you eat it. :-)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It becomes obvious once you translate it to code:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #D8DEE9; background-color: #2E3440;&quot;&gt;&lt;code data-lang=&quot;common-lisp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(define (eat-sandwich sandwich)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (display &lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A3BE8C;&quot;&gt;burp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #ECEFF4;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; ((pocket #f)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      (fridge &amp;#39;(turkey bread))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      (counter &amp;#39;()))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;  ; in front of the refrigerator&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (call&#x2F;cc (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt; (k) (set! pocket k)))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt; (empty? counter) (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;not&lt;&#x2F;span&gt;&lt;span&gt; (empty? fridge)))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #81A1C1;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; ((sandwich (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;cons&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;car&lt;&#x2F;span&gt;&lt;span&gt; fridge) (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;cadr&lt;&#x2F;span&gt;&lt;span&gt; fridge))))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      (set! fridge &amp;#39;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      (set! counter (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span&gt; sandwich))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      (pocket))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (eat-sandwich (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88C0D0;&quot;&gt;car&lt;&#x2F;span&gt;&lt;span&gt; counter))))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #616E88;&quot;&gt;; =&amp;gt; burp&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The program saves its continuation. Then it checks if there&#x27;s anything on
the counter and, finding nothing, it makes the sandwich and invokes the
previously saved continuation, returning to the beginning of the conditional.
This time it finds a sandwich in the counter, so it can start eating.&lt;&#x2F;p&gt;
&lt;p&gt;Lucky bastard. It gets to travel back in time, while I&#x27;m left here writing posts
that the past me will never get a glimpse of.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;It is important to remember that undelimited continuations are not proper functions, because they cannot in any case return a value: in a typed language aware of continuations, a continuation would have a return type equivalent to &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;basic-types.html#never&quot;&gt;TypeScript&#x27;s &lt;code&gt;never&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, or a type annotation like &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;en.cppreference.com&#x2F;w&#x2F;c&#x2F;language&#x2F;_Noreturn&quot;&gt;C&#x27;s &lt;code&gt;_Noreturn&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. See &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;okmij.org&#x2F;ftp&#x2F;continuations&#x2F;undelimited.html&quot;&gt;Oleg Kiselyov&#x27;s explaination&lt;&#x2F;a&gt; for more information on this.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>The TTY Protocol</title>
          <pubDate>Fri, 10 Feb 2017 00:00:00 +0000</pubDate>
          <author>steen</author>
          <link>https://sgt.hootr.club/blog/tty/</link>
          <guid>https://sgt.hootr.club/blog/tty/</guid>
          <description xml:base="https://sgt.hootr.club/blog/tty/">&lt;p&gt;A few months ago, &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;destroyallsoftware.com&quot;&gt;Gary Bernhardt&lt;&#x2F;a&gt; streamed himself
writing a text editor. I&#x27;m not sure he ever finished it, and for some reason he took down
the VoDs on Twitch, but the little I&#x27;ve seen was still very interesting.&lt;&#x2F;p&gt;
&lt;p&gt;Writing my own text editor (and using it) has always been one of my dreams and, being a
vim user, I&#x27;d much rather have it run on my terminal. Bernhardt shares my opinion, so he
started by writing (or rather, copying from one of his previous projects) the terminal
interaction code. I&#x27;ve always thought terminal interaction would be pretty complicated,
but it turned out to be a very small amount of code.&lt;&#x2F;p&gt;
&lt;p&gt;This got me wondering about the protocol.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-brief-incomplete-and-mostly-wrong-history-of-the-tty-protocol&quot;&gt;A brief, incomplete, and mostly wrong history of the TTY protocol&lt;&#x2F;h2&gt;
&lt;p&gt;The TTY protocol predates computers by quite a bit, and hasn&#x27;t changed much since the
late 19th century. The protocol was created for &lt;strong&gt;teletypewriters&lt;&#x2F;strong&gt;, or teletypes,
to communicate with each other: it included not only printable characters such as
numbers, alphabets, and punctuation, but also directives for the teletype that were
not supposed to be printed called &lt;strong&gt;control characters&lt;&#x2F;strong&gt;, such as Line Feed (&lt;code&gt;\n&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;The character encodings that we still have to deal with nowadays also largely
come from back then: Unicode later tried to patch them all together, but it was already too late.&lt;&#x2F;p&gt;
&lt;p&gt;Then computers came along. Printing the result of a computation to a sheet of paper
was way easier than interpreting a bunch of lights on a front panel, so they stuck
teletypes to them, and thus computers learnt the TTY protocol too.&lt;&#x2F;p&gt;
&lt;p&gt;When screens became widespread, the protocol acquired more commands known as
&lt;strong&gt;escape sequences&lt;&#x2F;strong&gt;, so called because they were prefixed by
ESC (&lt;code&gt;0x1B&lt;&#x2F;code&gt;). These allowed computers to move the cursor arbitrarily on the screen,
clearing part of it or applying some effects such as inverting the background and
the foreground color.&lt;&#x2F;p&gt;
&lt;p&gt;Some terminals, such as the Tektronix 4010 series, also allowed drawing lines on
the screen thanks to their vector displays. These unfortunately died an untimely
death (even though a tek emulation mode still lives on inside xterm), and only the
text-based VTs stuck.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summing-up&quot;&gt;Summing up&lt;&#x2F;h2&gt;
&lt;p&gt;For most intents and purposes, &lt;code&gt;&#x2F;dev&#x2F;tty&lt;&#x2F;code&gt; still behaves a lot like a physical
typewriter: you can scroll the &quot;sheet of paper&quot;, you can move the cursor
around, you can imbue its head with colored ink or special effects. You can easily
find your way around with &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;www.xfree86.org&#x2F;4.5.0&#x2F;ctlseqs.html&quot;&gt;a control sequences chart&lt;&#x2F;a&gt;,
and it&#x27;s not hard to emulate ncurses or readline just by printing a few characters
in &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Cooked_mode&quot;&gt;raw mode&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The TTY is unfortunately very stateful, but &lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nsf&#x2F;termbox&quot;&gt;termbox&lt;&#x2F;a&gt;
provides a very nice abstraction: it essentially turns all interactions stateless by printing a few more escape sequences every time you write something.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not sure if I&#x27;ll ever write my own text editor, but it&#x27;s certainly fun to play
with terminal graphics and line editing using nothing but &lt;code&gt;write&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.destroyallsoftware.com&#x2F;talks&#x2F;a-whole-new-world&quot;&gt;Gary Bernhardt - A Whole New World&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;garbagecollected.org&#x2F;2017&#x2F;01&#x2F;31&#x2F;four-column-ascii&#x2F;&quot;&gt;Four Column ASCII&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;www.linusakesson.net&#x2F;programming&#x2F;tty&#x2F;&quot;&gt;The TTY demystified&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pqwy&#x2F;notty&quot;&gt;Declarative terminal graphics for OCaml&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;use.perl.org&#x2F;use.perl.org&#x2F;_scrottie&#x2F;journal&#x2F;39195.html&quot;&gt;Playing with Tektronix emulation for vector graphics!&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;withoutboats&#x2F;notty&quot;&gt;notty: A new kind of terminal&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;systemswe.love&#x2F;videos&#x2F;utf-8&quot;&gt;Daniel Morsing - UTF-8&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;utcc.utoronto.ca&#x2F;~cks&#x2F;space&#x2F;blog&#x2F;unix&#x2F;HowUnixBackspaces&quot;&gt;How Unix erases things when you type a backspace while entering text&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;denisbider.blogspot.com&#x2F;2015&#x2F;09&#x2F;when-monospace-fonts-arent-unicode.html&quot;&gt;When monospace fonts aren&#x27;t: The Unicode character width nightmare&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
    </channel>
</rss>
