<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[sitr.us - posts by Jesse Hallett]]></title><description><![CDATA[posts by Jesse Hallett]]></description><link>http://github.com/dylang/node-rss</link><generator>GatsbyJS</generator><lastBuildDate>Sat, 09 Mar 2024 06:36:41 GMT</lastBuildDate><item><title><![CDATA[Nix, NPM, and Dependabot]]></title><link>https://sitr.us/2024/03/08/nix-npm-and-dependabot.html</link><guid isPermaLink="false">https://sitr.us/2024/03/08/nix-npm-and-dependabot.html</guid><pubDate>Fri, 08 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have a project, &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://github.com/hallettj/git-format-staged&quot; class=&quot;css-1od09yo&quot;&gt;git-format-staged&lt;/a&gt;, that I build with Nix.
It includes NPM dependencies, and it is convenient to have &lt;a href=&quot;https://github.blog/2020-06-01-keep-all-your-packages-up-to-date-with-dependabot/&quot; class=&quot;css-1od09yo&quot;&gt;Dependabot&lt;/a&gt; keep
those up-to-date for me.
Dependabot creates pull requests that update &lt;code class=&quot;css-0&quot;&gt;package-lock.json&lt;/code&gt; when it sees
updates with security fixes and such.
But my Nix configuration includes a hash of the project NPM dependencies -
that hash must be updated when &lt;code class=&quot;css-0&quot;&gt;package-lock.json&lt;/code&gt; changes.
Unfortunately Dependabot does not know how to do that.
So I came up with a workflow to help that bot out.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The hash is in &lt;a href=&quot;https://github.com/hallettj/git-format-staged/blob/972b79c242ede25387503e69726efc90830ff545/test/test.nix#L12&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;test/test.nix&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The product of that repo is actually a zero-dependency Python program.
I&amp;#x27;m just using Node and NPM to run a test framework (for perfectly-valid reasons).
I have implemented test runs as derivations which means they run in Nix&amp;#x27;
sandboxed build environment.
To get reproducibility that means network requests are not allowed unless
I specify the hash of what&amp;#x27;s going to be downloaded up front.
The hash here is a recursive hash of a directory of downloaded NPM packages that
can be installed later by running &lt;code class=&quot;css-0&quot;&gt;npm install --cache&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;(When I&amp;#x27;m working on Rust projects I use &lt;a href=&quot;https://crane.dev/&quot; class=&quot;css-1od09yo&quot;&gt;Crane&lt;/a&gt; which is able to infer
dependency hashes from &lt;code class=&quot;css-0&quot;&gt;Cargo.lock&lt;/code&gt; so I don&amp;#x27;t need to update a hash in a Nix
expression when dependencies change. I haven&amp;#x27;t found a tool that does that for
NPM, so for now at least I have this hash to keep up to date.)&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;updating-the-hash&quot; class=&quot;css-1bzbprl&quot;&gt;Updating the Hash&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;So what I want is an automated process that updates that hash when
&lt;code class=&quot;css-0&quot;&gt;package-lock.json&lt;/code&gt; changes.
That means I need to be able to:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ol class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;compute the new dependencies hash&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;update &lt;code class=&quot;css-0&quot;&gt;test/test.nix&lt;/code&gt; with the new hash&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are existing solutions out there for doing this kind of thing.
For example the nixpkgs repo has
&lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/d1aa2475eb5d4bc33a1a10ded347b7d64d78674c/maintainers/scripts/update.nix&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;maintainers/scripts/update.nix&lt;/code&gt;&lt;/a&gt;.
I did some looking to see if there is something out there that would work for
me.
But then I decided it would be easier to write my own solution.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/d1aa2475eb5d4bc33a1a10ded347b7d64d78674c/pkgs/build-support/node/fetch-npm-deps/default.nix&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;fetchNpmDeps&lt;/code&gt;&lt;/a&gt;
is a fetcher in nixpkgs that is specialized for fetching NPM dependencies -
given a source directory with a &lt;code class=&quot;css-0&quot;&gt;package-lock.json&lt;/code&gt; file it fetches exactly what
you need.
Most Nix fetchers come with a corresponding &amp;quot;prefetch&amp;quot; tool that tells you the
hash of the fetched content.
&lt;code class=&quot;css-0&quot;&gt;fetchNpmDeps&lt;/code&gt; is paired with &lt;code class=&quot;css-0&quot;&gt;prefetch-npm-deps&lt;/code&gt; (defined in the same file) for
that purpose.
I can use &lt;code class=&quot;css-0&quot;&gt;prefetch-npm-deps&lt;/code&gt; for step 1,
and a little sed for step 2.
I ended up with this package definition in my
&lt;a href=&quot;https://github.com/hallettj/git-format-staged/blob/972b79c242ede25387503e69726efc90830ff545/flake.nix#L18&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;flake.nix&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Now I can run this command to update the hash automatically:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The script ends up being quite simple,
partly because I only have one hash in &lt;code class=&quot;css-0&quot;&gt;test/test.nix&lt;/code&gt; so it is easy to target
the sed script.
I could keep things cleaner by separating the hashes out into a separate file:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In that case the update script would look like this:&lt;/p&gt;&lt;h2 id=&quot;automation-to-help-dependabot&quot; class=&quot;css-1bzbprl&quot;&gt;Automation to Help Dependabot&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Every one of the Dependabot PRs in my repo was failing required checks because
the PRs updated &lt;code class=&quot;css-0&quot;&gt;package-lock.json&lt;/code&gt;,
but did not update that hash.
So the next step was to set up a Github workflow to run the hash-updating script
after every change from Dependabot.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It would be great if I could configure Dependabot to run a custom shell command
along with its updates.
But as far as I can tell that is not an option.
Instead I added a workflow that runs on pushes to Dependabot&amp;#x27;s PR branches.
Those all have names of the form &lt;code class=&quot;css-0&quot;&gt;dependabot/npm_and_yarn/*&lt;/code&gt;.
After some research I used this workflow,
&lt;a href=&quot;https://github.com/hallettj/git-format-staged/blob/972b79c242ede25387503e69726efc90830ff545/.github/workflows/dependabot-post.yml&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;.github/workflows/dependabot-post.yml&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This workflow enables a special permission to allow it to push commits back to
the repo.
That can be dangerous if you have third-party code running because that code
would have access to modify your repository.
Notably NPM packages can run arbitrary code when they are installed.
But that is not an issue here:
&lt;code class=&quot;css-0&quot;&gt;prefetch-npm-deps&lt;/code&gt; does not &amp;quot;install&amp;quot; dependencies so it does not run package
install scripts.
Instead it pre-populates a local cache that can be &amp;quot;installed&amp;quot; later.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Normally Dependabot will refuse to automatically update one of its PRs after
someone else has pushed commits to it.
The workflow includes &lt;code class=&quot;css-0&quot;&gt;[dependabot skip]&lt;/code&gt; in its commit messages to signal to
Dependabot that it is OK to throw those commits out when it recreates or rebases
its PRs.
When that happens the workflow runs again, and re-applies the correct hash.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Note that I don&amp;#x27;t have to worry about getting into an infinite loop because once
the correct hash is set any subsequent runs will be noops, and so will not
trigger more branch push events.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;So now I&amp;#x27;m happily automated, and I&amp;#x27;ve been merging a bunch of Dependabot PRs
that I let pile up.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[`void` is not a unit type in TypeScript]]></title><link>https://sitr.us/2021/12/04/void-is-not-a-unit-type.html</link><guid isPermaLink="false">https://sitr.us/2021/12/04/void-is-not-a-unit-type.html</guid><pubDate>Sat, 04 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In type theory a &lt;strong class=&quot;css-0&quot;&gt;unit type&lt;/strong&gt; is any type that represents exactly one possible
value.
The unit types in TypeScript include &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;, and literal types.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;TypeScript also has the type &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; which is used as the return type for
functions that don&amp;#x27;t have an explicit return value.
In JavaScript a function that does not explicitly return implicitly returns
&lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;;
so at first glance it would seem that &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; is an alias for the &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;
type.
But it is not!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We can test this by checking assignability:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If they were the same type, &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; would be mutually assignable.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;According to the &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/basic-types.html#void&quot; class=&quot;css-1od09yo&quot;&gt;Handbook&lt;/a&gt;,
&amp;quot;[&lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; is] the absence of having any type at all.&amp;quot;
TypeScript is nicely consistent with the behavior of types as sets.
(&lt;code class=&quot;css-0&quot;&gt;any&lt;/code&gt; is an exception because it does not behave like a well-defined set.)
&amp;quot;The absence of any type at all&amp;quot; is not a sensical statement about sets;
so that description seems odd to me.
But that may be a way of explaining that in some ways &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt;, like &lt;code class=&quot;css-0&quot;&gt;any&lt;/code&gt;, does
not behave as a well-defined set.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt; is assignable to &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; which tells us that the type &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; does
include the value &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;.
From a type theory perspective the fact that the reverse is not allowed implies
that &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; represents a set of possible values that includes values other than
&lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;.
And we can see that is true in practice.
If you have a variable with a function type where the return type is &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt;, any
function is assignable to that variable regardless of the actual return type as
long as the argument types are compatible.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This behavior is convenient - instead of requiring certain return values the use
of &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; in this case is more like a statement by the caller that it will
ignore the return value of the callback.
But we can see in this example that although the type of &lt;code class=&quot;css-0&quot;&gt;x&lt;/code&gt; is &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt;,
values assigned to &lt;code class=&quot;css-0&quot;&gt;x&lt;/code&gt; could strings, numbers, or booleans.
In fact &lt;code class=&quot;css-0&quot;&gt;x&lt;/code&gt; could be assigned any type of value!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This makes it look like it would be most accurate to think of &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; as an alias
for &lt;code class=&quot;css-0&quot;&gt;unknown&lt;/code&gt;.
That would be consistent with the assignability tests from before:
&lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt; is assignable to &lt;code class=&quot;css-0&quot;&gt;unknown&lt;/code&gt;, but &lt;code class=&quot;css-0&quot;&gt;unknown&lt;/code&gt; is not assignable to
&lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;.
But now let&amp;#x27;s look at the ways in which &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; does not behave like a set.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; were a set that contains every possible value
(and in the &lt;code class=&quot;css-0&quot;&gt;foo&lt;/code&gt; example we have seen that in practice it is)
then we should be able to assign any value to a variable of type &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt;.
But it turns out that the only value that TypeScript will permit assigning to
a variable of type &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; is &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It looks like either TypeScript has a special rule that prohibits assignment to
&lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; variables, or TypeScript thinks of &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; as a small set but the &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt;
return callback feature lets non-&lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; values &amp;quot;leak&amp;quot; into &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; variables.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;My suggestion for making TypeScript more consistent is to make &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; a proper
alias of &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;.
It&amp;#x27;s possible the reason that it was not set up that way in the first place is
that &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; predates &lt;code class=&quot;css-0&quot;&gt;unknown&lt;/code&gt;.
In any case, that is how I am going to think of &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; going forward.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Choosing a headless CMS]]></title><link>https://sitr.us/2020/04/29/choosing-a-headless-cms.html</link><guid isPermaLink="false">https://sitr.us/2020/04/29/choosing-a-headless-cms.html</guid><pubDate>Wed, 29 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;This is an excerpt of &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://medium.com/p/choosing-a-headless-cms-4810c259b33?source=email-f7888c2f0f22--writer.postDistributed&amp;amp;sk=f6cbef65e5721137b69bc77fda0d5d4c&quot; class=&quot;css-1od09yo&quot;&gt;a post&lt;/a&gt; that I wrote for my employer, &lt;a href=&quot;https://www.originate.com/&quot; class=&quot;css-1od09yo&quot;&gt;Originate&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;One of our recent projects called for a feature to allow editors to manage and
publish content. In other words, we needed a CMS.  We already had a React-based
web app, and a React Native mobile app in the works where we wanted to present
that content. So we needed a Headless CMS. Unlike Wordpress or other traditional
CMS solutions, a headless CMS does not come with a web interface where readers
will see published content. Instead, a headless system acts more like
a database: it hosts content, and your app uses an API to fetch content to
display. As a result, your choice of app architecture is not tied to your choice
of CMS. This can be especially helpful if you want to incorporate managed
content into an app that is not primarily a publishing platform, or if you want
to present the same content differently in different contexts as was the case
with our web vs mobile apps.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;One of the biggest challenges with headless CMS systems is choosing one. The
field is wide open, and headless systems have not been around long enough for
a clear leader to stand out.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;&lt;a href=&quot;https://medium.com/p/choosing-a-headless-cms-4810c259b33?source=email-f7888c2f0f22--writer.postDistributed&amp;amp;sk=f6cbef65e5721137b69bc77fda0d5d4c&quot; class=&quot;css-1od09yo&quot;&gt;» Read the rest&lt;/a&gt; on the Originate blog.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[When to use `never` and `unknown` in TypeScript]]></title><link>https://sitr.us/2019/01/29/never-and-unknown-in-typescript.html</link><guid isPermaLink="false">https://sitr.us/2019/01/29/never-and-unknown-in-typescript.html</guid><pubDate>Tue, 29 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;This is an excerpt of &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://blog.logrocket.com/when-to-use-never-and-unknown-in-typescript-5e4d6c5799ad/&quot; class=&quot;css-1od09yo&quot;&gt;a post&lt;/a&gt; that I wrote for &lt;a href=&quot;https://logrocket.com/&quot; class=&quot;css-1od09yo&quot;&gt;LogRocket&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:60%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAABaElEQVQoz22S6Y6CQBCEef+3MoFojBiJxAgqiaJyCAIegAfsfrsdOTbbPyY9M11VPdWjfH2irmvWx+ORpqnruqvVarfbeZ53PB7jOC6KoqlpQumCT6eT7/tZlr1eL5DkYPI8v16vXIVhWFXVP2CQqJ3PZwCWZSGL2uVyud/v5LZtO45zOBz2+/3z+WzB0gntIbhcLqfTKRQQDYdDpDjRdT0IAk7G4/Fms4Gip0xvCIKfzWZs6RNGWh0MBlBIAYKsECHOlTT7A0YTk9brtbAmSQKexDAMxOWEAhIaAcz7WzAXt9uNtwGGlZwL8JPJhPdT0PgEnZjagnFFjKXJxWLBtDgkH41GmqbN53OZE/4DjqIIU1owgR9lWW63W1VVqQBJh2zJebn+GzgCCxQMsjcq9qZp4iqstIp54gUhhvEKmkeD+fXclmnhJ2aA//MT3u83J5DSCHZ0/5nSrZMJyWcIPiFbDBfS7g/9BvwhoKkdTtxjAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;venn diagram of the string and number types, and their union&quot; title=&quot;venn diagram of the string and number types, and their union&quot; src=&quot;/static/557252cc1472b6e00df3db6ff0c367d0/7d769/string-union-number.png&quot; srcSet=&quot;/static/557252cc1472b6e00df3db6ff0c367d0/5243c/string-union-number.png 240w,/static/557252cc1472b6e00df3db6ff0c367d0/ab158/string-union-number.png 480w,/static/557252cc1472b6e00df3db6ff0c367d0/7d769/string-union-number.png 960w,/static/557252cc1472b6e00df3db6ff0c367d0/87339/string-union-number.png 1440w,/static/557252cc1472b6e00df3db6ff0c367d0/0d4f8/string-union-number.png 1600w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;never&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;unknown&lt;/code&gt; primitive types were introduced in TypeScript &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html&quot; class=&quot;css-1od09yo&quot;&gt;v2.0&lt;/a&gt;
and &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html&quot; class=&quot;css-1od09yo&quot;&gt;v3.0&lt;/a&gt; respectively. These two types represent fundamental and
complementary aspects of type theory. TypeScript is carefully designed according
to principles of type theory, but it is also a practical language, and its
features all have practical uses – including &lt;code class=&quot;css-0&quot;&gt;never&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;unknown&lt;/code&gt;. To
understand those uses we will have to begin with the question, what exactly are
types?&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;types-explained-using-set-theory&quot; class=&quot;css-1bzbprl&quot;&gt;Types explained using set theory&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When you get down to a fundamental definition a type is a set of possible
values, and nothing more. For example, the type &lt;code class=&quot;css-0&quot;&gt;string&lt;/code&gt; in TypeScript is the
set of all possible strings. The type &lt;code class=&quot;css-0&quot;&gt;Date&lt;/code&gt; is the set of all instances of the
&lt;code class=&quot;css-0&quot;&gt;Date&lt;/code&gt; class (plus all structurally-compatible objects), and the type
&lt;code class=&quot;css-0&quot;&gt;Iterable&amp;lt;T&amp;gt;&lt;/code&gt; is the set of all objects that implement the iterable interface
for the given type of iterated values.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;TypeScript is especially faithful to the set-theoretic basis for types; among
other features, TypeScript has union and intersection types. A type like &lt;code class=&quot;css-0&quot;&gt;string
| number&lt;/code&gt; is called a &amp;quot;union&amp;#x27; type because it literally is the union of the set
of all strings, and the set of all numbers.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;&lt;a href=&quot;https://blog.logrocket.com/when-to-use-never-and-unknown-in-typescript-5e4d6c5799ad/&quot; class=&quot;css-1od09yo&quot;&gt;» Read the rest&lt;/a&gt; on the LogRocket blog.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Automatic Code Formatting for Partially-Staged Files]]></title><link>https://sitr.us/2018/08/16/automatic-code-formatting.html</link><guid isPermaLink="false">https://sitr.us/2018/08/16/automatic-code-formatting.html</guid><pubDate>Thu, 16 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;This is an excerpt of &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://www.olioapps.com/blog/automatic-code-formatting/&quot; class=&quot;css-1od09yo&quot;&gt;a post&lt;/a&gt; that I wrote for &lt;a href=&quot;https://www.olioapps.com/&quot; class=&quot;css-1od09yo&quot;&gt;Olio Apps&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I wrote &lt;a href=&quot;https://github.com/hallettj/git-format-staged/blob/master/README.md&quot; class=&quot;css-1od09yo&quot;&gt;git-format-staged&lt;/a&gt; to apply an automatic code formatter or linter to
staged files. It ignores unstaged changes, and leaves those changes unstaged.
When run in a Git pre-commit hook git-format-staged guarantees that committed
files are formatted properly, and does not clobber unstaged changes if
formatting cannot be applied to working tree files cleanly.&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;how-i-learned-to-love-automatic-formatting&quot; class=&quot;css-1bzbprl&quot;&gt;How I learned to love automatic formatting&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I used to pay a lot of attention to code formatting. I would split or join
lines, indent to just the right column, and so on. Then I learned about
&lt;a href=&quot;https://prettier.io/&quot; class=&quot;css-1od09yo&quot;&gt;Prettier&lt;/a&gt;, and tried it out in my Javascript projects. After setting up an
editor plugin I could tap a key combination, and Prettier would do the same
things that I was doing by hand in an instant without any thought on my part.
The code did not wind up in exactly the style that I was used to - but it was
formatted according to a set of consistent, sensible rules that I can live with.
I realized that if I can trust a program to format my code nicely I can free up
a not-insubstantial chunk of my attention. That leaves me with more mental
capacity to devote to matters that actually require human input. I have not
looked back!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;&lt;a href=&quot;https://www.olioapps.com/blog/automatic-code-formatting/&quot; class=&quot;css-1od09yo&quot;&gt;» Read the rest&lt;/a&gt; on the Olio Apps blog.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[I finally set up XMonad to build with Stack!]]></title><link>https://sitr.us/2018/05/13/build-xmonad-with-stack.html</link><guid isPermaLink="false">https://sitr.us/2018/05/13/build-xmonad-with-stack.html</guid><pubDate>Sun, 13 May 2018 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://xmonad.org/&quot; class=&quot;css-1od09yo&quot;&gt;XMonad&lt;/a&gt; window manager is configured in Haskell.
That means that when you want to apply a new configuration you actually build
xmonad itself incorporating code from your configuration file.
It sounds more painful than it is -
when you install xmonad you get an executable called &lt;code class=&quot;css-0&quot;&gt;xmonad&lt;/code&gt; that handles the
details of bootstrapping your custom build.
The command &lt;code class=&quot;css-0&quot;&gt;xmonad --recompile&lt;/code&gt; builds &lt;code class=&quot;css-0&quot;&gt;~/.xmonad/xmonad.hs&lt;/code&gt;,
and subsequent invocations of &lt;code class=&quot;css-0&quot;&gt;xmonad&lt;/code&gt; run the executable that is produced.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When you configure xmonad you are actually writing your own version of the
program.
Because you can write arbitrary code the possibilities for customization are
endless!
As with any software project,
you get maximum expressive power when you bring in third-party libraries.
&lt;code class=&quot;css-0&quot;&gt;xmonad-contrib&lt;/code&gt; is a popular choice -
but you can import any Haskell library that you want.
With libraries come the problem of managing library packages.
In the past I used the &lt;code class=&quot;css-0&quot;&gt;cabal&lt;/code&gt; command to globally install library packages.
From time to time I would clear out my installed packages,
or change something while working on another Haskell project,
and then my window manager would stop working.
I wanted a better option.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;https://docs.haskellstack.org/en/stable/README/&quot; class=&quot;css-1od09yo&quot;&gt;Stack&lt;/a&gt; is my preferred dependency management and build tool for Haskell
projects.
Stack automatically fetches project dependencies,
and maintains isolated sets of installed packages for each project.
With stack I declare dependencies in a &lt;a href=&quot;https://github.com/hallettj/dot-xmonad/blob/master/home/.xmonad/my-xmonad.cabal&quot; class=&quot;css-1od09yo&quot;&gt;.cabal file&lt;/a&gt;,
and stack ensures that I have up-to-date copies of all libraries whenever
xmonad builds itself.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To use stack I needed to hook into xmonad&amp;#x27;s build process.
I used this &lt;a href=&quot;http://brianbuccola.com/how-to-install-xmonad-and-xmobar-via-stack/&quot; class=&quot;css-1od09yo&quot;&gt;blog post&lt;/a&gt; as a starting point.
That post provides instructions for using the &lt;code class=&quot;css-0&quot;&gt;stack ghc&lt;/code&gt; command to invoke ghc
in an environment prepared by stack.
But I have some custom code modules in &lt;code class=&quot;css-0&quot;&gt;~/.xmonad/lib/&lt;/code&gt;,
and I had problems getting ghc to find those when running &lt;code class=&quot;css-0&quot;&gt;stack ghc&lt;/code&gt;.
So I opted to set up a fully-fledged stack project which is built with the
usual &lt;code class=&quot;css-0&quot;&gt;stack build&lt;/code&gt; command.
You can take a look at my &lt;a href=&quot;https://github.com/hallettj/dot-xmonad/tree/master/home/.xmonad&quot; class=&quot;css-1od09yo&quot;&gt;~/.xmonad/&lt;/a&gt; directory to get the high-level view.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The key is the &lt;a href=&quot;https://github.com/hallettj/dot-xmonad/blob/master/home/.xmonad/build&quot; class=&quot;css-1od09yo&quot;&gt;build&lt;/a&gt; script.
Starting in xmonad v0.13 if there is an executable called &lt;code class=&quot;css-0&quot;&gt;build&lt;/code&gt; in your
&lt;code class=&quot;css-0&quot;&gt;~/.xmonad/&lt;/code&gt; directory then xmonad will defer to that script.
&lt;code class=&quot;css-0&quot;&gt;build&lt;/code&gt; gets a path as an argument which is where the compiled xmonad executable
should be placed.
My script looks like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;My &lt;code class=&quot;css-0&quot;&gt;my-xmonad.cabal&lt;/code&gt; file declares an executable named &lt;code class=&quot;css-0&quot;&gt;my-xmonad&lt;/code&gt;
(which is actually my customized version of xmonad).
This script builds that executable,
installs the it to &lt;code class=&quot;css-0&quot;&gt;~/.xmonad/bin/my-xmonad&lt;/code&gt;
(thanks to the &lt;code class=&quot;css-0&quot;&gt;--local-bin-path&lt;/code&gt; argument to &lt;code class=&quot;css-0&quot;&gt;stack install&lt;/code&gt;),
and finally moves the executable to the location given by the first argument to
the &lt;code class=&quot;css-0&quot;&gt;build&lt;/code&gt; script.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You can detailed on working with stack from stack&amp;#x27;s &lt;a href=&quot;https://docs.haskellstack.org/en/stable/GUIDE/&quot; class=&quot;css-1od09yo&quot;&gt;user guide&lt;/a&gt;.
If you want to get going quickly I created a stack &lt;a href=&quot;https://raw.githubusercontent.com/hallettj/dot-xmonad/master/home/.xmonad/xmonad.hsfiles&quot; class=&quot;css-1od09yo&quot;&gt;project template&lt;/a&gt; to set
up xmonad with stack.
Here is what you do:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;Install xmonad and stack using your preferred package manager - you need xmonad v0.13 or later.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Run &lt;code class=&quot;css-0&quot;&gt;stack new my-xmonad https://raw.githubusercontent.com/hallettj/dot-xmonad/master/home/.xmonad/xmonad.hsfiles&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;If you are setting up a new xmonad configuration then &lt;code class=&quot;css-0&quot;&gt;mv my-xmonad ~/.xmonad&lt;/code&gt;. Otherwise copy files from &lt;code class=&quot;css-0&quot;&gt;my-xmonad/&lt;/code&gt; to &lt;code class=&quot;css-0&quot;&gt;~/.xmonad/&lt;/code&gt; and then delete &lt;code class=&quot;css-0&quot;&gt;my-xmonad/&lt;/code&gt;. The relevant files are:&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;my-xmonad.cabal&lt;/code&gt;, your project manifest&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;build&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;lib/&lt;/code&gt; - this directory must exist or the project will not build!&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;xmonad.hs&lt;/code&gt;, in case you do not have your own&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;.gitignore&lt;/code&gt;, in case you want to version-control your xmonad config&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;chmod a+x ~/.xmonad/build&lt;/code&gt; - if the build script is not marked as executable xmonad will not execute it.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;In &lt;code class=&quot;css-0&quot;&gt;~/.xmonad/&lt;/code&gt; run &lt;code class=&quot;css-0&quot;&gt;stack setup&lt;/code&gt; to install the version of ghc that stack wants to use. (Stack installs ghc in a sandbox so that it does not conflict with any other ghc installation on your machine.)&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Names of any custom modules that you have in &lt;code class=&quot;css-0&quot;&gt;~/.xmonad/lib&lt;/code&gt; need to be listed in the &lt;code class=&quot;css-0&quot;&gt;other-modules&lt;/code&gt; section in &lt;code class=&quot;css-0&quot;&gt;~/.xmonad/my-xmonad.cabal&lt;/code&gt;.
If you want to add library dependencies beyond &lt;code class=&quot;css-0&quot;&gt;xmonad&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;xmonad-contrib&lt;/code&gt;
then add them to the &lt;code class=&quot;css-0&quot;&gt;build-depends&lt;/code&gt; section in the same file.
Stack pulls dependencies from &lt;a href=&quot;https://www.stackage.org/&quot; class=&quot;css-1od09yo&quot;&gt;Stackage&lt;/a&gt;,
which hosts curated sets of packages.
The &lt;code class=&quot;css-0&quot;&gt;resolver&lt;/code&gt; setting in &lt;code class=&quot;css-0&quot;&gt;stack.yaml&lt;/code&gt; determines the version of each
dependency that you get,
and the version of ghc that stack will use to compile xmonad.
If you want to use libraries that are not hosted on Stackage you will need to
list package names with exact version numbers in the &lt;code class=&quot;css-0&quot;&gt;extra-deps&lt;/code&gt; section in
&lt;code class=&quot;css-0&quot;&gt;stack.yaml&lt;/code&gt; according to &lt;a href=&quot;https://docs.haskellstack.org/en/stable/GUIDE/#extra-deps&quot; class=&quot;css-1od09yo&quot;&gt;these instructions&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Test everything by running &lt;code class=&quot;css-0&quot;&gt;xmonad --recompile&lt;/code&gt; from any directory.
If that works then you are all set!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Checking Types Against the Real World in TypeScript]]></title><link>https://sitr.us/2018/04/12/checking-types-against-the-real-world.html</link><guid isPermaLink="false">https://sitr.us/2018/04/12/checking-types-against-the-real-world.html</guid><pubDate>Thu, 12 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;This is an excerpt of &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://www.olioapps.com/blog/checking-types-real-world-typescript/&quot; class=&quot;css-1od09yo&quot;&gt;a post&lt;/a&gt; that I wrote for &lt;a href=&quot;https://www.olioapps.com/&quot; class=&quot;css-1od09yo&quot;&gt;Olio Apps&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The shape of data defines a program. There are important benefits to writing out
types for your data.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Let’s consider a Hacker News client, which consumes stories and other items from
the Hacker News API. This is a TypeScript type that describes the format for
stories:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In Javascript and other dynamically-typed languages, it is common to write
a program without any explicit description of a data structure like Story. The
shape of the data is implied in the code that manipulates the data. But that
means anyone reading the code has to mentally reconstruct that shape from
context, or refer to documentation outside of the program itself.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;&lt;a href=&quot;https://www.olioapps.com/blog/checking-types-real-world-typescript/&quot; class=&quot;css-1od09yo&quot;&gt;» Read the rest&lt;/a&gt; on the Olio Apps blog.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Type-Driven Development with TypeScript]]></title><link>https://sitr.us/2018/02/12/type-driven-development-with-typescript.html</link><guid isPermaLink="false">https://sitr.us/2018/02/12/type-driven-development-with-typescript.html</guid><pubDate>Mon, 12 Feb 2018 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;This is an excerpt of &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://www.olioapps.com/blog/type-driven-development-with-typescript/&quot; class=&quot;css-1od09yo&quot;&gt;a post&lt;/a&gt; that I wrote for &lt;a href=&quot;https://www.olioapps.com/&quot; class=&quot;css-1od09yo&quot;&gt;Olio Apps&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I am always interested in making my coding process faster and more robust.
I want to be confident that my code will work as expected. And I want to spend
as little time as possible debugging, testing, and hunting down pieces of code
that I forgot to update when I make changes to a project. That is why I am a fan
of correct-by-construction methods. Those are methods of constructing programs
where the program will not build / compile unless it behaves the way that it is
supposed to.&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;correct-by-construction&quot; class=&quot;css-1bzbprl&quot;&gt;Correct by construction&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Test-driven development is one form of correct-by-construction method. The
philosophy of test-driven development is that your tests are the specification
for how your program should behave. If you look at your test suite as
a mandatory part of your build process, if your tests do not pass the program
does not build because it is not correct. Of course the limitation is that the
correctness of your program is only as certain as the completeness of your
tests. Nevertheless studies have found that
&lt;a href=&quot;http://evidencebasedse.com/?q=node/78&quot; class=&quot;css-1od09yo&quot;&gt;test-driven development can result in 40%-80% fewer bugs in production&lt;/a&gt;.&lt;/p&gt;&lt;style data-emotion-css=&quot;1u9n620&quot;&gt;.css-1u9n620{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.5rem;margin-top:1rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1u9n620{font-size:1.875rem;}}@media screen and (min-width:768px){.css-1u9n620{font-size:2.25rem;}}&lt;/style&gt;&lt;h3 id=&quot;writing-your-program-around-types&quot; class=&quot;css-1u9n620&quot;&gt;Writing your program around types&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Type-driven development is the practice of writing your program around types,
and of choosing types that make it easy for the type checker to catch logic
errors. In type-driven development your data types and type signatures are the
program specification. Types also serve as a form of documentation that is
guaranteed to be up-to-date. Type-driven development is another
correct-by-construction method. As with test-driven development, type-driven
development can improve your confidence in your code, and can save you time when
making changes to a large codebase.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;&lt;a href=&quot;https://www.olioapps.com/blog/type-driven-development-with-typescript/&quot; class=&quot;css-1od09yo&quot;&gt;» Read the rest&lt;/a&gt; on the Olio Apps blog.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What is Poodle]]></title><link>https://sitr.us/2017/06/20/what-is-poodle.html</link><guid isPermaLink="false">https://sitr.us/2017/06/20/what-is-poodle.html</guid><pubDate>Tue, 20 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This is a description of my passion project. I plan to publish more detail and motivation when I get to a minimum viable product.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I want to make social collaboration software without walls.
A company can have its private discussions and documents -
but people in a company should be able to include clients or contractors in discussions seamlessly.
It should not be necessary to require people to join a private account on
service X just to have a conversation.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The idea is to make a decentralized protocol -
there is no central server, and no one company that controls everything.
This has been attempted before by Identi.ca, Diaspora, Google Wave, and others.
I thought that a decentralized protocol could have more success if it takes a different approach: reinvent as little as possible, and build on top of an existing protocol with a large user base.
I&amp;#x27;m working on a reference client (called &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://github.com/PoodleApp/poodle&quot; class=&quot;css-1od09yo&quot;&gt;Poodle&lt;/a&gt;) that implements a new protocol that builds on email.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Email is one of the few decentralized social protocols that has broad adoption
(&lt;a href=&quot;http://www.radicati.com/wp/wp-content/uploads/2015/02/Email-Statistics-Report-2015-2019-Executive-Summary.pdf&quot; class=&quot;css-1od09yo&quot;&gt;2.6 billion users&lt;/a&gt;);
and it has stood the test of time.
Email is not perfect - but over the years email systems have evolved to address many subtle problems;
and we have deployed a great deal of email infrastructure and supporting software.
A protocol that builds on email instead of attempting to replace it will have a massive head start thanks to all of that work.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is a deeply-set mindset that email messages are text or HTML,
and that one message corresponds to one reply.
This is the same way people thought about web pages in the &amp;#x27;90s.
Then AJAX came along and changed everything by decoupling HTTP requests from page views.
That was the big idea that led to &lt;a href=&quot;http://www.cbsnews.com/news/what-is-web-20/&quot; class=&quot;css-1od09yo&quot;&gt;Web 2.0&lt;/a&gt;.
I want to apply a similar idea:
decouple email messages from conversation replies,
and use email to transport structured JSON data representing activities.
A basic activity type would be &amp;quot;post a message&amp;quot; or &amp;quot;post a reply&amp;quot;.
But there are less obvious activity types,
such as &amp;quot;make an edit to a message that I sent earlier&amp;quot;,
or &amp;quot;upvote that other message&amp;quot;.
Those latter types affect the way that participants see a discussion,
but are not presented the same way that replies are.
I think that this idea can lead to many new applications that operate over
email in ways that we never imagined.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An important feature is that I do not have to get people to switch to a new system &lt;em class=&quot;css-0&quot;&gt;en masse&lt;/em&gt;.
These new-style exchanges degrade gracefully when viewed in a traditional email client.
If someone edits one of their messages after sending it,
Poodle users see the edited version with a small note that says &amp;quot;edited a few minutes ago&amp;quot;.
Users on traditional email clients see the edit as a follow-up message with the new content.
Where Poodle users see +1 counts,
traditional email users see messages that just say &amp;quot;+1&amp;quot;
(or whatever text the sender has selected for their fallback &amp;quot;like&amp;quot; message).
This works because email supports multiple data parts,
including fallback presentation for clients that do not understand a new format.
Poodle messages contain a JSON part intended to be consumed by Poodle
(or other interoperable clients),
and an HTML view of the same content for traditional clients.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Poodle supports interaction modes beyond the traditional &amp;quot;email exchanges are
discussions&amp;quot; point of view.
Poodle splits out several activity types that can serve as the starting point of an interaction -
for example there is a &amp;quot;share a document&amp;quot; option.
The document might be HTML edited directly in the client,
or an uploaded file such as a spreadsheet, presentation, image, or video.
Anyone who the document is shared with can edit the document.
What really happens is that their client sends out an &amp;quot;edit&amp;quot; activity to collaborators;
but what everyone sees is that the document in their client updates with the new changes.
(And anyone can browse previous revisions of a document at any time,
because those revisions are really just email messages on their IMAP server.)
Once again, people using traditional clients see document edits as follow-up messages
with the new content in its entirety.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have &lt;a href=&quot;/talks/poodle/&quot; class=&quot;css-1od09yo&quot;&gt;a small slide deck of screen shots&lt;/a&gt; from an early iteration of Poodle.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Keep in mind that all of the features that you see work without any new servers.
This is all just email.
The only thing that is new is a special email client (which runs locally via a downloaded app).
And everything gracefully degrades when interacting with people with regular email clients.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Going slide-by-slide:&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:70.83333333333333%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAABdUlEQVQoz41Sy07CQBTtl+lGF0ZFSXSFC/0EXZC4kAULNUpAlITYuMZXAgs/QRNduDRiiBEEBWql9DHtTNuZqbdWFEiKnJy500zvmXPvzAjx6FQ8Op1YWUyuLiVika1YJLkGHwvrsxMb85M/nBsg/ALV5vKMcJrdLYr7JTFTEo+Kx9mLXOoynz7PpQrpnUKmx4Mhbp8d7gEFz/PK913pzcIOVlXdtm1vbAiMuVcnlYe71menVX2paqrqcU6pC4Doz44DkVE6QOZHEDPOHWJbhNgYY0ge2v47gYc5M1mWG426JEnN5ntHUUzTpJSOK3YdbpngiqFhhJCmaY7jsB5go1AxrDe75S5q65qhKErgGWh+ESqGcVMRn9u3HVmt118JIbDC+/BP2QQThKBTv3R/Mq1xDwyqwhYxDGQYBugM5IP1YXTPXDWbCKtgrus6lMn9e/4DXF64GHp+zD81rj/aSq1WDV5Yf8OjygYnQk3K3MAQUsd/JF+V0vfARExCzAAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;message wide&quot; title=&quot;message wide&quot; src=&quot;/static/934d6e33b268c159f4af472d2fa995c8/7d769/message-wide.png&quot; srcSet=&quot;/static/934d6e33b268c159f4af472d2fa995c8/5243c/message-wide.png 240w,/static/934d6e33b268c159f4af472d2fa995c8/ab158/message-wide.png 480w,/static/934d6e33b268c159f4af472d2fa995c8/7d769/message-wide.png 960w,/static/934d6e33b268c159f4af472d2fa995c8/d6ebf/message-wide.png 1366w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    Participants can edit their own post after-the-fact; each activity has +1 counts.
  &lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:70.83333333333333%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAACB0lEQVQoz21Sy27TQBT1l5UNSEhAxUPdUBb9BbqohEQrgYBIRSVRWwnRdUtRJOAL2BdRNomSOk7sOPWrjuz4bY89tuGMm7QJ5erO2L6Zc849d8JtPLq98fjO1rOHr9aebK4ubz598HptZWt1+fm9pfX7t+ZyqUr2jp+AerFylztu1Jp7218/1r8f7H771GjuvW/u73zZ3T7ceXP04e1VHtdrn+vvrj8bNSRHae75vh8ESNf3E0L+/C9oXhRl+U+R81z39+kpstfr/Tw5kSSREBKGYT6LAovSwPcJScqiKKoKHti5KIoEoS+Koq7pcRQBBjrs8wqAeJ5X3lQOgqDb7bbbbUEQwjBKEjTOIqoCLMAADGc38RwWjprm2DRNSRpqLHTPY0cxBtd14RVgx3Enk0mWZQtgfKuqCsPyULZty3Ec27KAdKqwLJtSCkFUIA6ueXHWNs/znU4HijgHLuxpmmKf9xwnMRzFs0BlqnxhGP1+X1EU9Okzcz40YXgBHAcpuY4p+HJgrVZrMBiAKJ3FzB5rMi/ISHvpuB1VNWV5iClOwVg4Nx6PYRXiuqZhBLIsG4ZR2WNgzJukEqURISmaBzXufgoGE8/3BqI4kkeGcQEisAA8sW1MGAxZkf042z83eUM3QX01keqqkkQ5V1RNtW0bVwLDGMnCn6TMR9aviLgkYcqX4qj/BQG75HUBG36EAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;document wide&quot; title=&quot;document wide&quot; src=&quot;/static/cac17d19f28ca03ecdc384ad66df9ad3/7d769/document-wide.png&quot; srcSet=&quot;/static/cac17d19f28ca03ecdc384ad66df9ad3/5243c/document-wide.png 240w,/static/cac17d19f28ca03ecdc384ad66df9ad3/ab158/document-wide.png 480w,/static/cac17d19f28ca03ecdc384ad66df9ad3/7d769/document-wide.png 960w,/static/cac17d19f28ca03ecdc384ad66df9ad3/0c221/document-wide.png 1367w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    An interaction might be presented as a collaborative document instead of as a thread. In this case there are calls-to-action to edit, or link to the document. (In this example the document content is markdown; but this concept could work for any type of file: images, videos, slide decks, whatever.)
  &lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:811px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:45.416666666666664%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA50lEQVQoz42QXU7DMBCEeyVuxD04Cc9cAYlHbkAl8tQGIhFInL+qIsSl8XrWTBIQASqFkW2NLX/j9a5UNQSOMJof0lOan6/oN+vdzdVjf0TT1MaYPM+TJOm67m8itwAm470nHC4v7s7Prh/ud5l52m62cRxHUcSUtm0nWER4lYakc24Oo0zt+rYQB/HSf8laS3he/68SBpgTEA2M7JkvMj0zmKnCk/qE+YnSFCbLsufU5C9VYejTUVVVL8CAf+21Ofj9UWnqd1gJi/qG2x6FRXkA4dLizel/YXYP7KRiGBhWhV/U2Br5ACZnCx59PF7EAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;link to document&quot; title=&quot;link to document&quot; src=&quot;/static/9835a16e3f9a46cb597bf96ac4728636/63ce0/link-to-document.png&quot; srcSet=&quot;/static/9835a16e3f9a46cb597bf96ac4728636/5243c/link-to-document.png 240w,/static/9835a16e3f9a46cb597bf96ac4728636/ab158/link-to-document.png 480w,/static/9835a16e3f9a46cb597bf96ac4728636/63ce0/link-to-document.png 811w&quot; sizes=&quot;(max-width: 811px) 100vw, 811px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    Linking to email messages is a much under-used feature in my opinion. There is actually already an &lt;a href=&quot;https://tools.ietf.org/html/rfc2392&quot; class=&quot;css-1od09yo&quot;&gt;RFC&lt;/a&gt; that specifies how to construct a globally-unique URI for any email message.
  &lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:70.41666666666667%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAACE0lEQVQoz32STY/SUBSG+yOMG+PCnYuJxrhyMZrRrYYFgRlSFwjFCKW0pUCVWZq4NcaN7NmQAD9gBssA9SM6wjBunB9ACF+FoTAtIOBrL81kBuOT5uTmvfec895zS927eWPj2pW3b17/+PqlVNhTDxS1+BFfRSmsf+XC3rfPqvj82e3rV5FIPXl4f/POxkFhf7lcDk6HQ13vaZphmst/MZ/PEVPv3z24e+vx1iYlv3zlZ5hKpQJV1/Wz8fjMwjSM6XT6+yKz2QzHcrkcLwjJ3V2K53mHw6EoCtROp9Pv94fD4WAw6PV65lr/xWKBmM1mw+GwLMuUKIput7tcLkNttVqapvUtNAssULHdbhOl0WiQzqtkQRCcTmepVILa7XZPbXB6YIM1jIzHY2L7vHMymQwEAuTOyMGJ0WikXwQKtgzDuGw7Fot5PB5iG67gDf2bzSasdi2IAtCc2M7n8+e2XS5XsViEOplMZmuQIZMFeapMJsOy7KozTdOqqkLF9vK/mCOdDGyVjKfadm+n0+mTk1/VavXIpl6v/7Q5Pq7Xjj7Vaof4l6qH3z+kUhzHJRIJCgUkSfJ6vZ6dv+D+iHg8mn7KMIzfxuejERgm4PP7cOG4BYUCKIOBRyIRNsRGLOCKj/CCKAg8L0WlhBzfYR6x/ItQiOW4sGSxSsbMgsEgZ0GSsQc9bhOLSUHOK0b5aDQqWpDkP+29WGhnWyZvAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;permalink&quot; title=&quot;permalink&quot; src=&quot;/static/7d7c2bce22b6a3a484b7c5d0138c21aa/7d769/permalink.png&quot; srcSet=&quot;/static/7d7c2bce22b6a3a484b7c5d0138c21aa/5243c/permalink.png 240w,/static/7d7c2bce22b6a3a484b7c5d0138c21aa/ab158/permalink.png 480w,/static/7d7c2bce22b6a3a484b7c5d0138c21aa/7d769/permalink.png 960w,/static/7d7c2bce22b6a3a484b7c5d0138c21aa/ee48c/permalink.png 1379w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    This is an early iteration of the process of linking to a document from another activity. Combining collaborative documents with linking allows for a decentralized wiki that actually lives in copies in the participant&amp;#x27;s email accounts.
&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:476px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:169.16666666666663%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAiCAIAAADQyG7qAAAACXBIWXMAAAsTAAALEwEAmpwYAAADXklEQVRIx62Vy08TURTGWbt24X9hSFyYuFCMIbIwxp0LlxoT2SiJJsZEw8oVC0NMNIAoamLEoMEHaWOpKYS2tB1boFgoTN/ttDOlLdOZaTudGb+ZS4YplIfgSXpz59575p7zO9+cdmjHsA5VVVdWVqLRKMdx8XicYZhUKpVIJGKxWDKZTKfT2Mpms1ihaRpbjUajxXlzc7NSqVSr1WKxyPN8qVTCIxbL5TLGDYthRVGUbedjhW19QBT7HN29qztXhLlw8jqdf6JqkiTVGSZXKBTyhiFbjCzLklRVw6w5NxdS1yKuLoo6zQrjkqhRlD8UCgEeRrfbjZGiKFmW24e9xtwP+TqDS+cqNbfS1IobHG7OZDIgzxpGJqCICbC1ODeaXK48VhJcmOMGbAM4CRt4UUK8CCsoBLZQlB3AzDTUo9PehzPhtAO47pyqLL4N3rWvPVc0uSbVEC2CRHjIELJBqKIomp5W4KCtjFC3P366+uz7xVDRzpck77wnEAhEIpFgMOgzDAXbs84flh6OfukZtPesltxqQ+OKLGgTwgjBrDmigFqxCIrbzmWJmaaHFvJ2zOv1Og6RUhHIrMXgRl5BoviftNUDhd0iT/xy/Op4+LEr/lpRZUVRoaRarSYIAjhjgkfrN7yzGYz8vjP09fKArWuR+ylL6rzPu7y8DNpg7vf7vV4vdL4n7XfBe6OfewanusPcLwQObYNNwbBcLgdCQF0xDFLFd9fizFUT36IDvswE3g7a+CRxCD0ItAl2NCO8Dllg3Ol8GGCH6SQHAG+j7QzNv+oP2d/TQI0zYIsu12w2kQKCFAzDvP3NT2/O3Tr7o/e8zWvTNezz6YQ9Hg/SxsThcMzMzLSVN0ql9d+YfXBluveCbXYyrWkKw+RNbRNOMATS/ub4n/LLR9Tk0CpasigKuazeAEEY5cUEesZobddH6dttgKlbkE3aW+fMq3b7tNwcq6p9wcabmN5c4SOIIlEC8sQEniRhvae3MtedLznFU8OxE2PsBIhKvNvjdTqdIIz/OpfLhX6CEd0b5CFVK/MOWdU6p8Qzw+GTI8kXNFZlpsASVUOkoE10SjoJ4KHm5nem3+wsKN0Ovi8gSqpWk8RMNgtPnDbH9fV18h+AEpA2TlriFm3FkKX6b9LW/gJl3nRtXrEArAAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;who can see this discussion&quot; title=&quot;who can see this discussion&quot; src=&quot;/static/0068aacc4c5e72a8c451766629ad93e2/d9059/who-can-see-this-discussion.png&quot; srcSet=&quot;/static/0068aacc4c5e72a8c451766629ad93e2/5243c/who-can-see-this-discussion.png 240w,/static/0068aacc4c5e72a8c451766629ad93e2/d9059/who-can-see-this-discussion.png 476w&quot; sizes=&quot;(max-width: 476px) 100vw, 476px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    I was experimenting with features to make email more usable generally. I tried to design Poodle to make it really obvious who sees what in any exchange. Poodle uses a reply-to-all form as the default reply option, and the user must go through extra steps to reply to just one person, or to a subset of the people already participating in a discussion.
  &lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:70.83333333333333%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAABy0lEQVQoz4WRW08TQRSA969hYjC+lIjRB0l4wRBNtIgQICik7yVqMBL8DTxgjA9KRAO6AUHKrSlts9sb221nZueyW9iZznZ17AaiZqsn30xOZs6XMyejTd/sn7l1bX5oIDV8Y+5OYm4okRoeVMlEom9y4Eos6mp68Ors7evas/HR54/vLT0Zf52aejX7aHHqwdLTiZczyfTDkYXkXUVa7WN/kE6OKOXF5H2NIpDd3c7om4pcZqd4mMnv7+b3vxcO9hQqMXPHgvttwX9HcIWveZR8W/+wtrqy/nZ16+P7nU9rmc3P2e2vx1tfFEf6RvFgLwzDH3GhucSpFk4s0zg1i6V8TlEvG41Kya6YETWjEARBvCylq78z3yyb1COlUrlu2+o0ahQ1JAi0hYiXWx6xK8gyXRm0fd/nnEsp2xfxS4bNnrKamRLUhA3LsgAAlDESBcYEEyEEhb07e4w4CFZrNQDgX7P9/9kuwahuU4QwbLrYIQhSBMkFDCO7Wg6kjJcDGdQaWYCqxMEIgjPPvaTlMo+x87NWz69SS88vF229YTuGaYjuC8NudDodNUiUx8uqwufnnPthJ4ytUwX/khllGGPZHSxqGDmXSS/5JzWc3QpJ5vKHAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;private aside 1&quot; title=&quot;private aside 1&quot; src=&quot;/static/dc1d34dd274c5c183db6faa2745315e4/7d769/private-aside-1.png&quot; srcSet=&quot;/static/dc1d34dd274c5c183db6faa2745315e4/5243c/private-aside-1.png 240w,/static/dc1d34dd274c5c183db6faa2745315e4/ab158/private-aside-1.png 480w,/static/dc1d34dd274c5c183db6faa2745315e4/7d769/private-aside-1.png 960w,/static/dc1d34dd274c5c183db6faa2745315e4/d6ebf/private-aside-1.png 1366w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    If someone does reply without including everyone, you get this &amp;quot;private aside&amp;quot; feature. (This works just by examining To:, From:, and Cc: fields in email messages in the discussion.) The private aside acts like a self-contained sub-discussion, interleaved with the original discussion. My hope is that this kind of feature can help to avoid embarrassing information leaks.
  &lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:71.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAABuUlEQVQoz4WSWS8DURTH55MRQkKCWIInInwDa9KE0JRaIsSjiH2LBw8eJCUiQhAP1ka3aalu03a2ztremTvj6JCgLSe/nPxz7/nnnLsQQ83VttZae1eLo6d9rLNptKPR0d0Gub+hYqCxsq++vJDeurLB5ipbWw2xMz+57BxZmRpbn51Ym3EsT45uzE6sTtsX7bal8eHteefWnBNqfrG7MA0Qpmlq2EA6Rrqe0wDNElkEApt/BiFm+NCz2/tw53u8D/u9sWAgQvqipB+IkP5X33PsJahrGtb1QgiBZa6OD137eycH+5dHh9cnrrvzU/f1xdPVOfB4eRZ4uDUMo3jnDEOHPd54MAQN3wLesN8DgPgkrzHWi5uzQtb96roJbSaTLEmSHMfDqtUKoRxkLp3S8qKIWeIziZQvwXtQTpMkSVVVcGKMrQwVbIoqaRY4RuQEgZcVVf21Z/Xn0smS5gxLs+mkIouy+B3ByookpWJReL5SZkbguSyMqygfqIqGELyNBQRNxWGlpFmWRKhEX4Hz4xpfY7N/jc3QqizTDCPLkllw6H8uDH4YFQnTVCKdiH9C/YCKvsFnKmp+B3Ac5bUBxbwDAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;private aside 2&quot; title=&quot;private aside 2&quot; src=&quot;/static/651fd0779371ae6efb637613005e5e85/7d769/private-aside-2.png&quot; srcSet=&quot;/static/651fd0779371ae6efb637613005e5e85/5243c/private-aside-2.png 240w,/static/651fd0779371ae6efb637613005e5e85/ab158/private-aside-2.png 480w,/static/651fd0779371ae6efb637613005e5e85/7d769/private-aside-2.png 960w,/static/651fd0779371ae6efb637613005e5e85/e113d/private-aside-2.png 1364w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    Private aside messages are interleaved time-wise with the top-level discussion. And each private aside has its own private reply form after the last message in the aside.
  &lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Somehow I missed including a slide showing another feature that I think is useful:
when someone includes a new person in the recipient list in a reply,
there is a card that appears in the discussion view that says &amp;quot;so-and-so joined the discussion&amp;quot;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I think those features work well for small discussions with a fixed set of participants.
On the roadmap is scaling up via hosted groups.
Each group has its own email address -
to start an interaction with the group you send to the group&amp;#x27;s address
(e.g. &lt;a href=&quot;mailto:backyard-goats@raddiscussions.net&quot; class=&quot;css-1od09yo&quot;&gt;backyard-goats@raddiscussions.net&lt;/a&gt;).
People who join the group later have access to existing discussions and documents via archives on the group server.
This is basically just the concept of a mailing list,
but with some added usability features.
The combination of social collaboration features and groups produces something
like subreddits, or Facebook groups, or Jive communities.
Groups support the philosophy of decentralization:
anyone can start up their own group servers and have complete control over them;
anyone can bring people from outside the group into a group interaction by including them in the recipient list;
an interaction can be shared with multiple groups simultaneously;
and so on.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Changes I would make to Go]]></title><link>https://sitr.us/2017/02/21/changes-i-would-make-to-go.html</link><guid isPermaLink="false">https://sitr.us/2017/02/21/changes-i-would-make-to-go.html</guid><pubDate>Tue, 21 Feb 2017 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have been programming primarily in Go for about six months.
I find it frustrating.
There are two reasons for this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;First, functional programming is particularly difficult in Go.
In fact the language &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Go_(programming_language)#Conventions_and_code_style&quot; class=&quot;css-1od09yo&quot;&gt;discourages functional programming&lt;/a&gt;.
This frustrates me because the imperative code that I write requires a lot of
boilerplate,
and I think it is more error-prone than it could be if I could use functional
abstractions.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Second, I see Go as a missed opportunity.
There is exciting innovation in programming languages -
especially in areas of type checking and type inference to make code safer,
faster, and more ergonomic.
I wish Google had chosen to put their weight behind some of those ideas.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I am not the first person to look at Go this way.
Here are other posts that echo my feelings:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://yager.io/programming/go.html&quot; class=&quot;css-1od09yo&quot;&gt;Why Go Is Not Good&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://crufter.com/@crufter/everyday-hassles-in-go&quot; class=&quot;css-1od09yo&quot;&gt;Everyday hassles in Go&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;a href=&quot;https://www.barrucadu.co.uk/posts/etc/2016-08-25-three-months-of-go.html&quot; class=&quot;css-1od09yo&quot;&gt;Three Months of Go (from a Haskeller&amp;#x27;s perspective)&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://journal.stuffwithstuff.com/2010/10/21/the-language-i-wish-go-was/&quot; class=&quot;css-1od09yo&quot;&gt;The Language I Wish Go Was&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I will add to those with some of my own opinions.
To highlight exactly how I think Go could be better,
I want to make comparisons to Rust.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Work on Go and Rust began at close to the same time:
Go was first announced to the world in 2009,
Rust in 2010.
There is a lot of overlap in their philosophies;
both languages:&lt;/p&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;compile to fast, native binaries&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;eschew inheritance in favor of composition&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;support imperative programming&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;omit exception-catching in favor of explicitly passing error results&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;emphasize concurrency&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;come with static type-checking&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;come with a modern packaging system that promotes modularity&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Both languages may have been intended to replace C++:
&lt;a href=&quot;https://en.wikipedia.org/wiki/Go_(programming_language)#History&quot; class=&quot;css-1od09yo&quot;&gt;Go&amp;#x27;s designers have said&lt;/a&gt; that a primary motivation for Go was their
dislike of the complexity of C++.
One of Mozilla&amp;#x27;s prominent applications for Rust is &lt;a href=&quot;https://servo.org/&quot; class=&quot;css-1od09yo&quot;&gt;Servo&lt;/a&gt;, a potential
replacement for the Gecko HTML rendering engine, which is written in C++.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;As I see it the key differences are that
Rust aims for soundness,&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref css-1od09yo&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; powerful abstractions, and high performance;
Go aims to be accessible, consistently simple, and fast to compile.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;That said, Rust is not necessarily a replacement for Go,
and I do not mean to say that everyone using Go should switch to Rust.
Rust supports real-time operation,
can operate with stack-only allocation if necessary,
and has a sophisticated type system that can, for example,
detect problems due to concurrent access to shared data at compile time.
Those requirements add to the complexity of Rust programs.
The &lt;a href=&quot;https://doc.rust-lang.org/book/ownership.html&quot; class=&quot;css-1od09yo&quot;&gt;borrow-checker&lt;/a&gt; in particular has a reputation for its learning curve.
My purpose for making comparisons to Rust is to provide context for specific
ways in which I think Go could be better.
Rust borrows a lot of good ideas from other languages,
and puts them together in one nice package.
Most of those ideas are not even tied to borrow-checking.
I think that Go could be a better language if it adopted some of the same ideas
that Rust has adopted.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You can grab the &lt;a href=&quot;https://github.com/hallettj/comparing-go-and-rust&quot; class=&quot;css-1od09yo&quot;&gt;code examples&lt;/a&gt; from this post.
The examples come with executable tests to encourage experimentation.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;So, here is my view of how Go compares:&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;the-good&quot; class=&quot;css-1bzbprl&quot;&gt;The Good&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I think that Go&amp;#x27;s use of interfaces to promote composition is great.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I like the separation of behavior and data:
structs store data, methods manipulate data in structs -
it is clean separation of state (structs), and behavior (methods).
I think that distinction can become unfortunately blurry in languages that use
inheritance.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Go is easy to learn.
Go repurposes object-oriented concepts to make something new in a way that
makes it approachable to programmers who are familiar with other
object-oriented languages.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is often a pretty clear &amp;quot;Go way&amp;quot; to solve a problem.
This is also an oft-touted virtue of, for example, Python.
Encouraging consistent idioms via the languages makes it likely that any Go
programmer will be able to understand code written by any other Go programmer.
This is part of a philosophy of simplicity that is described in the keynote
&lt;a href=&quot;https://dave.cheney.net/2015/03/08/simplicity-and-collaboration&quot; class=&quot;css-1od09yo&quot;&gt;Simplicity and collaboration&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are lots of features in the Go standard libraries that have clearly had
a lot of thought put into them.
This is one of my favorites:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Goroutines are cheap,
so programs can be structured in the way that makes most sense algorithmically,
even if that involves spawning large numbers of goroutines.
(But this is not unique to Go.
Erlang and Scala also implement lightweight actors.
Rust and other languages have their own solutions for lightweight concurrent
and parallel programming.)&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Since I am using Rust as a point-of-reference I will point out that
Rust has a separation of behavior and data that is a lot like Go&amp;#x27;s,
and Rust also goes for composition-over inheritance.
Instead of structs and interfaces,
Rust uses structs, enums, and traits.
Rust traits serve the same purpose as interfaces;
but they are different enough that they might seem a little weird to
programmers with an object-oriented background.
Rust and Go differ in that Rust prioritizes expressiveness over simplicity,
and type safety over fast compile times.
(Fast compile times are a high priority for the Rust team -
just not the top priority.)&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I could go on - there are plenty of nice features in Go.
But there are also:&lt;/p&gt;&lt;h2 id=&quot;the-bad&quot; class=&quot;css-1bzbprl&quot;&gt;The Bad&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;These are features of Go that I find especially frustrating.&lt;/p&gt;&lt;style data-emotion-css=&quot;1u9n620&quot;&gt;.css-1u9n620{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.5rem;margin-top:1rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1u9n620{font-size:1.875rem;}}@media screen and (min-width:768px){.css-1u9n620{font-size:2.25rem;}}&lt;/style&gt;&lt;h3 id=&quot;nil&quot; class=&quot;css-1u9n620&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I am disappointed by the decision to include null pointers in a new language
when safer solutions have been in use for decades.
Or to be more precise: I think it is a bad idea for a language to use &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; as
a bottom-ish type,
where &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; is type-compatible with every pass-by-reference type.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I understand that &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; is not technically a &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt; pointer -
but its behavior is close enough that criticisms that apply to null pointers
also apply to &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt;.
I have read &lt;a href=&quot;https://speakerdeck.com/campoy/understanding-nil&quot; class=&quot;css-1od09yo&quot;&gt;Understanding Nil&lt;/a&gt;,
and I understand that it is possible to implement methods where the receiver is
&lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt;,
and that &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; can be useful.
Go does some nice things to make &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; less bad than it could be.
But the fact remains:
&lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; is type-compatible with every pass-by-reference type,
whether or not methods on that type have sensible behavior for &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; receivers,
and that leads to lots of opportunities for runtime errors.
To me that seems like an opportunity to make language changes so that it is
easier for the type checker to catch problems at compile time.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Some newer languages have a &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt;, but treat it as a distinct type that is
generally not compatible with other types.
(For example, &lt;a href=&quot;http://fantom.org/&quot; class=&quot;css-1od09yo&quot;&gt;Fantom&lt;/a&gt; and &lt;a href=&quot;https://flowtype.org/&quot; class=&quot;css-1od09yo&quot;&gt;Flow&lt;/a&gt; do this.)
In those languages values are non-nullable by default.
Here is how one might declare and use a nullable variable in Flow when writing
React code:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Without a concept of nullability,
uses of &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; are contradictions of what is stated in
your type signatures.
This Go signature claims that the argument is a pointer to a &lt;code class=&quot;css-0&quot;&gt;User&lt;/code&gt; struct -
but if you take that claim at face value you are likely to get a &lt;code class=&quot;css-0&quot;&gt;nil pointer
dereference&lt;/code&gt; error:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In Go every variable with a pass-by-reference type comes with the implied
ambiguity,
&amp;quot;...or it might be &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt;&amp;quot;.
Support for non-nullable types makes a language expressive enough to avoid that
ambiguity.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The problem with &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; in Go is exacerbated by the fact that &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; checks
sometimes fail.
If an interface value has a concrete type but its value is &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt;,
&lt;a href=&quot;http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#nil_in_nil_in_vals&quot; class=&quot;css-1od09yo&quot;&gt;a &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; check will not return &lt;code class=&quot;css-0&quot;&gt;true&lt;/code&gt;&lt;/a&gt;.
The purist explanation for this is that the value is not really &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt;:
it is an interface value that happens to have &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; in its value slot.
I don&amp;#x27;t find that explanation to be satisfactory.
When a method is dispatched on that not-really-nil value,
the receiver value will really be &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; in the method body.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But what about zero values?
What would the zero value be for a function type or an interface type
without &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt;?
Well, I think that zero values are also a bad idea.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;One of the design decisions in Go is that every type must have
a well-defined default value, which is called the zero value.
This can be convenient because it means you do not have to write constructors
by hand when all you need is a default value.
But I suspect that the real reason why zero values are a part of Go is that
they provide well-defined behavior for what happens when you use uninitialized
variables.
C and C++ are notorious for &lt;a href=&quot;http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html&quot; class=&quot;css-1od09yo&quot;&gt;undefined behaviors&lt;/a&gt;,
which lead to traps for programmers, and to problems making code portable
between compiler implementations.
Use of uninitialized variables is one notable example of undefined behavior in
both languages.&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref css-1od09yo&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;
I imagine that the designers of Go learned from C and C++,
and set out to clearly define as much of Go&amp;#x27;s behavior as possible.
I think that is a great attitude for language designers to adopt!
But there is another option that I think leads to better code safety:
Rust, Flow, and other languages use &lt;a href=&quot;https://en.wikipedia.org/wiki/Data-flow_analysis&quot; class=&quot;css-1od09yo&quot;&gt;data-flow analysis&lt;/a&gt; to detect uses of
uninitialized variables,
and fail type-checking if any such uses are found.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The zero-value requirement introduces a constraint that &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; must exist and
must be assignable to a variety of types.
A lot of types have no sensible default value,
so &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; is the only choice.
So that is one problem.
Another problem is that the language does not have enough information to
produce sensible default values for domain-specific types.
The fact that it tries anyway undermines soundness in code.
The zero values for functions and interface values
(i.e., interface values with no runtime type tag)
are not going to be useful under any circumstances.
Pointer types can implement methods with &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; receivers -
but that is not useful for types that do not have sensible behavior for
uninitialized values.
Default struct values can be useful in some cases -
but in other cases the default value violates invariants that would have been
enforced by a hand-written constructor.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The author of &lt;a href=&quot;https://www.barrucadu.co.uk/posts/etc/2016-08-25-three-months-of-go.html&quot; class=&quot;css-1od09yo&quot;&gt;Three Months of Go&lt;/a&gt; called out problems with zero values in
work at Pusher:&lt;/p&gt;&lt;style data-emotion-css=&quot;1gtwvjp&quot;&gt;.css-1gtwvjp{border-left-color:var(--theme-ui-colors-primary,#6b46c1);border-left-style:solid;border-left-width:6px;margin-left:0;margin-right:0;padding-left:2rem;}.css-1gtwvjp p{font-style:italic;}&lt;/style&gt;&lt;blockquote class=&quot;css-1gtwvjp&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Zero values caused so many problems over the summer, because everything would appear to be fine, then it suddenly breaks because the zero value wasn&amp;#x27;t sensible for its context of use. Perhaps it&amp;#x27;s an unrelated change that causes things to break (like a struct getting an extra field).&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;how-rust-does-it-differently&quot; class=&quot;css-1u9n620&quot;&gt;How Rust does it differently&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Rust goes a step beyond nullable types:
it does not have a &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; value.
Rust uses enums, which are types whose values come in multiple variants,
where each variant is effectively a distinct struct.
If you want to represent an absence of a value you use an enum variant that
holds no data.
The generalized form of this pattern is known as the &amp;quot;Option Pattern&amp;quot;.
The definition of the &lt;code class=&quot;css-0&quot;&gt;Option&lt;/code&gt; type from Rust&amp;#x27;s standard library looks
approximately like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;None&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;Some&lt;/code&gt; are constructors:
essentially they are each a function that returns a value of type &lt;code class=&quot;css-0&quot;&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;.
&lt;code class=&quot;css-0&quot;&gt;Some&lt;/code&gt; takes one argument, &lt;code class=&quot;css-0&quot;&gt;None&lt;/code&gt; takes zero arguments.
Given an &lt;code class=&quot;css-0&quot;&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; value, you can use pattern matching to determine which
constructor was used to create the value;
and pattern matching is also how you read back any constructor arguments.
(In the case of a value created by calling &lt;code class=&quot;css-0&quot;&gt;Some(x)&lt;/code&gt;,
pattern matching lets you get access to that &lt;code class=&quot;css-0&quot;&gt;x&lt;/code&gt; value.)&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here is the Option pattern in action
(&lt;a href=&quot;http://rustbyexample.com/std/option.html&quot; class=&quot;css-1od09yo&quot;&gt;source&lt;/a&gt;):&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An advantage of the option pattern over nullable types is that you can
distinguish between values like &lt;code class=&quot;css-0&quot;&gt;None&lt;/code&gt; versus &lt;code class=&quot;css-0&quot;&gt;Some(None)&lt;/code&gt;.
If you are looking up values in a cache (for example),
a &lt;code class=&quot;css-0&quot;&gt;None&lt;/code&gt; result might indicate that there is no entry in the cache for a given
key;
and a &lt;code class=&quot;css-0&quot;&gt;Some(None)&lt;/code&gt; result might indicate that there is an entry,
and the value of the entry is &lt;code class=&quot;css-0&quot;&gt;None&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I once advocated for use of the Option pattern at a Java company -
but at least one of my coworkers was put off by the idea of allocating an extra
object on the heap just to distinguish between a value and an absence of
a value.
Rust is built with the Option pattern in mind,
and prioritizes zero-cost abstractions:
If the type parameter for &lt;code class=&quot;css-0&quot;&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; is a reference type,
then the &lt;code class=&quot;css-0&quot;&gt;None&lt;/code&gt; case can be safely represented at runtime as a null pointer.
So the &lt;code class=&quot;css-0&quot;&gt;Some&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;None&lt;/code&gt; wrapper often disappears at compile time.
In those cases the code is just as efficient as it would have been if the
language allowed uses of unsafe &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt; values in source code.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In the example above since neither &lt;code class=&quot;css-0&quot;&gt;Option&amp;lt;i32&amp;gt;&lt;/code&gt; nor the &lt;code class=&quot;css-0&quot;&gt;i32&lt;/code&gt; parameter are
reference types, the compiler allocates contiguous space on the stack for the
numeric result, and for a tag to distinguish between &lt;code class=&quot;css-0&quot;&gt;Some&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;None&lt;/code&gt;.
There is no extra heap allocation, and no added pointer indirection.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Rust Book has &lt;a href=&quot;https://doc.rust-lang.org/book/error-handling.html&quot; class=&quot;css-1od09yo&quot;&gt;a lot more detail&lt;/a&gt; on error handling.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It is possible to implement the Option pattern in Go with similar efficiency;
and one could even get a compile-time check that errors are handled by
implementing a &lt;code class=&quot;css-0&quot;&gt;match&lt;/code&gt; method that uses the visitor pattern (&lt;a href=&quot;https://github.com/hallettj/comparing-go-and-rust/tree/master/go/option&quot; class=&quot;css-1od09yo&quot;&gt;example&lt;/a&gt;).
But without generics there would be no type safety for values wrapped in an
&lt;code class=&quot;css-0&quot;&gt;Option&lt;/code&gt; type.&lt;/p&gt;&lt;h3 id=&quot;error-handling-boilerplate--lack-of-compile-time-checks-unhandled-errors&quot; class=&quot;css-1u9n620&quot;&gt;Error-handling boilerplate &amp;amp; lack of compile-time checks unhandled errors&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Error handling in Go has two related problems:
there is a lot of boilerplate required;
and if the programmer neglects to check for an error,
or makes a small mistake such as checking the wrong error variable,
the compiler will not detect the problem.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Rust has a type, &lt;code class=&quot;css-0&quot;&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt;,
that is quite similar to &lt;code class=&quot;css-0&quot;&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;.
The difference is that the failure variant of the &lt;code class=&quot;css-0&quot;&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt; enum is not empty -
it contains an error value (of type &lt;code class=&quot;css-0&quot;&gt;E&lt;/code&gt;).
A returned value of type &lt;code class=&quot;css-0&quot;&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt; may be either
&lt;code class=&quot;css-0&quot;&gt;Ok(value)&lt;/code&gt; (on success) or
&lt;code class=&quot;css-0&quot;&gt;Err(err)&lt;/code&gt; (on error).&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A common complaint about the Option and Result patterns is that unwrapping
success values is a pain.
But a language with support for pattern matching makes unwrapping painless,
and first-class result values permit combinators that can handle a series of
potential failures more cleanly and safely than explicit error checks.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Consider this Go function:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are a few ways the &lt;code class=&quot;css-0&quot;&gt;fetchAllBySameAuthor&lt;/code&gt; function this could be
implemented in Rust.
The pattern matching approach is probably the most accessible to people who do
not have prior experience with the Option or Result Pattern:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;match&lt;/code&gt; keyword introduces a pattern-match block.
The block includes a pattern for each possible variant of the type of the
expression in the head of the &lt;code class=&quot;css-0&quot;&gt;match&lt;/code&gt; paired with an expression to evaluate if
that pattern matches.
This is somewhat like the &lt;a href=&quot;https://golang.org/doc/effective_go.html#type_switch&quot; class=&quot;css-1od09yo&quot;&gt;type switch&lt;/a&gt; feature in Go,
where the code that runs depends on the type of the variable in the head of the
&lt;code class=&quot;css-0&quot;&gt;switch&lt;/code&gt; block.
But the Rust version comes with a compile-time check that a pattern is given
for every possible variant of the given type,
which avoids potential runtime errors.
This is especially helpful when a custom type is updated to add new variants:
the compiler will immediately point out any uses of the type that need to be
updated.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Anyway, that Rust code is no less verbose than the Go version -
but it demonstrates that unwrapping &lt;code class=&quot;css-0&quot;&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; values does
not have to be any more onerous than &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; checks;
and if we had left out a failure check in the Rust version Rust would have
emitted an error at compile time.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Rust has a macro, &lt;code class=&quot;css-0&quot;&gt;try!&lt;/code&gt;, that abstracts the pattern matches and early returns
that we see above.
So this is an equivalent function:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;try!&lt;/code&gt; rewrites an expression at compile time.
For example, &lt;code class=&quot;css-0&quot;&gt;try!(fetch_post(post_id))&lt;/code&gt; is expanded to put the &lt;code class=&quot;css-0&quot;&gt;fetch_post&lt;/code&gt;
call inside a &lt;code class=&quot;css-0&quot;&gt;match&lt;/code&gt;,
and inserts the boilerplate pattern matches for the &lt;code class=&quot;css-0&quot;&gt;Ok&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;Err&lt;/code&gt; match cases.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;try!&lt;/code&gt; macro was used so often that Rust&amp;#x27;s designers decided to extend the
language a bit to better support the pattern:
putting the &lt;code class=&quot;css-0&quot;&gt;?&lt;/code&gt; postfix operator at the end of an expression has the same effect.
For example,
the line &lt;code class=&quot;css-0&quot;&gt;let post = try!(fetch_post(post_id));&lt;/code&gt; can be equivalently written as
&lt;code class=&quot;css-0&quot;&gt;let post = fetch_post(post_id)?;&lt;/code&gt;
And type-checking will fail appropriately if you forget the &lt;code class=&quot;css-0&quot;&gt;?&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But Go does not support macros.
Thankfully, the Result pattern does not require macros to be concise.
For the more functionally-inclined,
here is another equivalent implementation that uses combinator methods:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;and_then&lt;/code&gt; is a method on &lt;code class=&quot;css-0&quot;&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt; values.
If the value is a successful result, it runs the given callback,
which should return a new &lt;code class=&quot;css-0&quot;&gt;Result&amp;lt;U,E&amp;gt;&lt;/code&gt; value.
Or if the value is an error result, &lt;code class=&quot;css-0&quot;&gt;and_then&lt;/code&gt; short-circuits,
and passes the error result through.
&lt;code class=&quot;css-0&quot;&gt;and_then&lt;/code&gt; is a lot like the &lt;code class=&quot;css-0&quot;&gt;then&lt;/code&gt; method on Javascript promises.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But wait - what if you want to wrap error results to add context?
There is a combinator for that to:
&lt;code class=&quot;css-0&quot;&gt;map_err&lt;/code&gt; permits arbitrary transformations to error results.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The idea is that failure checks are almost always the same:
check for an error, return the error if it exists, otherwise continue.
The &lt;a href=&quot;http://deviq.com/don-t-repeat-yourself/&quot; class=&quot;css-1od09yo&quot;&gt;DRY&lt;/a&gt; thing to do is to abstract the common pattern into a helper method
or a macro.
And again,
a common theme in all of these Rust implementations is that there is
a compile-time guarantee that every error is handled.
That could be with some recovery code,
or by passing the error up the call stack.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt; does not get the same disappear-at-compile-time optimization that
&lt;code class=&quot;css-0&quot;&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; does because both enum variants hold data.
But its efficiency compares favorably with Go&amp;#x27;s multiple return values.
Go allocates enough space for every value in a multiple return.
Rust allocates enough space to hold either &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;E&lt;/code&gt;
(i.e., enough space to hold the largest possible value),
plus a tag to distinguish between an &lt;code class=&quot;css-0&quot;&gt;Ok(value)&lt;/code&gt; value and an &lt;code class=&quot;css-0&quot;&gt;Err(err)&lt;/code&gt; value.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A nice thing about the generality of Rust enums is that if &lt;code class=&quot;css-0&quot;&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt; did
not exist,
it would be easy to implement it as a library.
So what about using the Result pattern in Go?
Well, we can&amp;#x27;t put methods on Go tuples (a.k.a, multiple return values),
because they are not first-class values.
It is not possible to define a function that that accepts a tuple and a callback:
a Go function that takes a tuple cannot accept additional arguments
(because Go tuples are not first-class values).
Those constraints make the combinator pattern difficult.
We could implement a custom struct type -
but without generics it would not be very useful.&lt;/p&gt;&lt;h3 id=&quot;list-manipulation-is-not-practical&quot; class=&quot;css-1u9n620&quot;&gt;List manipulation is not practical&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is a special slap-in-the-face for functional programmers built into Go:
there is no good way to write a polymorphic function that can manipulate slices
with arbitrary types.
In Rust you can write a function with a signature that looks like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;That is a function that takes a callback and an input slice,
and returns a new array that is computed by accumulating the results of
applying the callback to each element in the input array.
Even better, there are built-in methods in Rust&amp;#x27;s iterator types that do
exactly this.
The input slice might hold any type of values;
type variables allow the type checker to track how the type of the output array
relates to the type of the input slice,
and also allow the type checker to check that the callback has the appropriate
input and output types.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;That pattern does not work well in Go.
Without type variables the only way to express a type that is polymorphic
over all slice types is to use the top-type: &lt;code class=&quot;css-0&quot;&gt;[]interface{}&lt;/code&gt;.
For example:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But that function is not really polymorphic:
a slice type with a more specific type parameter
(e.g., &lt;code class=&quot;css-0&quot;&gt;[]int&lt;/code&gt;)
is not type-compatible with &lt;code class=&quot;css-0&quot;&gt;[]interface{}&lt;/code&gt;.
So you cannot pass a variable with type &lt;code class=&quot;css-0&quot;&gt;[]int&lt;/code&gt; to that &lt;code class=&quot;css-0&quot;&gt;Map&lt;/code&gt; function.
You have to create a new slice of type &lt;code class=&quot;css-0&quot;&gt;[]interface{}&lt;/code&gt; first,
and copy &lt;code class=&quot;css-0&quot;&gt;int&lt;/code&gt; values to the new slice one-by-one in a &lt;code class=&quot;css-0&quot;&gt;for&lt;/code&gt; loop.
Then after getting a result from &lt;code class=&quot;css-0&quot;&gt;Map&lt;/code&gt; you have to copy result values into yet
another slice to get the proper final slice type.
That means two custom loops are required around every invocation of &lt;code class=&quot;css-0&quot;&gt;Map&lt;/code&gt; -
plus a runtime type assertion or type switch in the callback implementation.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A slice with an arbitrary type parameter &lt;em class=&quot;css-0&quot;&gt;is&lt;/em&gt; type-compatible with the
top-type, &lt;code class=&quot;css-0&quot;&gt;interface{}&lt;/code&gt;.
If you just use &lt;code class=&quot;css-0&quot;&gt;interface{}&lt;/code&gt; type for every polymorphic argument you get
a signature like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;With that signature you can pass in any slice type and callback type that you want,
and assign the result to a variable with the proper type.
But to make that work it is necessary to use the reflection API to fix up
runtime type tags for the input slice, the input callback, and the output slice.
The process is described in &lt;a href=&quot;http://blog.burntsushi.net/type-parametric-functions-golang/&quot; class=&quot;css-1od09yo&quot;&gt;Writing type parametric functions in Go&lt;/a&gt;.
The reflection code is ugly,
but can be hidden in implementations of general-purpose functions.
The unavoidable downsides are that you lose all compile-time type-safety,
and there is an order-of-magnitude performance cost.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The same problem applies to other list manipulation functions:
&lt;code class=&quot;css-0&quot;&gt;Filter&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;Take&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;Reduce&lt;/code&gt;, etc.
This is bad because list manipulation is the bread-and-butter of functional
programming.
The fact that Go discourages such a basic building block as &lt;code class=&quot;css-0&quot;&gt;Map&lt;/code&gt; means that
functional programming is not likely to thrive in Go,
and the Go community will not be able to benefit from the
&lt;a href=&quot;http://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf&quot; class=&quot;css-1od09yo&quot;&gt;advantages of functional programming&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You might have noticed a pattern here:
Go does not support generics, and that leads to problems.
Except the issue is not just lack of generics.
Dynamic languages like Javascript, Python, and Ruby do not support generics
either -
at least not in the sense of compile-time checking.
But functional idioms work just fine in those languages.
For example,
in Javascript you can pass any list to a generic list manipulation function,
and it just works.
Go occupies an awkward middle ground where it checks types at compile time,
but does not provide tools to explain to the compiler how input and output
types relate to each other.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Generics - and type variables in particular -
are a means to &amp;quot;talk&amp;quot; about types.
They let you use function signatures to make statements like,
&amp;quot;This function takes a slice of values of some type, and returns a slice of
values of the same type.&amp;quot;
Working in a programming language with no type variables is as frustrating to
me as talking in a spoken language without the word &amp;quot;the&amp;quot;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;So Go code must re-implement list abstractions everywhere.
Consider this Go function:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;That function iterates over an input collection, skips over some values, does
something with the values that are not skipped, and returns a collection with
the results.
In other words, this is a &lt;code class=&quot;css-0&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;take&lt;/code&gt; operation.
Here is an equivalent Rust implementation:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Rust version lets you say what you mean.
In other words, the Rust function is &amp;quot;declarative&amp;quot;.
And the difference becomes more pronounced if you want to process values
concurrently -
for example to make network requests in parallel.
More on that in a bit.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I once complained to a coworker about a lack of abstractions in Go.
My coworker replied, &amp;quot;Well, maybe you shouldn&amp;#x27;t be doing that.&amp;quot;
So I want to emphasize that functional abstractions do not necessarily make
code less efficient.
Rust typically does &amp;quot;list&amp;quot; manipulation using lazily-evaluated iterators.
(This is also how the standard data structures in Clojure work.)
So you can chain &lt;code class=&quot;css-0&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;take&lt;/code&gt;, without intermediate collection
allocations,
and without wasting cycles on computing values beyond those that the caller
requests.
The function above does not run those &lt;code class=&quot;css-0&quot;&gt;filter&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt; callbacks on every
element in the input collection -
it stops processing elements as soon as it has enough results to satisfy the
&lt;code class=&quot;css-0&quot;&gt;take(count)&lt;/code&gt; step.
On top of that,
&lt;code class=&quot;css-0&quot;&gt;iter&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;take&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;collect&lt;/code&gt; are polymorphic methods,
but they are dispatched statically thanks to a compile-time monomorphization
step.
And the compiler will probably inline the &lt;code class=&quot;css-0&quot;&gt;filter&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt; callbacks.
There are some notes in the Rust Book on
&lt;a href=&quot;https://rust-lang.github.io/book/ch13-04-performance.html&quot; class=&quot;css-1od09yo&quot;&gt;performance of functional abstractions on iterators&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It is possible that my coworker was more concerned with cognitive burden
than with performance.
I think that complaints of cognitive burden may be partly a result of looking
at unfamiliar idioms.
To an experienced functional programmer,
a call to, e.g., &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt; states a clear intention:
&amp;quot;The input collection will be transformed according to this mapping function.&amp;quot;
With some practice declarative code is fast to read and understand.
And type checkers are more effective at checking declarative code than at
checking custom &lt;code class=&quot;css-0&quot;&gt;for&lt;/code&gt; loops.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Let&amp;#x27;s get into the parallel-fetch problem that I mentioned.
Here is a Go function that I wrote recently to fetch a set of documents in
parallel:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The use of a &lt;code class=&quot;css-0&quot;&gt;WaitGroup&lt;/code&gt;, the allocation of a new slice, copying result values,
and explicit error checks are all low-level details that I should not have to
reimplement every time I want to do things concurrently.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Now that I am looking at this again,
I realize there might be a problem here with concurrent access to the
&lt;code class=&quot;css-0&quot;&gt;docs&lt;/code&gt; slice.
Maybe it would be a good idea to use a mutex around updates to &lt;code class=&quot;css-0&quot;&gt;docs&lt;/code&gt;,
or to send results from goroutines back to the main thread via a channel.
But if I use a channel I will need to implement a custom struct or use two channels,
because I want to capture errors,
and I cannot send the type &lt;code class=&quot;css-0&quot;&gt;(models.Document, error)&lt;/code&gt; over a channel,
because Go tuples are not first-class values...&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Rust gives a compile-time error if a mutable reference to a thread-unsafe data
structure is passed to a function that could run in another thread.
So I don&amp;#x27;t have to worry as much about what is or is not thread-safe when
writing Rust code.
But that is almost made moot by the fact that Rust can hide hairy concurrency
details in library functions.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Compare the Go code to an equivalent Rust function that uses the &lt;a href=&quot;https://aturon.github.io/blog/2016/08/11/futures/&quot; class=&quot;css-1od09yo&quot;&gt;futures&lt;/a&gt;
library:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Rust function works the same way that the Go function does:
if all fetches are successful,
you get a collection of data from responses.
But if any fetches fail, you get the first failure as an error value.
The difference is that in the Rust version the concurrency, mapping, and
error-checking details are handled by a general-purpose library.
(Another difference is that the Rust version returns as soon as any fetch fails,
but the Go version always waits for all fetches to finish.)&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This example shows that the &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt; abstraction is so powerful that concurrent
versus sequential execution is an implementation detail.
(I will talk more about more about functional parallel processing in a later
section.)&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Rust implementation assumes that &lt;code class=&quot;css-0&quot;&gt;fetch_document&lt;/code&gt; returns a &lt;code class=&quot;css-0&quot;&gt;Future&lt;/code&gt;.
The function &lt;code class=&quot;css-0&quot;&gt;future::join_all&lt;/code&gt; also returns a &lt;code class=&quot;css-0&quot;&gt;Future&lt;/code&gt;.
(Futures work very similarly to promises in Javascript:
they represent an eventual result or error.)
It would be more idiomatic to return that last future directly instead of using
&lt;code class=&quot;css-0&quot;&gt;wait&lt;/code&gt; to block on the result.
But blocking gives us a function that is logically equivalent to the Go version,
and shows that using futures in Rust does not lock you into using
callback-driven code everywhere.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Using futures, and the related &lt;code class=&quot;css-0&quot;&gt;Stream&lt;/code&gt; type,
makes some kinds of network server implementations much simpler than with
blocking IO.
In particular, streaming requests and responses are easy if code is implemented
in terms of &lt;code class=&quot;css-0&quot;&gt;Stream&lt;/code&gt; values.&lt;/p&gt;&lt;h3 id=&quot;third-party-libraries-are-second-class-citizens&quot; class=&quot;css-1u9n620&quot;&gt;Third-party libraries are second-class citizens&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Go has a magical function called &lt;code class=&quot;css-0&quot;&gt;make&lt;/code&gt; that seems to do whatever the standard
library authors need for a given type.
It takes a type as an argument, which is unlike most other Go functions.
Called with one argument it initializes a small slice, map, or channel.
&lt;code class=&quot;css-0&quot;&gt;make&lt;/code&gt; can take one or two integer arguments,
depending on the choice of the first argument.
For example, when creating a slice you can provide a length and a capacity:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When creating a channel, you can specify the channel&amp;#x27;s buffer size with
a second argument to &lt;code class=&quot;css-0&quot;&gt;make&lt;/code&gt;.
As far as I know, this is the only way to set a channel&amp;#x27;s buffer size.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It seems that standard libraries have a special privilege to overload &lt;code class=&quot;css-0&quot;&gt;make&lt;/code&gt;
to delegate to custom constructor code when initializing their types.
Third-party libraries do not get to do this.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We see something like this with the &lt;code class=&quot;css-0&quot;&gt;range&lt;/code&gt; operator also.
&lt;code class=&quot;css-0&quot;&gt;range&lt;/code&gt; is one of the few constructs that changes its behavior depending on the
number of arguments that are assigned from its output:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But more importantly:
only standard library types get to be range-able.
There is no way to make a third-party data type iterable.
Library authors can implement adapters to output a view of their data structure
as a slice, or to spit out values over a channel.
But that puts extra complexity on code that uses third-party data structures,
and requires programmers to use non-standard idioms.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Yet another privilege is that only standard types can be compared using &lt;code class=&quot;css-0&quot;&gt;==&lt;/code&gt;,
&lt;code class=&quot;css-0&quot;&gt;&amp;gt;&lt;/code&gt;, etc.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;And of course only standard libraries are allowed to define generic types.
This is severely limiting to the library ecosystem around Go.
It means that, for example,
a third-party functional data structures library,
or a third-party futures library
will never be as usable as the standard library collection types.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Rust supports generics for third-party code;
and Rust implements iteration, equality, and comparison via traits that any
third-party type can implement.
Third-party Rust types are nearly indistinguishable from standard library
types,
which promotes innovation in Rust&amp;#x27;s library ecosystem.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Incidentally,
&lt;code class=&quot;css-0&quot;&gt;make&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;range&lt;/code&gt; are ad-hoc examples of a pattern that has generalized
support in Rust:
functions that are polymorphic in their return type.
Rust traits are more flexible than typical object-oriented interfaces:
when an interface method is dispatched the choice of the method implementation
to execute is determined solely by the type of the receiver of the method.
But a trait method implementation can be selected based on
the type of the receiver,
the type of any argument position,
the combination of types of multiple argument positions
(e.g., an equality trait can require that the two polymorphic arguments to an
&lt;code class=&quot;css-0&quot;&gt;equals&lt;/code&gt; method have the same type),
or by the expected return type.
An implementation of &lt;code class=&quot;css-0&quot;&gt;make&lt;/code&gt; in Rust might look like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Any type can implement any trait.
The only rule is that the code for an implementation must be in the same crate
as either the type or the trait.
(A &amp;quot;crate&amp;quot; is a distributable Rust package.)
So if Rust itself or a Rust library implemented &lt;code class=&quot;css-0&quot;&gt;make&lt;/code&gt;,
then any third-party library could define their own custom implementations.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I had to implement &lt;code class=&quot;css-0&quot;&gt;make&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;make_with_capacity&lt;/code&gt; as separate methods because
Rust does not support method overloading.
But in theory neither does Go.&lt;/p&gt;&lt;h2 id=&quot;the-ugly&quot; class=&quot;css-1bzbprl&quot;&gt;The Ugly&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;These are some features of Go that I dislike on what I think are more
subjective grounds than the &amp;quot;bad parts&amp;quot;,
or that could be worked around if some of the bad parts were fixed.&lt;/p&gt;&lt;h3 id=&quot;no-tagged-unions-limited-pattern-matching&quot; class=&quot;css-1u9n620&quot;&gt;No tagged unions, limited pattern matching&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Scala is another language that encourages passing messages over channels.
Scala supports tagged-union types in combination with pattern matching.
These features make great companions for channels:
a tagged union describes a fixed set of message types that a channel can accept
or produce.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Rust supports tagged-union types in the form of enums:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Rust does not require channels to be closed explicitly.
Channel senders and receivers implement a &lt;code class=&quot;css-0&quot;&gt;Drop&lt;/code&gt; trait;
any type can implement &lt;code class=&quot;css-0&quot;&gt;Drop&lt;/code&gt; to schedule some cleanup code to run when a value
goes out of scope.
In this example when the background thread terminates &lt;code class=&quot;css-0&quot;&gt;resp_tx&lt;/code&gt; goes out of
scope,
and closes automatically.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This block is the pattern matching code from the example above:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The block matches against &lt;code class=&quot;css-0&quot;&gt;instr&lt;/code&gt;,
which has the type &lt;code class=&quot;css-0&quot;&gt;CounterInstruction&lt;/code&gt;.
There are three variants for &lt;code class=&quot;css-0&quot;&gt;CounterInstruction&lt;/code&gt;;
each variant is represented by a pattern in the &lt;code class=&quot;css-0&quot;&gt;match&lt;/code&gt; block with code to run
in case the pattern matches.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Go has type switches, which are similar to pattern matching.
Comparable Go matching code could look like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The difference is in compile-time type-safety:
A tagged union describes a &lt;em class=&quot;css-0&quot;&gt;fixed&lt;/em&gt; set of possible messages.
When sending a value to a channel in Rust,
the compiler is able to check that the value has a type that the channel
consumer knows how to handle.
And what is especially valuable is checking that all pieces of code that send
or receive on a channel are consistent in the types of values that the they
produce or consume.
If you make a change to the set of possible messages in a Rust program,
but forget to update some critical code to accommodate the change,
the type checker will report an error at compile time.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Because Go does not support tagged unions,
messages over polymorphic channels are dynamically typed.
You can use an interface as the type parameter for a channel,
which does limit the types of messages that are sent over the channel.
But a Go interface is not sealed:
when a new type is created that implements the interface
there is no compile-time check to ensure that all consumers of the channel are
updated to handle the new type.
Unpacking channel messages with a type switch
(as opposed to using exclusively interface methods)
can lead to bugs that a different type system would have caught.&lt;/p&gt;&lt;h3 id=&quot;dynamic-typing&quot; class=&quot;css-1u9n620&quot;&gt;Dynamic typing&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Go makes heavy use of dynamic type-checking.
Any use of &lt;code class=&quot;css-0&quot;&gt;interface{}&lt;/code&gt;, any type assertion or type switch,
is dynamic typing.
This is good and bad:
without generics, it is often necessary to coerce a value to a different type;
dynamic type assertions are a safer way to do this than unchecked type casts.
Either way the program will (probably) crash at runtime if a value ends up
having an unexpected type.
But with an unchecked cast the program might corrupt memory or leak information
to attackers before crashing.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The fact that errors are reported at runtime means that problems are likely to
go unnoticed without good test coverage.
That does not just mean 100% code coverage -
there might be different combinations of conditions that lead to a code path
and a type error might not manifest under every combination.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A type checker that can detect errors at compile time is like an extra test
suite that always tests every combination of conditions.
Dynamic type tests are nearly unheard of in Rust because the compiler resolves
types at compile time.
Pattern matches on enum variants looks a bit like a dynamic type test -
but pattern matching is qualitatively different because enums are sealed.
There is a compile-time guarantee that pattern matching will not fail.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are two caveats:&lt;/p&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;unsafe&lt;/code&gt; blocks, functions, and traits in Rust do make unchecked type casts -
especially when interacting with the foreign function interface.
But &lt;code class=&quot;css-0&quot;&gt;unsafe&lt;/code&gt; implementations are delimited regions where the normal rules do
not apply.
The best practice is to keep unsafe code to a minimum;
and &lt;a href=&quot;https://alex-ozdemir.github.io/rust/unsafe/unsafe-in-rust-syntactic-patterns/#how-many-crates-use-unsafe&quot; class=&quot;css-1od09yo&quot;&gt;most libraries&lt;/a&gt; do not use any &lt;code class=&quot;css-0&quot;&gt;unsafe&lt;/code&gt; code.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Rust supports &lt;a href=&quot;https://doc.rust-lang.org/book/trait-objects.html&quot; class=&quot;css-1od09yo&quot;&gt;trait objects&lt;/a&gt;,
which is are cases where the compiler does not resolve a concrete type for
a value at compile time.
But the compiler does verify at compile-time that the value implements a given
trait.
A trait object is a lot like a Go interface value -
except that a trait object cannot have a &lt;code class=&quot;css-0&quot;&gt;nil&lt;/code&gt; value.
Despite the lack of a compile-time concrete type,
it is safe to dispatch trait methods on a trait object because there is
compile-time verification that whatever value is in the object implements the
given trait and therefore implements the appropriate methods.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;dynamic-dispatch-reinventing-the-wheel&quot; class=&quot;css-1u9n620&quot;&gt;Dynamic dispatch, reinventing the wheel&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In general Rust resolves every value to a concrete type at compile time.
That means that Rust can use static dispatch when invoking trait methods.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;(As I mentioned,
the exception is &lt;a href=&quot;https://doc.rust-lang.org/book/trait-objects.html&quot; class=&quot;css-1od09yo&quot;&gt;trait objects&lt;/a&gt;,
which do use dynamic dispatch much like Go interface values.
My understanding is that idiomatic Rust code uses trait objects sparingly.
Most uses of traits in Rust use the trait as a bound on a type variable,
which leads to compile-time resolution to a concrete type.
For details see the Rust book sections on &lt;a href=&quot;https://doc.rust-lang.org/book/traits.html&quot; class=&quot;css-1od09yo&quot;&gt;traits&lt;/a&gt; and &lt;a href=&quot;https://doc.rust-lang.org/book/trait-objects.html&quot; class=&quot;css-1od09yo&quot;&gt;trait objects&lt;/a&gt;.)&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In Go every invocation of an interface method uses dynamic dispatch.
Dynamic dispatch, type assertions, and type switches require runtime reflection,
which adds some runtime overhead.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The technique of resolving concrete types at compile-time is not new to Rust,
and it has nothing to do with borrow-checking.
Haskell was doing the same thing at least ten years before work began on Go.
And Haskell has just as much polymorphic flexibility as Rust or Go.
(Rust traits are an adaptation of Haskell &lt;a href=&quot;http://learnyouahaskell.com/types-and-typeclasses&quot; class=&quot;css-1od09yo&quot;&gt;type classes&lt;/a&gt;.)&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The designers of Go wanted a model for polymorphism that is simpler and more
flexible than what other object-oriented languages provide.
In particular they wanted composition over inheritance,
and an ability to implement interfaces after-the-fact
so that new interfaces can be applied to pre-existing types.
This is &lt;em class=&quot;css-0&quot;&gt;exactly&lt;/em&gt; what traits and type classes do.
The solution that Go introduced feels to me like a less-capable reinvented wheel.&lt;/p&gt;&lt;h3 id=&quot;no-first-class-tuple&quot; class=&quot;css-1u9n620&quot;&gt;No first-class tuple&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Go supports multiple return values.
Other languages (including Rust) support multiple return values via first-class
data types called &amp;quot;tuples&amp;quot;.
First-class values can have methods,
can be stored in data structures,
and can be passed over channels.
Multiple-values in Go cannot do any of those things.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We already saw tuple return values in the implementation of the &lt;code class=&quot;css-0&quot;&gt;Make&lt;/code&gt; trait,
and in the &lt;code class=&quot;css-0&quot;&gt;new_counter&lt;/code&gt; example.
Here is a smaller example:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A consequence of the lack of first-classness in Go is that there is no obvious
way to communicate potential failures over a channel.
It seems like this should work, but it does not:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;As far as I know the best options for doing this are to define a custom
&lt;code class=&quot;css-0&quot;&gt;struct&lt;/code&gt; type;
to coerce both success and error values to &lt;code class=&quot;css-0&quot;&gt;interface{}&lt;/code&gt;,
and use a type switch to distinguish success and error on the other end of the
channel;
or to send success and error values over two parallel channels.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Another consequence is that there is no good way to define methods on multiple
return values,
or to define a function that accepts multiple return values with a callback.
This makes it impractical to define an analog of Rust&amp;#x27;s &lt;code class=&quot;css-0&quot;&gt;and_then&lt;/code&gt; combinator
for Go return values.&lt;/p&gt;&lt;h3 id=&quot;shortage-of-high-level-parallelism-and-concurrency-features&quot; class=&quot;css-1u9n620&quot;&gt;Shortage of high-level parallelism and concurrency features&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The post &lt;a href=&quot;https://gist.github.com/kachayev/21e7fe149bc5ae0bd878&quot; class=&quot;css-1od09yo&quot;&gt;Channels Are Not Enough&lt;/a&gt; makes a detailed argument about problems
with Go&amp;#x27;s lack of concurrency abstractions.
In the section &amp;quot;List manipulation cannot be abstracted&amp;quot; I pointed out that Go
does not provide an easy way to run a set of computations in parallel.
@kachayev points out that the problem is more general than that.
Really this is another symptom of lack of generics.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In the parallel fetch example I showed a Rust solution that uses futures,
which are great for concurrent IO.
The &lt;a href=&quot;https://aturon.github.io/blog/2016/08/11/futures/&quot; class=&quot;css-1od09yo&quot;&gt;futures&lt;/a&gt; library relates to a larger async IO library called &lt;a href=&quot;https://tokio.rs/&quot; class=&quot;css-1od09yo&quot;&gt;Tokio&lt;/a&gt;,
which seems to be popular.
But async IO is not intended to provide parallelism.
The excellent book &lt;a href=&quot;http://chimera.labs.oreilly.com/books/1230000000929&quot; class=&quot;css-1od09yo&quot;&gt;Parallel and Concurrent Programming in Haskell&lt;/a&gt; has this
to say about the distinction between parallel and concurrent code:&lt;/p&gt;&lt;blockquote class=&quot;css-1gtwvjp&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A parallel program is one that uses a multiplicity of computational hardware
(e.g., several processor cores) to perform a computation more quickly. The aim
is to arrive at the answer earlier, by delegating different parts of the
computation to different processors that execute at the same time.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;By contrast, concurrency is a program-structuring technique in which there
are multiple threads of control. Conceptually, the threads of control execute
“at the same time”; that is, the user sees their effects interleaved. Whether
they actually execute at the same time or not is an implementation detail;
a concurrent program can execute on a single processor through interleaved
execution or on multiple physical processors.&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Go uses goroutines for both parallel and concurrent programming.
Rust libraries offer a selection of tools whose strengths make them especially
useful for different kinds of problems.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When you want true multi-core parallel processing,
the Map-Reduce pattern offers a powerful solution.
This is another application of list abstractions.
You can apply Map-Reduce in Rust using parallel iterators from the &lt;a href=&quot;https://github.com/nikomatsakis/rayon&quot; class=&quot;css-1od09yo&quot;&gt;Rayon&lt;/a&gt;
library:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Parallel iterators implement a variation of the &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt; method that divides work
among a set of job queues that feed into a worker pool,
so that invocations of the &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt; callback run in parallel.
As with goroutines,
this is lightweight parallelism that scales to a large number of parallel tasks.
But parallel iterators are a high-level abstraction that take care of some
complicated details for you.
For example, Rayon transparently splits work into batches,
which provides better performance than queueing up every invocation of the
&lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt; callback separately.
(The default batch size is 5000 items, but that value is tunable.)
The &lt;code class=&quot;css-0&quot;&gt;sum&lt;/code&gt; method (which is the Reduce step in this example) is also part of Rayon -
which means that it is optimized for consuming batches of results from worker
threads.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It might seem strange to have to use two different libraries for concurrent vs
parallel code.
But those are separate concerns, with different underlying assumptions.
Usually you do not actually want both at the same time.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot; class=&quot;css-1bzbprl&quot;&gt;Conclusion&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;How do I think Go 2.0 could be better?
Generics.
Nearly all of my complaints boil down to lack of support for generics.
But I think that support for non-nil-able types,
and getting rid of zero values would also be useful changes.
Even Rust-style traits might be workable.
Traits would require support for receiver-less methods;
bit it might be possible to make traits work with Go&amp;#x27;s implicit implementation
feature.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In its current form, I prefer not to use Go.
It is not that Go is bad - it is just that there are lots of languages
available that I find more enjoyable.
When I work with Go I cannot help thinking about how I could be doing things
differently in another language.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you are willing to put in the time to understand lifetimes and borrow checking,
Rust makes a fantastic language that does everything that Go does,
but has none of the &amp;quot;bad parts&amp;quot; that I called out.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Javascript is (like Go) easy to learn,
and has wonderful support for concurrency (if not parallelism).
When paired with Flow or Typescript you get more robust type safety than Go
provides.
The combination of Javascript and Flow in particular has none of the &amp;quot;bad
parts&amp;quot; from this post.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Erlang and Scala both support lightweight concurrency in the same style that Go
does,
and they are both great for functional programming.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Clojure is not type-safe - but it does fantastic things!
My favorite functional data structure implementations are the ones in Clojure.
And Clojure encourages functional programming.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Haskell has amazing type safety,
some of the best concurrency and parallelism features that I have seen in any
language,
and is well-suited to network server code.
Haskell is another language that avoids the &amp;quot;bad parts&amp;quot; from this post.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We have better tools than ever for getting work done.
Even Go - whether I like it or not - is clearly useful for building cool things.
But if you take anything away from this post,
I hope it is an interest in taking a look beyond the imperative
/ object-oriented world.
I encourage you to pick one of the languages above,
and take some time to learn about it and to get a good feel for its strengths.
I think you will be glad that you did.&lt;/p&gt;&lt;div class=&quot;footnotes css-0&quot;&gt;&lt;style data-emotion-css=&quot;1ldi06f&quot;&gt;.css-1ldi06f{background-color:var(--theme-ui-colors-muted,#e2e8f0);border:0;height:1px;margin:1rem;}&lt;/style&gt;&lt;hr class=&quot;css-1ldi06f&quot;/&gt;&lt;ol class=&quot;css-15rlv7r&quot;&gt;&lt;li id=&quot;fn-1&quot; class=&quot;css-0&quot;&gt;Soundness is a property of a type system where any &amp;quot;claims&amp;quot; made by program types are guaranteed to hold at runtime. Runtime type errors will not occur if a language is sound.&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref css-1od09yo&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id=&quot;fn-2&quot; class=&quot;css-0&quot;&gt;This may have changed over time; the C and C++ language specifications are evolving, and I am not very familiar with the details.&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref css-1od09yo&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Flow Cookbook: Flow & React]]></title><link>https://sitr.us/2017/01/03/flow-cookbook-react.html</link><guid isPermaLink="false">https://sitr.us/2017/01/03/flow-cookbook-react.html</guid><pubDate>Tue, 03 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;This recipe is part of the &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;/2016/12/20/flow-cookbook.html&quot; class=&quot;css-1od09yo&quot;&gt;Flow Cookbook&lt;/a&gt; series.&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow and React are both Facebook projects -
so as you might imagine, they work quite well together.
React components can take type parameters to specify types for props and state.
Type-checking works well with both &lt;a href=&quot;https://facebook.github.io/react/docs/components-and-props.html#functional-and-class-components&quot; class=&quot;css-1od09yo&quot;&gt;functional and class components&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow type annotations provide an alternative to &lt;code class=&quot;css-0&quot;&gt;propTypes&lt;/code&gt; runtime checks.
Flow&amp;#x27;s static checking has some advantages:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;Problems are reported immediately - it is not necessary to run tests that
evaluate every component to identify &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt; mismatches.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Flow types can be more precise and concise than &lt;code class=&quot;css-0&quot;&gt;propTypes&lt;/code&gt;.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Flow can also check &lt;code class=&quot;css-0&quot;&gt;state&lt;/code&gt; as well as &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt;.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;In addition to checking that a component gets the correct &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt;,
Flow checks that &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;states&lt;/code&gt; are used correctly within the component&amp;#x27;s
&lt;code class=&quot;css-0&quot;&gt;render&lt;/code&gt; method,
and in other component methods.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow changed the way that it handles React types in &lt;a href=&quot;https://github.com/facebook/flow/blob/master/Changelog.md#0530&quot; class=&quot;css-1od09yo&quot;&gt;version 0.53.0&lt;/a&gt;.
This recipe assumes that you are using Flow v0.53.0 or later.&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;how-it-works&quot; class=&quot;css-1bzbprl&quot;&gt;How it works&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;With Flow you specify the types of &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;state&lt;/code&gt; using type parameters on
the &lt;code class=&quot;css-0&quot;&gt;React.Component&lt;/code&gt; class.
The form for type parameters is:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Where &lt;code class=&quot;css-0&quot;&gt;Props&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;State&lt;/code&gt; can be whatever object types you want.
If your component has the props &lt;code class=&quot;css-0&quot;&gt;title&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;createdAt&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;authorId&lt;/code&gt;
then a definition for &lt;code class=&quot;css-0&quot;&gt;Props&lt;/code&gt; could look like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If your component is stateless then you can omit the &lt;code class=&quot;css-0&quot;&gt;State&lt;/code&gt; parameter,
in which case the state type defaults to &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;Props&lt;/code&gt; should list types for &lt;em class=&quot;css-0&quot;&gt;all&lt;/em&gt; props -
even props that are optional or that have default values.
If you add a &lt;code class=&quot;css-0&quot;&gt;defaultProps&lt;/code&gt; value to your component then Flow will
automatically infer that fields from &lt;code class=&quot;css-0&quot;&gt;defaultProps&lt;/code&gt; are not required when your
component is called.
So you should not use a &lt;code class=&quot;css-0&quot;&gt;?&lt;/code&gt; in the corresponding field in your &lt;code class=&quot;css-0&quot;&gt;Props&lt;/code&gt; type.
Leaving out the &lt;code class=&quot;css-0&quot;&gt;?&lt;/code&gt; lets you avoid unnecessary &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt; checks in your
component&amp;#x27;s methods.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To declare a type for &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt; in a stateless functional component use
a type annotation like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Once again, &lt;code class=&quot;css-0&quot;&gt;Props&lt;/code&gt; can be any object type that you want,
and should include types for all props, even those that have default values.
The return type for a functional component is &lt;code class=&quot;css-0&quot;&gt;React.Element&amp;lt;*&amp;gt;&lt;/code&gt; -
but Flow can infer that so don&amp;#x27;t bother with a return type annotation.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Type definitions for React are built into Flow.
The Flow documentation includes a
&lt;a href=&quot;https://flow.org/en/docs/react/types/&quot; class=&quot;css-1od09yo&quot;&gt;type reference for React types&lt;/a&gt;.
It is useful to look at those definitions to see exactly what Flow expects.
You can also go straight to the source:
a lot of what I know about using Flow came from examining
&lt;a href=&quot;https://github.com/facebook/flow/blob/master/lib/react.js&quot; class=&quot;css-1od09yo&quot;&gt;Flow&amp;#x27;s type definition file for React&lt;/a&gt;
and other files in the same directory.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Flow documentation also includes its own guide on
&lt;a href=&quot;https://flow.org/en/docs/react/&quot; class=&quot;css-1od09yo&quot;&gt;using Flow with React&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;a-hacker-news-client&quot; class=&quot;css-1bzbprl&quot;&gt;A Hacker News client&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Let&amp;#x27;s build on the code from the &lt;a href=&quot;/2016/12/20/flow-cookbook-unpacking-json.html&quot; class=&quot;css-1od09yo&quot;&gt;Unpacking JSON API data&lt;/a&gt; recipe,
and build a simple Hacker News client.
The client will fetch lists of stories from the Hacker News API to display.
When the user selects a story,
the client will fetch and display comments.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Let&amp;#x27;s start with a component that displays a Hacker News story as a single line,
and accepts a callback to do something if the user selects that story.
First we define a type for the props that this component will accept.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We import the &lt;code class=&quot;css-0&quot;&gt;Story&lt;/code&gt; type from &lt;code class=&quot;css-0&quot;&gt;&amp;#x27;flow-cookbook-hacker-news&amp;#x27;&lt;/code&gt;,
which is the example code from the &lt;a href=&quot;/2016/12/20/flow-cookbook-unpacking-json.html&quot; class=&quot;css-1od09yo&quot;&gt;Unpacking JSON API data&lt;/a&gt; recipe.
That &lt;code class=&quot;css-0&quot;&gt;Story&lt;/code&gt; type is a ready-made type that describes everything we will want
to display in the new client.
Take a look at the &lt;a href=&quot;https://github.com/hallettj/flow-cookbook-hacker-news/blob/master/index.js.flow#L9&quot; class=&quot;css-1od09yo&quot;&gt;source file&lt;/a&gt; to see what &lt;code class=&quot;css-0&quot;&gt;Story&lt;/code&gt; looks like.
(The &lt;code class=&quot;css-0&quot;&gt;import { type T }&lt;/code&gt; bit is &lt;a href=&quot;https://flowtype.org/docs/syntax.html#importing-and-exporting-types&quot; class=&quot;css-1od09yo&quot;&gt;Flow syntax&lt;/a&gt; -
it is not part of Javascript.)&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;StoryListItemProps&lt;/code&gt; type lists the props that will be given to our
component.
The props are a &lt;code class=&quot;css-0&quot;&gt;story&lt;/code&gt;,
and a callback called &lt;code class=&quot;css-0&quot;&gt;onSelect&lt;/code&gt;.
The type indicates that &lt;code class=&quot;css-0&quot;&gt;onSelect&lt;/code&gt; must be a function that takes zero arguments,
and that returns &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt; (a.k.a. &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt;).
It is not necessary to require that the return type be &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt; -
you could use &lt;code class=&quot;css-0&quot;&gt;mixed&lt;/code&gt; instead if you do not want to put any constraint on the
callback&amp;#x27;s return type.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;My opinion is that using &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; is a clear indication to the caller that the
component will not do anything with a return value.
On the other hand you might get an irritating type error if you provide
a single-expression arrow function that implicitly returns a value.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;And here is the &lt;code class=&quot;css-0&quot;&gt;StoryListItem&lt;/code&gt; component itself:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The type annotation, &lt;code class=&quot;css-0&quot;&gt;props: StoryListItemProps&lt;/code&gt;,
does two things for us:
when &lt;code class=&quot;css-0&quot;&gt;StoryListItem&lt;/code&gt; is rendered, Flow will check that it is given the required
props of the appropriate types;
and Flow will also check uses of &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt; in the body of the &lt;code class=&quot;css-0&quot;&gt;StoryListItem&lt;/code&gt;
function for consistency with &lt;code class=&quot;css-0&quot;&gt;StoryListItemProps&lt;/code&gt;.
Checking props from both directions ensures that a component is in alignment
with its callers.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In general if a component does not have internal state,
and does not use life cycle callbacks such as &lt;code class=&quot;css-0&quot;&gt;componentDidMount&lt;/code&gt;,
then I prefer to use a functional component, like &lt;code class=&quot;css-0&quot;&gt;StoryListItem&lt;/code&gt;.
A functional component cannot have methods that refer to &lt;code class=&quot;css-0&quot;&gt;this.props&lt;/code&gt;;
so I made the &lt;code class=&quot;css-0&quot;&gt;selectStory&lt;/code&gt; event handler a top-level function that accepts
a reference to the component&amp;#x27;s props as an argument.
Reusing the &lt;code class=&quot;css-0&quot;&gt;StoryListItemProps&lt;/code&gt; type in the signatures for the component and
for the event handler means that we can easily keep track of available props in
both functions.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;selectStory&lt;/code&gt; also takes an &lt;code class=&quot;css-0&quot;&gt;event&lt;/code&gt; argument.
The &lt;code class=&quot;css-0&quot;&gt;Event&lt;/code&gt; type is built into Flow,
and Flow knows about the &lt;code class=&quot;css-0&quot;&gt;preventDefault&lt;/code&gt; method.
Flow&amp;#x27;s version of the &lt;code class=&quot;css-0&quot;&gt;Event&lt;/code&gt; type is defined in Flow&amp;#x27;s
&lt;a href=&quot;https://github.com/facebook/flow/blob/master/lib/dom.js#L210&quot; class=&quot;css-1od09yo&quot;&gt;DOM type definitions file&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To populate instances of the &lt;code class=&quot;css-0&quot;&gt;StoryListItem&lt;/code&gt; component,
we will need to make API requests and update some state.
Here is a type for that state,
and the parameters for the top-level component, which will manage that state:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The definition of &lt;code class=&quot;css-0&quot;&gt;AppState&lt;/code&gt; shows that the state will hold an array of &lt;code class=&quot;css-0&quot;&gt;stories&lt;/code&gt;,
which will not be defined while stories are loading;
a &lt;code class=&quot;css-0&quot;&gt;selectedStory&lt;/code&gt;,
which will be defined when the user is viewing comments on a story;
and an &lt;code class=&quot;css-0&quot;&gt;error&lt;/code&gt;,
in case something goes wrong while loading stories.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Because every property in &lt;code class=&quot;css-0&quot;&gt;AppState&lt;/code&gt; is optional,
&lt;code class=&quot;css-0&quot;&gt;state&lt;/code&gt; can be initialized as an empty object.
(The question mark at the end of a property name indicates that property might
not be set.)
&lt;code class=&quot;css-0&quot;&gt;selectedStory&lt;/code&gt; is optional, and its type is nullable,
so there are two question marks on that line.
That is because &lt;code class=&quot;css-0&quot;&gt;selectedStory&lt;/code&gt; is initially not set,
and after a user views a story and then backs out to the story list
&lt;code class=&quot;css-0&quot;&gt;selectedStory&lt;/code&gt; will be set to &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Since &lt;code class=&quot;css-0&quot;&gt;App&lt;/code&gt; has a &lt;code class=&quot;css-0&quot;&gt;constructor&lt;/code&gt;,
we specify the props type both in the second class type parameter,
and again in the type of the &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt; argument to &lt;code class=&quot;css-0&quot;&gt;constructor&lt;/code&gt;.
That is because a subclass can define a constructor with a signature that
differs from the parent class&amp;#x27; constructor.
It would not make much sense to do that in a React Component;
but Flow must be able to check all sorts of classes,
so it does not make assumptions about types of constructor arguments.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;App&lt;/code&gt; will fetch stories when it is mounted.
So we extend its definition with a &lt;code class=&quot;css-0&quot;&gt;componentDidMount&lt;/code&gt; callback.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;fetchTopStories&lt;/code&gt; takes an argument that specifies the number of stories to
fetch,
and it returns a promise.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Once stories are loaded we can display them via a &lt;code class=&quot;css-0&quot;&gt;render&lt;/code&gt; method.
But the component will initially display while the API request is loading;
so we will have to check whether stories have loaded,
and display a loading indicator if they are not ready.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The import piece of the JSX that is finally returned from &lt;code class=&quot;css-0&quot;&gt;render&lt;/code&gt; is &lt;code class=&quot;css-0&quot;&gt;content&lt;/code&gt;,
which is assigned a different value depending on whether an error occurred
while fetching stories,
a specific story has been selected,
or there is an array of stories available, and none has been selected.
If none of those cases applies,
then it means that stories are still loading.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When displaying a list of stories, &lt;code class=&quot;css-0&quot;&gt;content&lt;/code&gt; is populated by a list of
instances of &lt;code class=&quot;css-0&quot;&gt;StoryListItem&lt;/code&gt;,
which we defined earlier.
There is nothing special about rendering a type-checked component -
you pass props the same way as with any component.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow can track changes to the type of a variable over a series of statements.
Flow infers that when &lt;code class=&quot;css-0&quot;&gt;content&lt;/code&gt; is first defined its type is &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt;;
and it infers that no matter which code path gets executed,
by the time we get to the &lt;code class=&quot;css-0&quot;&gt;return&lt;/code&gt; statement the type has changed to either
&lt;code class=&quot;css-0&quot;&gt;React.Element&amp;lt;*&amp;gt;&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;React.Element&amp;lt;*&amp;gt;[]&lt;/code&gt; -
either of which is compatible with Flow&amp;#x27;s expectations for JSX content.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The event handling methods &lt;code class=&quot;css-0&quot;&gt;selectStory&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;deselectStory&lt;/code&gt; make state changes
to track whether the user is looking at a specific story:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Those state changes affect the output from &lt;code class=&quot;css-0&quot;&gt;render&lt;/code&gt; to switch between displaying
a list of story titles, or a detail view for a single story.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;At this point
we have made references to &lt;code class=&quot;css-0&quot;&gt;App&lt;/code&gt;&amp;#x27;s state in &lt;code class=&quot;css-0&quot;&gt;componentDidMount&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;render&lt;/code&gt;,
&lt;code class=&quot;css-0&quot;&gt;selectStory&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;deselectStory&lt;/code&gt;.
Flow checks all of those uses against the definition of &lt;code class=&quot;css-0&quot;&gt;AppState&lt;/code&gt;.
For example, if we made a mistaken assumption that &lt;code class=&quot;css-0&quot;&gt;selectedStory&lt;/code&gt; holds an ID,
as opposed to a value of type &lt;code class=&quot;css-0&quot;&gt;Story&lt;/code&gt;,
and tried to write something like &lt;code class=&quot;css-0&quot;&gt;this.setState({ selectedStory: story.id })&lt;/code&gt;
Flow would report an error, and point out the mismatch.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have not given the implementation of &lt;code class=&quot;css-0&quot;&gt;StoryView&lt;/code&gt;,
which is responsible for displaying comments on a story.
It happens that &lt;code class=&quot;css-0&quot;&gt;StoryView&lt;/code&gt; is quite similar to &lt;code class=&quot;css-0&quot;&gt;App&lt;/code&gt; -
except that &lt;code class=&quot;css-0&quot;&gt;StoryView&lt;/code&gt; loads comments where &lt;code class=&quot;css-0&quot;&gt;App&lt;/code&gt; loads stories.
The interested reader can see the full details of &lt;code class=&quot;css-0&quot;&gt;StoryView&lt;/code&gt; in the
&lt;a href=&quot;https://github.com/hallettj/flow-cookbook-react/blob/master/src/StoryView.js&quot; class=&quot;css-1od09yo&quot;&gt;accompanying code&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Complete working code is available at
&lt;a href=&quot;https://github.com/hallettj/flow-cookbook-react&quot; class=&quot;css-1od09yo&quot;&gt;https://github.com/hallettj/flow-cookbook-react&lt;/a&gt;.
Your next assignment is to clone that code and to add some features.
Some ideas to try are to add links to the original article for each story,
or to display profile pages for posters and commenters.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The example code here uses React&amp;#x27;s own state features to manage app state.
That helps to keep this recipe self-contained.
But in my opinion the best practice is to keep state in a third-party
state-management framework such as &lt;a href=&quot;http://redux.js.org/&quot; class=&quot;css-1od09yo&quot;&gt;Redux&lt;/a&gt;.
For details on using Flow with Redux and &lt;a href=&quot;http://redux.js.org/docs/basics/UsageWithReact.html&quot; class=&quot;css-1od09yo&quot;&gt;react-redux&lt;/a&gt;
take a look at the next recipe, &lt;a href=&quot;/todo.html&quot; class=&quot;css-1od09yo&quot;&gt;Flow &amp;amp; Redux&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Flow Cookbook]]></title><link>https://sitr.us/2016/12/20/flow-cookbook.html</link><guid isPermaLink="false">https://sitr.us/2016/12/20/flow-cookbook.html</guid><pubDate>Tue, 20 Dec 2016 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Type-checking can be a useful asset in a Javascript project.
A type checker can catch problems that are introduced when adding features or
refactoring, which can reduce the amount of time spent debugging and testing.
Type annotations provide a form of always-up-to-date documentation that makes
it easier for developers to understand an unfamiliar code base.
But it is important to use type-checking effectively to get its full benefit.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Javascript community is fortunate to have a choice of two great type
checkers.
These recipes focus on &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://flowtype.org/&quot; class=&quot;css-1od09yo&quot;&gt;Flow&lt;/a&gt;, and introduce patterns for using Flow
effectively.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To get updates when new recipes or extras are posted,
subscribe to the &lt;a href=&quot;https://feeds.feedburner.com/hallettj&quot; class=&quot;css-1od09yo&quot;&gt;Atom feed&lt;/a&gt;,
or follow &lt;a href=&quot;https://twitter.com/FlowCookbook&quot; class=&quot;css-1od09yo&quot;&gt;@FlowCookbook&lt;/a&gt;.
I appreciate your requests, questions, and suggestions!
Please send feedback by leaving comments here, or on recipes or extras;
or send messages to &lt;a href=&quot;https://twitter.com/FlowCookbook&quot; class=&quot;css-1od09yo&quot;&gt;@FlowCookbook&lt;/a&gt;.&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;recipes&quot; class=&quot;css-1bzbprl&quot;&gt;Recipes&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;These are primers on practical patterns for Flow.
I recommend using these patterns in any project that uses Flow.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;(Coming soon) &lt;em class=&quot;css-0&quot;&gt;Flow types are duck types&lt;/em&gt; -
Flow is not a new, strait-jacketed OOP language.
Flow is not Java or C#.
Flow is a codification of previously-unwritten Javascript idioms.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;(Coming soon) &lt;em class=&quot;css-0&quot;&gt;Uses for union types&lt;/em&gt;
introduces a pattern for managing data that comes in different shapes.
Union types are helpful for describing Redux actions,
for unpacking incoming JSON data,
and for passing messages over a channel.
If you have been tempted to use subclasses,
take a look at union types to see if they might be a better fit.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;/2016/12/20/flow-cookbook-unpacking-json.html&quot; class=&quot;css-1od09yo&quot;&gt;Unpacking JSON API data&lt;/a&gt; -
Javascript&amp;#x27;s flexibility is useful for handling incoming data in whatever form
it may take.
Flow is designed to be just as flexible when type-checking functions that
process data.
This is a case study that uses the Hacker News API as an example for type-safe
data processing.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;/2017/01/03/flow-cookbook-react.html&quot; class=&quot;css-1od09yo&quot;&gt;Flow &amp;amp; React&lt;/a&gt; -
This recipe demonstrates how to use Flow effectively when creating React
components.
Including type parameters in &lt;a href=&quot;https://facebook.github.io/react/docs/components-and-props.html#functional-and-class-components&quot; class=&quot;css-1od09yo&quot;&gt;functional and class components&lt;/a&gt; provides an
alternative to &lt;code class=&quot;css-0&quot;&gt;propTypes&lt;/code&gt; that can provide better safety and modularity.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;(Coming soon) &lt;em class=&quot;css-0&quot;&gt;Flow &amp;amp; Redux&lt;/em&gt; -
Flow and Redux could have been made for each other.
This recipe demonstrates several patterns that are useful for building Redux
action creators and reducers.
This is a companion to the post on React.&lt;/p&gt;&lt;h2 id=&quot;extras&quot; class=&quot;css-1bzbprl&quot;&gt;Extras&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Extras are not about practical patterns.
In these articles we explore ideas just because they are interesting.
Read these if you want to dig deeper into type theory,
or to learn about Flow&amp;#x27;s lesser-known capabilities.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;(Coming soon) &lt;em class=&quot;css-0&quot;&gt;What are types?&lt;/em&gt;
The short answer is, types are sets of possible values.
This post gets into what that means,
and shows that Flow takes more of a purist approach to types compared to most
object-oriented languages.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;(Coming soon) &lt;em class=&quot;css-0&quot;&gt;The &amp;quot;algebra&amp;quot; in &amp;quot;algebraic data types&amp;quot;&lt;/em&gt; -
In the recipe &lt;em class=&quot;css-0&quot;&gt;Uses for union types&lt;/em&gt; I mentioned that union types are also
called &amp;quot;sum types&amp;quot; or &amp;quot;algebraic data types&amp;quot;.
This post gives a brief background on type algebra so that you can understand
where those terms come from.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;(Coming soon) &lt;em class=&quot;css-0&quot;&gt;Advanced algebraic data types&lt;/em&gt; -
Union types are great, but not perfect.
This post introduces an alternative formulation for sum types that allows Flow
to check for missing pattern matches.
It also shows that GADTs are &lt;em class=&quot;css-0&quot;&gt;almost&lt;/em&gt; possible in Flow.&lt;/p&gt;&lt;h2 id=&quot;more-resources&quot; class=&quot;css-1bzbprl&quot;&gt;More resources&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow has inspired many programmers to put bits to screen.
Here are some articles that I found to particularly helpful:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;https://flowtype.org/docs/getting-started.html&quot; class=&quot;css-1od09yo&quot;&gt;Getting started with Flow&lt;/a&gt;
is a tutorial from the official documentation.
If you don&amp;#x27;t know where to start, start there.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Why use type-checking?
And if you do, &lt;a href=&quot;https://blog.aria.ai/post/why-use-flow/&quot; class=&quot;css-1od09yo&quot;&gt;Why Use Flow?&lt;/a&gt;
Follow that link for the answers,
and learn some things that you might not know about Flow.
Aria Fallah covers a lot of background,
and also introduces some interesting work from &lt;a href=&quot;https://medium.com/@gcanti&quot; class=&quot;css-1od09yo&quot;&gt;Giulio Canti&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://javascriptplayground.com/blog/2017/01/npm-flowjs-javascript/&quot; class=&quot;css-1od09yo&quot;&gt;Authoring and publishing JavaScript modules with Flow&lt;/a&gt;
is a detailed guide on publishing an NPM module with Flow type annotations
included,
so that anyone who uses the library can benefit from those annotations if they
choose to use Flow as well.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Flow Cookbook: Unpacking JSON API data]]></title><link>https://sitr.us/2016/12/20/flow-cookbook-unpacking-json.html</link><guid isPermaLink="false">https://sitr.us/2016/12/20/flow-cookbook-unpacking-json.html</guid><pubDate>Tue, 20 Dec 2016 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;This recipe is part of the &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;/2016/12/20/flow-cookbook.html&quot; class=&quot;css-1od09yo&quot;&gt;Flow Cookbook&lt;/a&gt; series.&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Hacker News provides &lt;a href=&quot;https://github.com/HackerNews/API&quot; class=&quot;css-1od09yo&quot;&gt;a public API&lt;/a&gt;.
One of the endpoints of that API accepts an ID and responds with an item:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here is an example of a response:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An &amp;quot;item&amp;quot; might be a story, a comment, a question, a job posting, a poll, or
a voting option in a poll.
Each item type has different properties -
for example stories have a &lt;code class=&quot;css-0&quot;&gt;title&lt;/code&gt;, but comments do not.
If you don&amp;#x27;t have context for the ID,
the only way to know what you have fetched is to check the &lt;code class=&quot;css-0&quot;&gt;type&lt;/code&gt; property of
the response at runtime.
To add type safety to a call to this API,
it is necessary to describe a type that encompasses all of the possible shapes
that the returned data might take.
That is going to be a &lt;a href=&quot;https://flowtype.org/docs/union-intersection-types.html&quot; class=&quot;css-1od09yo&quot;&gt;union type&lt;/a&gt;, which will look something like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are not a huge number of properties in the API responses -
but if we list out all of the properties in every branch the result will be too
big and dense for light reading.
So let&amp;#x27;s start by factoring out common properties into helper types.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;All of the different item types have &lt;code class=&quot;css-0&quot;&gt;by&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;id&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;time&lt;/code&gt; properties.
So we can put those all into one type:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Those &lt;code class=&quot;css-0&quot;&gt;Username&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;ID&lt;/code&gt; types are just aliases that I defined for primitive
types:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I think that using aliases like these helps to provide clarity on the purpose
of each property.
If we had a property with the type &lt;code class=&quot;css-0&quot;&gt;by: string&lt;/code&gt; it would not be obvious whether
the value of that property is an ID that happens to be a string,
or a human-readable value.
Using the &lt;code class=&quot;css-0&quot;&gt;Username&lt;/code&gt; type alias makes it obvious that the property will contain
a value that might be suitable for display to users.
Otherwise the types &lt;code class=&quot;css-0&quot;&gt;string&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;Username&lt;/code&gt; are interchangeable.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is more common structure in Hacker News item types:
the story, ask, job, and poll response types all represent top-level submissions,
which have several properties in common:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;With those helper types in place,
we can produce a type that describes all possible items:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An &lt;a href=&quot;https://flowtype.org/docs/union-intersection-types.html&quot; class=&quot;css-1od09yo&quot;&gt;intersection type&lt;/a&gt; like
&lt;code class=&quot;css-0&quot;&gt;{ type: &amp;#x27;story&amp;#x27;, kids: ID[], url: URL } &amp;amp; ItemCommon &amp;amp; TopLevel&lt;/code&gt;
is essentially a shorthand for an object type with a &lt;code class=&quot;css-0&quot;&gt;type&lt;/code&gt; property that is
always equal to &lt;code class=&quot;css-0&quot;&gt;&amp;#x27;story&amp;#x27;&lt;/code&gt;, combined with all of the properties listed in the
&lt;code class=&quot;css-0&quot;&gt;ItemCommon&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;TopLevel&lt;/code&gt; types.
Each branch of the union type contains a type intersection that combines common
properties with the properties that are particular to each item
type.[^top-level union]&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;[^top-level union]: It would have been more concise to write
&lt;code class=&quot;css-0&quot;&gt;type Item = ItemCommon &amp;amp; (/* union type */)&lt;/code&gt;.
That would put the union type inside of the intersection type.
But due to a quirk in Flow as of version 0.36 the union type must be the
outermost layer of of composition for type narrowing to work.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We don&amp;#x27;t have to do anything special to parse incoming data into that type.
&lt;a href=&quot;TODO&quot; class=&quot;css-1od09yo&quot;&gt;Flow types are duck types&lt;/a&gt; -
&lt;code class=&quot;css-0&quot;&gt;Item&lt;/code&gt; is just an alias for plain Javascript objects with a certain structure.
We just need to declare that API responses have the &lt;code class=&quot;css-0&quot;&gt;Item&lt;/code&gt; type.
That is done with with the return type in this function signature:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You might have noticed something a little strange about the types of the &lt;code class=&quot;css-0&quot;&gt;type&lt;/code&gt;
properties in &lt;code class=&quot;css-0&quot;&gt;Item&lt;/code&gt;.
We used string literals where there should have been type expressions!
For example, we gave &lt;code class=&quot;css-0&quot;&gt;&amp;#x27;story&amp;#x27;&lt;/code&gt; as a type in the first branch of the union type.
In fact string literals &lt;em class=&quot;css-0&quot;&gt;are&lt;/em&gt; types.
In a type expression, the literal &lt;code class=&quot;css-0&quot;&gt;&amp;#x27;story&amp;#x27;&lt;/code&gt; is a type with exactly one possible
value: the string &lt;code class=&quot;css-0&quot;&gt;&amp;#x27;story&amp;#x27;&lt;/code&gt;.
(&lt;code class=&quot;css-0&quot;&gt;&amp;#x27;story&amp;#x27;&lt;/code&gt; is a subtype of the more general type, &lt;code class=&quot;css-0&quot;&gt;string&lt;/code&gt;.)
This is useful because it signals to Flow which branch of the union type is
applicable inside the body of a &lt;code class=&quot;css-0&quot;&gt;case&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;if&lt;/code&gt; statement.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Consider this function, which does not type-check:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow can narrow the type of a variable in certain contexts.
A runtime comparison with a static string literal does the trick:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Hacker News does provide endpoints for fetching recent submissions of
a specific item type (e.g., the latest stories).
But to demonstrate the flexibility of the &lt;code class=&quot;css-0&quot;&gt;Item&lt;/code&gt; type,
let&amp;#x27;s write some code that fetches and displays the latest items of any type.
We will need to switch on the &lt;code class=&quot;css-0&quot;&gt;type&lt;/code&gt; property of each item to display it
properly:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow is able to infer which item type is given in each &lt;code class=&quot;css-0&quot;&gt;case&lt;/code&gt; body.
This is just like how type-narrowing worked in the &lt;code class=&quot;css-0&quot;&gt;if&lt;/code&gt; body in the &lt;code class=&quot;css-0&quot;&gt;getTitle&lt;/code&gt;
function.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow&amp;#x27;s checking has an added bonus:
if you have a &lt;code class=&quot;css-0&quot;&gt;case&lt;/code&gt; body with no &lt;code class=&quot;css-0&quot;&gt;return&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;break&lt;/code&gt; statement,
execution falls through into the next &lt;code class=&quot;css-0&quot;&gt;case&lt;/code&gt; body.
When switching on &lt;code class=&quot;css-0&quot;&gt;item.type&lt;/code&gt;, a fall-through would result in a situation
where a &lt;code class=&quot;css-0&quot;&gt;case&lt;/code&gt; body might be executed with any of several different item types.
For example:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow allows this, because all of the types listed in that example have
a &lt;code class=&quot;css-0&quot;&gt;title&lt;/code&gt; property.
But if a &lt;code class=&quot;css-0&quot;&gt;case&lt;/code&gt; body did something not compatible with all of the different
item types that could fall-through into it, then Flow would report an error.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Next up are functions to determine which items to fetch, and to make the
necessary requests:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;And finally, some code to set everything running:&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;refining-the-model&quot; class=&quot;css-1bzbprl&quot;&gt;Refining the model&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Later on we may realize that it would be useful to be able to refer to each
item type individually.
To do that, we can create a named alias for each item type:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Then we can replace the earlier definition of &lt;code class=&quot;css-0&quot;&gt;Item&lt;/code&gt; with a simpler one:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This well let us write specialized functions,
such as a function that specifically formats a poll with its options.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;So how do we get to a point where we can call a function that accepts only
polls?
The answer is, once again, type-narrowing:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Notice the use of &lt;code class=&quot;css-0&quot;&gt;flatMap&lt;/code&gt; in &lt;code class=&quot;css-0&quot;&gt;fetchPollOpts&lt;/code&gt;.
This filters results to check that the results are actually poll options.
At the same time, Flow is able to infer that the filtered results all have the
&lt;code class=&quot;css-0&quot;&gt;PollOpt&lt;/code&gt; type.
This uses a custom definition for &lt;code class=&quot;css-0&quot;&gt;flatMap&lt;/code&gt;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you trust that all of the items that are fetched will be of the right type,
and you do not want to bother with a runtime check,
then you could use a type-cast instead:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Finally, here is a function that feeds fetched items to the new-and-improved
item formatting function:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The code from this article is available at
&lt;a href=&quot;https://github.com/hallettj/hacker-news-example&quot; class=&quot;css-1od09yo&quot;&gt;https://github.com/hallettj/hacker-news-example&lt;/a&gt;.
I encourage you to check out the code to tinker with it.
Try building more functionality,
and see how type-checking affects the way you write code.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Advanced features in Flow]]></title><link>https://sitr.us/2015/05/31/advanced-features-in-flow.html</link><guid isPermaLink="false">https://sitr.us/2015/05/31/advanced-features-in-flow.html</guid><pubDate>Sun, 31 May 2015 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow has some very interesting features that are currently not documented.
It is likely that the reason for missing documentation is that these features
are still experimental.
&lt;em class=&quot;css-0&quot;&gt;Caveat emptor&lt;/em&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I took a stroll through the source code for Flow v0.11.
Here is what I found while reading &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;https://github.com/facebook/flow/blob/master/src/typing/type_inference_js.ml#L612&quot; class=&quot;css-1od09yo&quot;&gt;type_inference_js.ml&lt;/a&gt;
and &lt;a href=&quot;https://github.com/facebook/flow/blob/master/lib/react.js&quot; class=&quot;css-1od09yo&quot;&gt;react.js&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;Edit&lt;/em&gt;: It has been pointed out to me that Flow features prefixed with &lt;code class=&quot;css-0&quot;&gt;$&lt;/code&gt; are not technically public,
and that the semantics of those features may change.
But they are useful enough that I plan to use some of them anyway :)&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;table-of-contents&quot; class=&quot;css-1bzbprl&quot;&gt;Table of Contents&lt;/h2&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;/ul&gt;&lt;h2 id=&quot;classt&quot; class=&quot;css-1bzbprl&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;Class&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Type of the class whose instances are of type &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt;.
This lets you pass around classes as first-class values -
with proper type checking.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I use this in an event dispatch system where events are class instances,
and event handlers are invoked based on whether they accept a given event type:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This gives me a compile-time guarantee that event handlers can handle events of
the type they are invoked with.&lt;/p&gt;&lt;h2 id=&quot;diffab&quot; class=&quot;css-1bzbprl&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;$Diff&amp;lt;A,B&amp;gt;&lt;/code&gt;&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If &lt;code class=&quot;css-0&quot;&gt;A&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;B&lt;/code&gt; are object types,
&lt;code class=&quot;css-0&quot;&gt;$Diff&amp;lt;A,B&amp;gt;&lt;/code&gt; is the type of objects that have properties defined in &lt;code class=&quot;css-0&quot;&gt;A&lt;/code&gt;, but not in &lt;code class=&quot;css-0&quot;&gt;B&lt;/code&gt;.
Properties that are defined in both &lt;code class=&quot;css-0&quot;&gt;A&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;B&lt;/code&gt; are allowed too.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;React uses this to throw type errors if components are not given required props,
but to leave props with default values as optional.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Where &lt;code class=&quot;css-0&quot;&gt;P&lt;/code&gt; is the type for component props,
and &lt;code class=&quot;css-0&quot;&gt;D&lt;/code&gt; is the type for default props.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here is a simplified example:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In my testing,
this worked equally well if &lt;code class=&quot;css-0&quot;&gt;$Diff&lt;/code&gt; was used directly in the type of &lt;code class=&quot;css-0&quot;&gt;P&lt;/code&gt;
instead of as a type bound.
But in the React type declarations,
it is used in a type bound.&lt;/p&gt;&lt;h2 id=&quot;shapet&quot; class=&quot;css-1bzbprl&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;$Shape&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Matches the shape of &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt;.
React uses &lt;code class=&quot;css-0&quot;&gt;$Shape&lt;/code&gt; in the signatures for &lt;code class=&quot;css-0&quot;&gt;setProps&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;setState&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Where &lt;code class=&quot;css-0&quot;&gt;P&lt;/code&gt; is the type of a component&amp;#x27;s props,
and &lt;code class=&quot;css-0&quot;&gt;S&lt;/code&gt; is the type of a component&amp;#x27;s state.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An object of type &lt;code class=&quot;css-0&quot;&gt;$Shape&amp;lt;T&amp;gt;&lt;/code&gt; does not have to have all of the properties that
type &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt; defines.
But the types of the properties that it does have must match the types of the
same properties in &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In React this means that you can use, e.g., &lt;code class=&quot;css-0&quot;&gt;setState&lt;/code&gt; to set some state
properties, while leaving others unspecified.
Note how the type of &lt;code class=&quot;css-0&quot;&gt;state&lt;/code&gt; in &lt;code class=&quot;css-0&quot;&gt;replaceState&lt;/code&gt; differs:
when calling &lt;code class=&quot;css-0&quot;&gt;replaceState&lt;/code&gt; you must include a value for every property in &lt;code class=&quot;css-0&quot;&gt;S&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Some examples:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An object of type &lt;code class=&quot;css-0&quot;&gt;$Shape&amp;lt;T&amp;gt;&lt;/code&gt; is not allowed to have properties that are not
part of &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;$Shape&lt;/code&gt; is like a supertype for objects.
Consider that a type &lt;code class=&quot;css-0&quot;&gt;{ foo: number }&lt;/code&gt; (when used as a type bound) represents
all objects that have a &lt;code class=&quot;css-0&quot;&gt;foo&lt;/code&gt; property of type &lt;code class=&quot;css-0&quot;&gt;number&lt;/code&gt;.
That includes objects that have additional properties, such as
&lt;code class=&quot;css-0&quot;&gt;{ foo: 1, bar: &amp;#x27;string&amp;#x27; }&lt;/code&gt;.
So &lt;code class=&quot;css-0&quot;&gt;{ foo: number }&lt;/code&gt; is a supertype of, e.g., &lt;code class=&quot;css-0&quot;&gt;{ foo: number, bar: string }&lt;/code&gt;.
The type &lt;code class=&quot;css-0&quot;&gt;$Shape&amp;lt;{ foo: number, bar: string }&amp;gt;&lt;/code&gt; allows values of type
&lt;code class=&quot;css-0&quot;&gt;{ foo: number }&lt;/code&gt;.
That is to say, &lt;code class=&quot;css-0&quot;&gt;$Shape&amp;lt;T&amp;gt;&lt;/code&gt; refers to types that are more general than &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt; -
i.e., supertypes.&lt;/p&gt;&lt;h2 id=&quot;recordt&quot; class=&quot;css-1bzbprl&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;$Record&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;Update:&lt;/em&gt; &lt;code class=&quot;css-0&quot;&gt;$Record&amp;lt;T&amp;gt;&lt;/code&gt; is &lt;a href=&quot;https://github.com/facebook/flow/commit/6d4447b0300494f8a235f4d0907bee438705ba19&quot; class=&quot;css-1od09yo&quot;&gt;gone in Flow v0.12&lt;/a&gt;.
But instead of &lt;code class=&quot;css-0&quot;&gt;$Record&amp;lt;T&amp;gt;&lt;/code&gt;,
you can use the nearly equivalent construct: &lt;code class=&quot;css-0&quot;&gt;{[key: $Enum&amp;lt;T&amp;gt;]: U}&lt;/code&gt;,
where &lt;code class=&quot;css-0&quot;&gt;U&lt;/code&gt; is the type of values in the record.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The type of objects whose keys are those of &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt;.
This means that an object of type &lt;code class=&quot;css-0&quot;&gt;$Record&amp;lt;T&amp;gt;&lt;/code&gt; must have properties with all of
the same names as the properties in &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt; -
but the types assigned to those properties may be different.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;React uses &lt;code class=&quot;css-0&quot;&gt;$Record&lt;/code&gt; to check the type of &lt;code class=&quot;css-0&quot;&gt;propTypes&lt;/code&gt;.
Here is a simplified example:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Note that Flow does &lt;em class=&quot;css-0&quot;&gt;not&lt;/em&gt; verify that &lt;code class=&quot;css-0&quot;&gt;React.PropTypes.number&lt;/code&gt; matches the type
&lt;code class=&quot;css-0&quot;&gt;number&lt;/code&gt;.
It just checks that &lt;code class=&quot;css-0&quot;&gt;propTypes1&lt;/code&gt; has all of the same keys.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A &lt;code class=&quot;css-0&quot;&gt;$Record&lt;/code&gt; type might have additional keys that are not included in the
original object type.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An important detail to note is that a &lt;code class=&quot;css-0&quot;&gt;$Record&lt;/code&gt; type can only use property
names that are known statically.
Dynamic lookups are not allowed.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In its real definition,
React actually uses &lt;code class=&quot;css-0&quot;&gt;$Record&lt;/code&gt; in combination with &lt;code class=&quot;css-0&quot;&gt;$Supertype&lt;/code&gt; so that you do
not get an error if you omit some properties from &lt;code class=&quot;css-0&quot;&gt;propTypes&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;supertypet&quot; class=&quot;css-1bzbprl&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;$Supertype&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A type that is a supertype of &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt;.
React uses &lt;code class=&quot;css-0&quot;&gt;$Supertype&lt;/code&gt; in the type of &lt;code class=&quot;css-0&quot;&gt;propTypes&lt;/code&gt;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Where the type variable &lt;code class=&quot;css-0&quot;&gt;P&lt;/code&gt; is the type of the props for a component.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It looks like the intent is to check that if a component defines &lt;code class=&quot;css-0&quot;&gt;propTypes&lt;/code&gt;,
all of the properties listed are also in the type of &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt;.
But it should not report an error if &lt;code class=&quot;css-0&quot;&gt;propTypes&lt;/code&gt; excludes some props.
I could not get a type error when testing this.
It could  be that interaction between &lt;code class=&quot;css-0&quot;&gt;$Supertype&lt;/code&gt; and object types is still
a work in progress.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you are thinking of using &lt;code class=&quot;css-0&quot;&gt;$Supertype&lt;/code&gt; with an object type,
consider &lt;code class=&quot;css-0&quot;&gt;$Shape&lt;/code&gt; -
it might be a better choice.&lt;/p&gt;&lt;h2 id=&quot;subtypet&quot; class=&quot;css-1bzbprl&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;$Subtype&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A type that is a subtype of &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt;.
This is what you get when you use a type bound.
For example,
these signatures are equivalent:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But the second version has the disadvantage that you cannot refer to the type
that &lt;code class=&quot;css-0&quot;&gt;obj&lt;/code&gt; gets in the return type of the function, or in the types of other
arguments.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;While trying to come up with example cases for &lt;code class=&quot;css-0&quot;&gt;$Subtype&lt;/code&gt;,
I came across other nice improvements to Flow that render &lt;code class=&quot;css-0&quot;&gt;$Subtype&lt;/code&gt;
unnecessary in a lot of cases.
In a previous version of Flow,
I recall (possibly incorrectly) having to use a type bound in a function that
takes on object with
certain required properties,
where you don&amp;#x27;t want to prevent the caller from including additional properties.
But now this works without a type bound:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I also had a thought that &lt;code class=&quot;css-0&quot;&gt;$Subtype&lt;/code&gt; could be used to implement a composition
pattern that previously did not work.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But in Flow v0.11 this does work.
Hooray!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;$Subtype&lt;/code&gt; could be useful if you want to define an object type that can be
assigned properties that are not declared in the type:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;However this weakens property checking on the extensible type.
For example,
Flow does not infer that the type of &lt;code class=&quot;css-0&quot;&gt;a.foo&lt;/code&gt; must be &lt;code class=&quot;css-0&quot;&gt;number&lt;/code&gt;.
(It checks the type of &lt;code class=&quot;css-0&quot;&gt;foo&lt;/code&gt; correctly when the object is first created,
but not on reads or reassignment).&lt;/p&gt;&lt;h2 id=&quot;enumt&quot; class=&quot;css-1bzbprl&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;$Enum&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The set of keys of &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt;.
One use for this is to write a lookup function,
and have Flow check that the lookup key is valid.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The type &lt;code class=&quot;css-0&quot;&gt;$Enum&amp;lt;typeof props&amp;gt;&lt;/code&gt; is a lot like this type:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But with &lt;code class=&quot;css-0&quot;&gt;$Enum&lt;/code&gt;,
Flow computes the type union for you.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow&amp;#x27;s tests include &lt;a href=&quot;https://github.com/facebook/flow/blob/master/tests/enumerror/enumerror.js&quot; class=&quot;css-1od09yo&quot;&gt;more&lt;/a&gt; &lt;a href=&quot;https://github.com/facebook/flow/blob/master/tests/literal/enum_client.js&quot; class=&quot;css-1od09yo&quot;&gt;examples&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;existential-types&quot; class=&quot;css-1bzbprl&quot;&gt;Existential types&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow supports an &amp;quot;existential type&amp;quot;: &lt;code class=&quot;css-0&quot;&gt;*&lt;/code&gt;.
When &lt;code class=&quot;css-0&quot;&gt;*&lt;/code&gt; is given as a type,
it acts as a placeholder,
leaving it to the type checker to infer the type for that position.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Let&amp;#x27;s generalize the &lt;code class=&quot;css-0&quot;&gt;getProp&lt;/code&gt; function from the section above.
Let&amp;#x27;s write a function that takes any object and returns a getter.
The getter can be used to get arbitrary properties out of the object,
without having to refer to the object itself after creating the getter.
We would like to write this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The use of &lt;code class=&quot;css-0&quot;&gt;$Enum&amp;lt;T&amp;gt;&lt;/code&gt; ensures that a getter can only be called to get
properties that actually exist on the given object.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But Flow objects to the lack of type declarations in the inner function.
It reports an error:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An obvious idea is to copy types from the return type of &lt;code class=&quot;css-0&quot;&gt;makeGetter&lt;/code&gt; into the
signature of the inner function.
But that leads to another problem.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This time the error is:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The problem is that type variables in a function signature
(in this case, in the signature of &lt;code class=&quot;css-0&quot;&gt;makeGetter&lt;/code&gt;)
are not in scope in the function body.
So we cannot refer to the type &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt; in inner function types,
or in variable types in the body of &lt;code class=&quot;css-0&quot;&gt;makeGetter&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;On the other hand, Flow might be smart enough to figure out what the argument
type in the inner function should be.
So we can use &lt;code class=&quot;css-0&quot;&gt;*&lt;/code&gt; to kick the problem over to Flow.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Now we can see &lt;code class=&quot;css-0&quot;&gt;makeGetter&lt;/code&gt; in action.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Another option would have been to use &lt;code class=&quot;css-0&quot;&gt;any&lt;/code&gt; instead of &lt;code class=&quot;css-0&quot;&gt;*&lt;/code&gt;.
But that is so much less elegant!
The important difference is that where &lt;code class=&quot;css-0&quot;&gt;*&lt;/code&gt; appears,
Flow will fill in a specific type,
which could lead to accurate type checking in other areas of the code where the
value of type &lt;code class=&quot;css-0&quot;&gt;*&lt;/code&gt; appears.
If you annotate a value with &lt;code class=&quot;css-0&quot;&gt;any&lt;/code&gt;,
Flow will not attempt to type-check expressions where that value appears -
which could lead to type errors being missed.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In this case the choice of &lt;code class=&quot;css-0&quot;&gt;*&lt;/code&gt; versus &lt;code class=&quot;css-0&quot;&gt;any&lt;/code&gt; does not matter,
since the outer function has the type signature that we want.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;React uses an existential type to define the &lt;code class=&quot;css-0&quot;&gt;ReactClass&lt;/code&gt; type:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This allows React to pass around a polymorphic class as a first-class value
without losing type information.&lt;/p&gt;&lt;h2 id=&quot;scoped-type-variables-in-classes&quot; class=&quot;css-1bzbprl&quot;&gt;Scoped type variables in classes&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I mentioned in the last section that type variables in a function&amp;#x27;s signature
are not in scope in the body of that function.
I find it interesting that classes do not have this restriction:
type variables in a class declaration &lt;em class=&quot;css-0&quot;&gt;are&lt;/em&gt; in scope in the class definition.
(They are not in scope within method bodies; but you can use these variables in method signatures and class variable types).
Because of this,
there are some problems that do not exactly work with functions but that do
work with classes.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We can reimplement &lt;code class=&quot;css-0&quot;&gt;makeGetter&lt;/code&gt; from the last section as a class.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This probably seems unremarkable -
every object-oriented language with static type-checking scopes class-level
type variables this way.
But ES6 classes are mostly syntactic sugar for ES5 constructor functions -
yet a straight translation of &lt;code class=&quot;css-0&quot;&gt;Getter&lt;/code&gt; to ES5 syntax does not work:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We get the same error: &lt;code class=&quot;css-0&quot;&gt;identifier T, could not resolve name&lt;/code&gt;.
This time to fix the problem we have to refer to &lt;code class=&quot;css-0&quot;&gt;T&lt;/code&gt; indirectly using &lt;code class=&quot;css-0&quot;&gt;typeof obj&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The ability of classes to scope type variables over inner methods,
and the ability to use &lt;code class=&quot;css-0&quot;&gt;instanceof&lt;/code&gt; for type refinement,
lead me to use classes more often than I would if I were not using Flow.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Type checking React with Flow v0.11]]></title><link>https://sitr.us/2015/05/31/type-checking-react-with-flow.html</link><guid isPermaLink="false">https://sitr.us/2015/05/31/type-checking-react-with-flow.html</guid><pubDate>Sun, 31 May 2015 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;This is an old post - for an up-to-date guide see &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;/2017/01/03/flow-cookbook-react.html&quot; class=&quot;css-1od09yo&quot;&gt;Flow Cookbook: Flow &amp;amp; React&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow v0.11 was &lt;a href=&quot;https://github.com/facebook/flow/blob/master/Changelog.md&quot; class=&quot;css-1od09yo&quot;&gt;released&lt;/a&gt; recently.
The latest set of changes really improve type checking in React apps.
But there are some guidelines to follow to get the full benefits.&lt;/p&gt;&lt;style data-emotion-css=&quot;1u9n620&quot;&gt;.css-1u9n620{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.5rem;margin-top:1rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1u9n620{font-size:1.875rem;}}@media screen and (min-width:768px){.css-1u9n620{font-size:2.25rem;}}&lt;/style&gt;&lt;h3 id=&quot;use-es6-classes&quot; class=&quot;css-1u9n620&quot;&gt;Use ES6 classes&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;React added support in version 0.13 for implementing components as native Javascript classes
(&lt;a href=&quot;https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html&quot; class=&quot;css-1od09yo&quot;&gt;more information on that here&lt;/a&gt;).
The latest version of the React type definitions take full advantage of class-based type checking features.&lt;/p&gt;&lt;h3 id=&quot;reactcomponent-takes-type-parameters&quot; class=&quot;css-1u9n620&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;React.Component&lt;/code&gt; takes type parameters&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When creating a component, be sure to provide type parameters in your class
declaration to describe the types of your props, default props, and state.
Here is a modified example from the React blog:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The parameter signature is &lt;code class=&quot;css-0&quot;&gt;React.Component&amp;lt;DefaultProps,Props,State&amp;gt;&lt;/code&gt;.
This is not exactly documented;
but you can see types of React features in
&lt;a href=&quot;https://github.com/facebook/flow/blob/master/lib/react.js&quot; class=&quot;css-1od09yo&quot;&gt;Flow&amp;#x27;s type declarations for React&lt;/a&gt;.
All of the type declarations in that folder are automatically loaded whenever Flow runs,
unless you use the &lt;code class=&quot;css-0&quot;&gt;--no-flowlib&lt;/code&gt; option.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you define a constructor for your component,
it is a good idea to annotate the &lt;code class=&quot;css-0&quot;&gt;props&lt;/code&gt; argument too.
Unfortunately Flow does not make the connection that the constructor argument
has the same type as &lt;code class=&quot;css-0&quot;&gt;this.props&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Note that I included type annotations on &lt;code class=&quot;css-0&quot;&gt;render()&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;context&lt;/code&gt;.
This is just because Flow generally requires type annotations for class method
arguments and return values.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When those type parameters are given,
here are some of the things that Flow can check:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;when instantiating your component, the required props are given with correctly typed values.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;props that are not required have default values (checked only if &lt;code class=&quot;css-0&quot;&gt;defaultProps&lt;/code&gt; is defined)&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;references to &lt;code class=&quot;css-0&quot;&gt;this.props&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;this.state&lt;/code&gt; are checked to make sure that the properties accessed exist, and have a compatible type&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;properties set with &lt;code class=&quot;css-0&quot;&gt;this.setState()&lt;/code&gt; are declared in your state type and have the correct types&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;use-jsx&quot; class=&quot;css-1u9n620&quot;&gt;Use JSX&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I mentioned above that Flow will check that components are given required props.
In my testing, there were some cases where this worked when I used JSX syntax,
but did not work with the plain Javascript &lt;code class=&quot;css-0&quot;&gt;React.createElement&lt;/code&gt; option.
(The case I had trouble with was with a conditionally-rendered child in
a &lt;code class=&quot;css-0&quot;&gt;render&lt;/code&gt; method -
my uses of of &lt;code class=&quot;css-0&quot;&gt;React.createElement&lt;/code&gt; worked fine with both syntaxes.)
I suspect that engineers at Facebook tend to prefer JSX,
and, and maybe test code written with JSX syntax more heavily.&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;general-purpose-features&quot; class=&quot;css-1bzbprl&quot;&gt;General-purpose features&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;What is nice is that most of the features that Flow uses to support React are general-purpose.
As far as I can tell, the only feature in Flow that is React-specific is support for JSX syntax.
But some of the features that make Flow work so well are not yet documented.
For details,
see my post on &lt;a href=&quot;/2015/05/31/advanced-features-in-flow.html&quot; class=&quot;css-1od09yo&quot;&gt;Advanced features in Flow&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Flow is the JavaScript type checker I have been waiting for]]></title><link>https://sitr.us/2014/11/21/flow-is-the-javascript-type-checker-i-have-been-waiting-for.html</link><guid isPermaLink="false">https://sitr.us/2014/11/21/flow-is-the-javascript-type-checker-i-have-been-waiting-for.html</guid><pubDate>Fri, 21 Nov 2014 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I am very excited about &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://flowtype.org/&quot; class=&quot;css-1od09yo&quot;&gt;Flow&lt;/a&gt;, a new JavaScript type checker from Facebook.
I have put some thought into what a type checker for JavaScript should do -
and in my opinion Facebook gets it right.
The designers of Flow took great effort to make it work well with JavaScript
idioms,
and with off-the-shelf JavaScript code.
The key features that make that possible are type inference
and &lt;a href=&quot;http://flowtype.org/docs/nullable-types.html#type-annotating-null&quot; class=&quot;css-1od09yo&quot;&gt;path-sensitive&lt;/a&gt; &lt;a href=&quot;http://flowtype.org/docs/dynamic-type-tests.html#_&quot; class=&quot;css-1od09yo&quot;&gt;analysis&lt;/a&gt;.
I think that Flow has the potential to enable sweeping improvements to the code
quality of countless web apps and Node apps.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;From the &lt;a href=&quot;https://code.facebook.com/posts/1505962329687926/flow-a-new-static-type-checker-for-javascript/&quot; class=&quot;css-1od09yo&quot;&gt;announcement post&lt;/a&gt;:&lt;/p&gt;&lt;style data-emotion-css=&quot;1gtwvjp&quot;&gt;.css-1gtwvjp{border-left-color:var(--theme-ui-colors-primary,#6b46c1);border-left-style:solid;border-left-width:6px;margin-left:0;margin-right:0;padding-left:2rem;}.css-1gtwvjp p{font-style:italic;}&lt;/style&gt;&lt;blockquote class=&quot;css-1gtwvjp&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;...underlying the design of Flow is the assumption that most JavaScript code
is implicitly statically typed; even though types may not appear anywhere in
the code, they are in the developer’s mind as a way to reason about the
correctness of the code. Flow infers those types automatically wherever
possible, which means that it can find type errors without needing any changes
to the code at all.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote class=&quot;css-1gtwvjp&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This makes Flow fundamentally different than existing JavaScript type systems
(such as TypeScript), which make the weaker assumption that most JavaScript
code is dynamically typed, and that it is up to the developer to express which
code may be amenable to static typing.&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Path-sensitive analysis means that Flow reads control flow to narrow types where
appropriate.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This example comes from the announcement post:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The presence of a use of &lt;code class=&quot;css-0&quot;&gt;length&lt;/code&gt; with a &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt; argument informs Flow that there
should be a null check in that function.
This version does type-check:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Flow is able to infer that &lt;code class=&quot;css-0&quot;&gt;x&lt;/code&gt; cannot be &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt; inside the &lt;code class=&quot;css-0&quot;&gt;if&lt;/code&gt; body.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An alternate fix would be to get rid of any invocations of &lt;code class=&quot;css-0&quot;&gt;length&lt;/code&gt; where the
argument might be &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt;.
That would cause Flow to infer a non-nullable type for &lt;code class=&quot;css-0&quot;&gt;x&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This capability goes further - here is an example from the Flow documentation:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The type of &lt;code class=&quot;css-0&quot;&gt;o&lt;/code&gt; is initially &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt;.
But Flow is able to determine that the type of &lt;code class=&quot;css-0&quot;&gt;o&lt;/code&gt; changes when &lt;code class=&quot;css-0&quot;&gt;o&lt;/code&gt; is
reassigned,
and that its type is definitely &lt;code class=&quot;css-0&quot;&gt;string&lt;/code&gt; on the last line.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In addition to null checks,
Flow also narrows types when it sees dynamic type checks.
This example (which passes the type checker) comes from the documentation:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The inferred return type of &lt;code class=&quot;css-0&quot;&gt;foo&lt;/code&gt; is &lt;code class=&quot;css-0&quot;&gt;string | number&lt;/code&gt;.
That is a type union,
meaning that values returned by &lt;code class=&quot;css-0&quot;&gt;foo&lt;/code&gt; might be of type &lt;code class=&quot;css-0&quot;&gt;string&lt;/code&gt; or of type
&lt;code class=&quot;css-0&quot;&gt;number&lt;/code&gt;.
The &lt;code class=&quot;css-0&quot;&gt;typeof&lt;/code&gt; checks in &lt;code class=&quot;css-0&quot;&gt;bar&lt;/code&gt; narrow the possible types of &lt;code class=&quot;css-0&quot;&gt;x&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;y&lt;/code&gt; in the
&lt;code class=&quot;css-0&quot;&gt;if&lt;/code&gt; body to just &lt;code class=&quot;css-0&quot;&gt;number&lt;/code&gt;.
That means that it is safe to multiply values returned by &lt;code class=&quot;css-0&quot;&gt;bar&lt;/code&gt; -
the type checker knows that &lt;code class=&quot;css-0&quot;&gt;bar&lt;/code&gt; will always return a number.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A coworker told me that what he would like is support for type-checked structs.
That would let him add or remove properties from an object in one part of
a program,
and be assured that the rest of the program is using the new object format
correctly.
Struct types work using structural types and type aliases:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Notice the &lt;code class=&quot;css-0&quot;&gt;type&lt;/code&gt; keyword and type annotation on &lt;code class=&quot;css-0&quot;&gt;mkPoint&lt;/code&gt; -
Facebook&amp;#x27;s literature is a little misleading,
in that Flow is really a new language that compiles to JavaScript.
But the only differences between Flow and regular JavaScript are the added
syntax for type aliases and type annotations,
and the type-checking step.
Flow can be applied to regular JavaScript code without type annotations.  &lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;How well that works will depend on how that code is written.
If the JavaScript is cleanly written,
you might get a lot of help from Flow without ever needing type annotations.
But there are some valid JavaScript idioms that will not type-check
(at least not at this time.)
For example, optional arguments are not allowed unless you use either
&lt;a href=&quot;http://flowtype.org/docs/functions.html#too-few-arguments&quot; class=&quot;css-1od09yo&quot;&gt;Flow&amp;#x27;s syntax&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters&quot; class=&quot;css-1od09yo&quot;&gt;ECMAScript 6&amp;#x27;s default parameter syntax&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;These are early days for Flow.
I am optimistic that over time it will only get better at operating on code that
was not written with Flow in mind.&lt;/p&gt;&lt;del&gt;There is a side benefit to using the Flow language: it supports [ECMAScript 6][], but [compiling Flow programs][] produces ECMAScript 5 code that can run in most browsers.&lt;/del&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;Edit 2016-08-19:&lt;/em&gt; Early in Flow&amp;#x27;s development, Facebook recommended using
their own jsx compiler to process code that used Flow.
That is what produced ECMAScript 5 code, as mentioned in the previous paragraph.
Currently the best practice is to use Babel with the &lt;a href=&quot;https://babeljs.io/docs/plugins/preset-react/&quot; class=&quot;css-1od09yo&quot;&gt;React preset&lt;/a&gt; to
process Flow code.
You still get the benefit of transpiling ECMAScript 6 code to ECMAScript 5,
if you have Babel configured to do that.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;One of my favorite features of Flow is that &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt; are &lt;em class=&quot;css-0&quot;&gt;not&lt;/em&gt;
treated as bottom types.
A value is only allowed to be &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt; if it has a nullable type.
This makes Flow better at catching null pointer exceptions than almost any other
object-oriented language.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For example:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A type expression &lt;code class=&quot;css-0&quot;&gt;?T&lt;/code&gt; behaves pretty much like &lt;code class=&quot;css-0&quot;&gt;T | null | undefined&lt;/code&gt;.
(From what I can tell, &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt; are distinct types in Flow -
but it does not seem to be possible to use them in type annotations at this
time.
&lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; is allowed, which seems to be an alias for &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;.)&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In the past I have talked to one or two people who said that they would only be
interested in type-checked JavaScript if it supported &lt;a href=&quot;https://en.wikipedia.org/wiki/Algebraic_data_type&quot; class=&quot;css-1od09yo&quot;&gt;algebraic types&lt;/a&gt;.
(These are the kind of people I work with :))
That is possible too - here is an example that I wrote:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Thanks to type narrowing, accessing &lt;code class=&quot;css-0&quot;&gt;tree.value&lt;/code&gt; passes type-checking in the
&lt;code class=&quot;css-0&quot;&gt;if&lt;/code&gt; body where &lt;code class=&quot;css-0&quot;&gt;tree instanceof Node&lt;/code&gt; is true.
Without that check, the type checker would not allow accessing properties that
do not exist on both &lt;code class=&quot;css-0&quot;&gt;Node&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;EmptyTree&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are some details in this example that I want to call out:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;class&lt;/code&gt; syntax is from ECMAScript 6 -
but it is extended in Flow to support type annotations for properties.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;Tree&lt;/code&gt; is a type alias.  It has no runtime representation.
None is needed, since &lt;code class=&quot;css-0&quot;&gt;Node&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;EmptyTree&lt;/code&gt; have no shared behavior.
And unlike a superclass, &lt;code class=&quot;css-0&quot;&gt;Tree&lt;/code&gt; is &lt;em class=&quot;css-0&quot;&gt;sealed&lt;/em&gt;, meaning that it is not possible to
add more subtypes to &lt;code class=&quot;css-0&quot;&gt;Tree&lt;/code&gt; elsewhere in the codebase.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Flow supports parameterized types
(a.k.a. generics, a.k.a. parametric polymorphism).&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;You can specify the exact type of function values (in this case, the type of &lt;code class=&quot;css-0&quot;&gt;predicate&lt;/code&gt;).&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;find&lt;/code&gt; might return a value from a tree, or it might return &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;.
So the return type is &lt;code class=&quot;css-0&quot;&gt;T | void&lt;/code&gt; (where &lt;code class=&quot;css-0&quot;&gt;void&lt;/code&gt; is the type of &lt;code class=&quot;css-0&quot;&gt;undefined&lt;/code&gt;).
Writing &lt;code class=&quot;css-0&quot;&gt;?T&lt;/code&gt; would also work.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have not been sold on prior JavaScript type checkers.
They did not seem to &amp;quot;get&amp;quot; JavaScript.
Flow is a type checker that I actually plan to use.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Javascript generators and functional reactive programming]]></title><link>https://sitr.us/2014/08/02/javascript-generators-and-functional-reactive-programming.html</link><guid isPermaLink="false">https://sitr.us/2014/08/02/javascript-generators-and-functional-reactive-programming.html</guid><pubDate>Sat, 02 Aug 2014 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;tl;dr: ECMAScript 6 introduces a language feature called &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://tobyho.com/2013/06/16/what-are-generators/&quot; class=&quot;css-1od09yo&quot;&gt;generators&lt;/a&gt;, which
are really great for working with asynchronous code that uses promises.  But
they do not work well for &lt;a href=&quot;/2013/05/22/functional-reactive-programming-in-javascript.html&quot; class=&quot;css-1od09yo&quot;&gt;functional reactive programming&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;ES6 generators allow asynchronous code to be written in a way that looks
synchronous.  This example uses a hypothetical library called Do (implementation
below) that makes &lt;a href=&quot;/2012/07/31/promise-pipelines-in-javascript.html&quot; class=&quot;css-1od09yo&quot;&gt;promises&lt;/a&gt; work with generators:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It is possible to use generators now in Node.js version 0.11 by using the
&lt;code class=&quot;css-0&quot;&gt;--harmony&lt;/code&gt; flag.  Or you can use &lt;a href=&quot;https://github.com/google/traceur-compiler&quot; class=&quot;css-1od09yo&quot;&gt;Traceur&lt;/a&gt; to transpile ES6 code with
generators into code that can be run in any ES5-compatible web browser.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I recently saw an informative talk from &lt;a href=&quot;http://jbr.me/&quot; class=&quot;css-1od09yo&quot;&gt;Jacob Rothstein&lt;/a&gt; on &lt;a href=&quot;https://github.com/visionmedia/co&quot; class=&quot;css-1od09yo&quot;&gt;Co&lt;/a&gt; and
&lt;a href=&quot;https://github.com/koajs/koa&quot; class=&quot;css-1od09yo&quot;&gt;Koa&lt;/a&gt;.  Those are libraries that make full use of generators to make writing
asynchronous code pleasant.  The implementation of the above example in Co is
almost identical.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Co operates on promises and thunks, allowing them to be expanded with
the &lt;code class=&quot;css-0&quot;&gt;yield&lt;/code&gt; keyword.  It has nice options for pulling nested
asynchronous values out of arrays and objects, running asynchronous
operations in parallel, and so forth.  One can even use &lt;code class=&quot;css-0&quot;&gt;try&lt;/code&gt; / &lt;code class=&quot;css-0&quot;&gt;catch&lt;/code&gt;
to catch errors thrown in asynchronous code!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Co is specialized for asynchronous code.  But when I look at it I see something
that is really close to being a monad comprehension - very much like the &lt;a href=&quot;http://learnyouahaskell.com/a-fistful-of-monads#do-notation&quot; class=&quot;css-1od09yo&quot;&gt;do
notation&lt;/a&gt; feature in Haskell.  With just a little tweaking, the use of
generators that Co has pioneered can almost be generalized to work with any
kind of monad.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For example, libraries like &lt;a href=&quot;http://reactive-extensions.github.io/RxJS/&quot; class=&quot;css-1od09yo&quot;&gt;RxJs&lt;/a&gt; and &lt;a href=&quot;https://github.com/baconjs/bacon.js/&quot; class=&quot;css-1od09yo&quot;&gt;Bacon.js&lt;/a&gt; implement event streams,
which are a lot like promises, except that callbacks on event streams can run
more than once.  This example uses Bacon to manage a typeahead search feature in
a web interface:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The idea is that when the user enters more than two characters of text into
a search box, a background request is dispatched and search results appear on
the page automatically.  This example requires a library that is a little more
general than Co, that is able to operate on Bacon event streams in addition to
promises and thunks.  Here is a basic implementation of that library:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This is the function that makes the example at the top of this post work.  It is
a simplified version of what Co does - the difference being that Do delegates to
a &lt;code class=&quot;css-0&quot;&gt;flatMap&lt;/code&gt; function to handle yielding.  We just need an implementation of
&lt;code class=&quot;css-0&quot;&gt;flatMap&lt;/code&gt; that can operate on some different monad types.  The one above works
with promises or with Bacon event streams.  An implementation that is easier to
extend would be nice; but I will leave that problem for another time.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Unfortunately, the Bacon example does not work.  Streams - unlike promises - get
many values.  That means that the code in each generator has to run many times
(once for each stream value).  But ES6 generators are not reentrant: after
resuming a generator from the point of a given &lt;code class=&quot;css-0&quot;&gt;yield&lt;/code&gt; expression, it is not
possible to jump back to that entry point again (assuming the generator does not
contain a loop).  With the Bacon example, after the first &lt;code class=&quot;css-0&quot;&gt;keyup&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;change&lt;/code&gt;
event the search result list will just stop updating.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Getting synchronous-style functional reactive programming to work well would
require immutable, reentrant generators.  ES6 generators are stateful: every
invocation of a generator changes the way that it will behave on the next
invocation.  In other words, a generated is mutated on every invocation:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An immutable implementation would return a new object with a function for the
next generator entry point, instead of mutating the original generator:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An immutable generator could be implemented with some simple syntactic
transforms.  The basic case:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;would transform to:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;next&lt;/code&gt; property is returned is a generator that is used to invoke the next
step.  In this view generators are just functions.  Not only that - a generator
is a closure that has access to the results of previous steps via closure scope.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There would just need to be a few cases to handle appearances of &lt;code class=&quot;css-0&quot;&gt;yield&lt;/code&gt; in
a &lt;code class=&quot;css-0&quot;&gt;return&lt;/code&gt; statement, a &lt;code class=&quot;css-0&quot;&gt;try&lt;/code&gt;-&lt;code class=&quot;css-0&quot;&gt;catch&lt;/code&gt; block, or as its own statement.  With that
kind of stateless design, the Bacon example would work fine.  But as far as
I know, there is no plan for stateless, reentrant generators in ECMAScript.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It is possible to make functional reactive programming work with non-reentrant
generators by using loops in the generators.  This approach is not as general or
as composable.  Asynchronous pieces would have to be declared specially at the
top of the function, for example.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Notice that &lt;code class=&quot;css-0&quot;&gt;yield&lt;/code&gt; is overloaded to accept asynchronous values and to return
results - which requires some awkward logic to inspect generator values.
I do not know how to implement &lt;code class=&quot;css-0&quot;&gt;resultView&lt;/code&gt; as a loop, since it requires
combining two event streams: search queries and JSON responses.  I do not see
any advantage of loops in generators over asynchronous-style callbacks.  But
maybe someone more imaginative than me can come up with a more elegant solution.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Kinesis Advantage with DSA keycaps]]></title><link>https://sitr.us/2014/05/19/kinesis-advantage-with-dsa-keycaps.html</link><guid isPermaLink="false">https://sitr.us/2014/05/19/kinesis-advantage-with-dsa-keycaps.html</guid><pubDate>Mon, 19 May 2014 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIEAf/EABYBAQEBAAAAAAAAAAAAAAAAAAIAA//aAAwDAQACEAMQAAABmba85iQD/8QAGxAAAgMAAwAAAAAAAAAAAAAAAQIAAxMEERT/2gAIAQEAAQUCyYHLuJQSvLJ9SU156us//8QAFhEBAQEAAAAAAAAAAAAAAAAAAAES/9oACAEDAQE/AZGH/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAERIv/aAAgBAgEBPwGoZf/EAB0QAAICAQUAAAAAAAAAAAAAAAABESECEiIxYXH/2gAIAQEABj8Ch16cm3OhqaJ02Ri4XR//xAAcEAACAgIDAAAAAAAAAAAAAAABEQAhUWFxkbH/2gAIAQEAAT8hxryUAI+8ahnlL1UHksLbFx86MAE//9oADAMBAAIAAwAAABA/D//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QB//EABcRAQEBAQAAAAAAAAAAAAAAAAEAEVH/2gAIAQIBAT8QSt4b/8QAGxABAQADAQEBAAAAAAAAAAAAAREAITFRkaH/2gAIAQEAAT8QSowFTReQ7ksgFu8w5ZJpafOYyI4xmNZfZvwsxkKOinwM/9k=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;My Kinesis Advantage keyboard with blank DSA keycaps&quot; title=&quot;My Kinesis Advantage keyboard with blank DSA keycaps&quot; src=&quot;/static/cc39242ab7547a51e953e2f0c3a81b71/18e3b/kinesis-advantage-with-blank-dsa-keycaps.jpg&quot; srcSet=&quot;/static/cc39242ab7547a51e953e2f0c3a81b71/46946/kinesis-advantage-with-blank-dsa-keycaps.jpg 240w,/static/cc39242ab7547a51e953e2f0c3a81b71/55489/kinesis-advantage-with-blank-dsa-keycaps.jpg 480w,/static/cc39242ab7547a51e953e2f0c3a81b71/18e3b/kinesis-advantage-with-blank-dsa-keycaps.jpg 960w,/static/cc39242ab7547a51e953e2f0c3a81b71/87945/kinesis-advantage-with-blank-dsa-keycaps.jpg 1024w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I now have a Kinesis Advantage keyboard for use at work.
I have been feeling some wrist strain recently;
and some of my coworkers were encouraging me to try one.
So I borrowed a Kinesis for a week, and found that I really liked it.
The contoured shape makes reaching for keys comfortable;
I find the column layout to be nicer than the usual staggered key arrangement;
and between the thumb-key clusters and the arrow keys,
there are a lot of options for mapping modifier keys that are in easy reach.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIDBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAACAf/aAAwDAQACEAMQAAABerqxiqOFf//EABkQAAMBAQEAAAAAAAAAAAAAAAECAwATEv/aAAgBAQABBQKlGFDagMT0mZpuSbyF3//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPwGhf//EABwQAAIBBQEAAAAAAAAAAAAAAAABMRESISIyQf/aAAgBAQAGPwJ24UHbE/SDlGqof//EABsQAQACAwEBAAAAAAAAAAAAAAEAETFRYSFB/9oACAEBAAE/IWFotk+zNw7LJdGGXeW5keMByf/aAAwDAQACAAMAAAAQzy//xAAXEQEBAQEAAAAAAAAAAAAAAAABABFB/9oACAEDAQE/EMeRoX//xAAYEQEBAAMAAAAAAAAAAAAAAAABABEhMf/aAAgBAgEBPxADpKZ1f//EABwQAQADAAIDAAAAAAAAAAAAAAEAESExQVFx0f/aAAgBAQABPxAtAAtpjnfesQ3WcAgCiLtK0flRgBpXXVle0eyGLA2nlP/Z&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;Kinesis Advantage, before modification&quot; title=&quot;Kinesis Advantage, before modification&quot; src=&quot;/static/19c4af5752cf43493aec09fc79c111e1/18e3b/IMG_20140515_175553491_HDR.jpg&quot; srcSet=&quot;/static/19c4af5752cf43493aec09fc79c111e1/46946/IMG_20140515_175553491_HDR.jpg 240w,/static/19c4af5752cf43493aec09fc79c111e1/55489/IMG_20140515_175553491_HDR.jpg 480w,/static/19c4af5752cf43493aec09fc79c111e1/18e3b/IMG_20140515_175553491_HDR.jpg 960w,/static/19c4af5752cf43493aec09fc79c111e1/5d68e/IMG_20140515_175553491_HDR.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;Kinesis Advantage, before modification&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But I really like the PBT keycaps on my &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://imgur.com/a/XZ2hG&quot; class=&quot;css-1od09yo&quot;&gt;Leopold&lt;/a&gt;.
I would not enjoy going back to plain, old ABS.
I also don&amp;#x27;t want my keyboard to be just like every other Kinesis.
So I decided to get replacement keycaps.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I did some research on buying PBT keycaps with the same profiles as the stock Kinesis keys.
I assumed that I would end up getting blank keycaps -
putting together a set with legends appropriate for a Kinesis seemed like it would be a painful undertaking,
since there don&amp;#x27;t seem to be any sets made specifically for the Kinesis.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Most keyboards - including the Kinesis Advantage - use what is called a DCS profile,
where the keys in each row have different heights and angles.
(That does not include laptop keyboards,
or island-style keyboards such as the ones that Apple sells.
Those are in their own categories.)&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:384px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:22.499999999999996%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAbYuQP/EABUQAQEAAAAAAAAAAAAAAAAAABAR/9oACAEBAAEFAq//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAZEAACAwEAAAAAAAAAAAAAAAAAAREhUWH/2gAIAQEAAT8hsp4S9Zes/9oADAMBAAIAAwAAABAH3//EABYRAQEBAAAAAAAAAAAAAAAAAAARAf/aAAgBAwEBPxCrr//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/EIj/xAAaEAEAAgMBAAAAAAAAAAAAAAABABEhMUFR/9oACAEBAAE/EBFqtsRK+F98gq+GNz//2Q==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;DCS family: medium profile, cylindrical top, sculptured. (Image from Signature Plastics.)&quot; title=&quot;DCS family: medium profile, cylindrical top, sculptured. (Image from Signature Plastics.)&quot; src=&quot;/static/2a54c20d629d9da0888da49838797732/adbc5/angle3.jpg&quot; srcSet=&quot;/static/2a54c20d629d9da0888da49838797732/46946/angle3.jpg 240w,/static/2a54c20d629d9da0888da49838797732/adbc5/angle3.jpg 384w&quot; sizes=&quot;(max-width: 384px) 100vw, 384px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;DCS family: medium profile, cylindrical top, sculptured. (Image from Signature Plastics.)&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Input Nirvana on Geekhack has a post with &lt;a href=&quot;http://geekhack.org/index.php?topic=29875.0&quot; class=&quot;css-1od09yo&quot;&gt;a list of all necessary keycap sizes and profiles&lt;/a&gt;
to reproduce the arrangement on a stock Kinesis.
It is possible to order these individually from Signature Plastics;
but their inventory for &lt;em class=&quot;css-0&quot;&gt;à la carte&lt;/em&gt; orders varies depending on what they have left over
from production of large batches.
When I checked, SP did not have any row 5 PBT keycaps available.
I got the impression that building a custom DCS set would be somewhat difficult.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Then I saw prdlm2009 on Deskthority suggest that &lt;a href=&quot;http://deskthority.net/photos-videos-f8/kinesis-advantage-with-spherical-keycaps-dsa-family-for-sp-t4842.html&quot; class=&quot;css-1od09yo&quot;&gt;DSA profile keycaps work well on a Kinesis&lt;/a&gt;.
DSA is a uniform profile -
every key has the same height and angle.
It makes everything much simpler when dealing with unusual keyboard layouts,
or unusual keyboards.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:384px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:22.499999999999996%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHbFzQP/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFhABAQEAAAAAAAAAAAAAAAAAEQAQ/9oACAEBAAE/IVxv/9oADAMBAAIAAwAAABAP/wD/xAAWEQADAAAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8QrKz/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPxCI/8QAGRABAAIDAAAAAAAAAAAAAAAAAQARIYGR/9oACAEBAAE/ELAVzU29gwCf/9k=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;DSA family medium profile, spherical top, non-sculptured. (Image from Signature Plastics.)&quot; title=&quot;DSA family medium profile, spherical top, non-sculptured. (Image from Signature Plastics.)&quot; src=&quot;/static/c17e40f3b934ce485f63c5442164419a/adbc5/angle2.jpg&quot; srcSet=&quot;/static/c17e40f3b934ce485f63c5442164419a/46946/angle2.jpg 240w,/static/c17e40f3b934ce485f63c5442164419a/adbc5/angle2.jpg 384w&quot; sizes=&quot;(max-width: 384px) 100vw, 384px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;DSA family medium profile, spherical top, non-sculptured. (Image from Signature Plastics.)&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;DSA also features spherical tops.
If you look at the keys on a typical keyboard, you can see that the top curves up on the left and right sides -
as though someone had shaped them around a cylinder.
The tops of DSA keys are spherical; as though shaped around a large marble.
So the keys cup the fingertips from all sides.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Signature Plastics sells a variety of nice, blank DSA keycap sets.
I did not order the optimal combination of keycap sets;
but now I have a better idea of what that combination is.
The key count on an Advantage is:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;56 – 1x keys (optionally including two homing keys)&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;8 – 1.25x keys&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;4 – 2x keys&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;1x, 2x, etc. refers to the lengths of the keys.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;One can get everything except the 1.25x keys with one &lt;a href=&quot;http://keyshop.pimpmykeyboard.com/products/full-keysets/dsa-blank-numpad-sets-1&quot; class=&quot;css-1od09yo&quot;&gt;ErgoDox Base set&lt;/a&gt;
and two &lt;a href=&quot;http://keyshop.pimpmykeyboard.com/products/full-keysets/dsa-blank-numpad-sets&quot; class=&quot;css-1od09yo&quot;&gt;Numpad sets&lt;/a&gt;.
The Numpad sets seem to be the cheapest way to get all 4 2x keycaps, along with additional 1x caps.
The only set that includes 1.25x caps is the Standard Modifier set,
which includes 7 of them.
(So close!)
I recommend ordering the 1.25x keycaps individually from the &lt;a href=&quot;http://www.keycapsdirect.com/key-capsinventory.php&quot; class=&quot;css-1od09yo&quot;&gt;blank keycap inventory&lt;/a&gt;.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAwABAv/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAFkXiU7JP/EABkQAAMAAwAAAAAAAAAAAAAAAAABAgMQEv/aAAgBAQABBQK86kT6ke//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwFX/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8BR//EABgQAQEAAwAAAAAAAAAAAAAAAAECACBR/9oACAEBAAY/AkJpTB7r/8QAGhAAAgIDAAAAAAAAAAAAAAAAAAERYRAxUf/aAAgBAQABPyGslRMhFjYxC5j/2gAMAwEAAgADAAAAEATP/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEh/9oACAEDAQE/EJin/8QAFhEAAwAAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EKmQf//EABwQAQEAAgMBAQAAAAAAAAAAAAERADEhQVGhwf/aAAgBAQABPxCnIxNHlLetfcMJIscTi/mV5CJ6d4yRbnZF9ma1n//Z&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;stock Kinesis keycaps (left), DSA keycaps (right)&quot; title=&quot;stock Kinesis keycaps (left), DSA keycaps (right)&quot; src=&quot;/static/ac2a152d3eec905cc01570aeff41e2aa/18e3b/IMG_20140515_180022543_HDR.jpg&quot; srcSet=&quot;/static/ac2a152d3eec905cc01570aeff41e2aa/46946/IMG_20140515_180022543_HDR.jpg 240w,/static/ac2a152d3eec905cc01570aeff41e2aa/55489/IMG_20140515_180022543_HDR.jpg 480w,/static/ac2a152d3eec905cc01570aeff41e2aa/18e3b/IMG_20140515_180022543_HDR.jpg 960w,/static/ac2a152d3eec905cc01570aeff41e2aa/5d68e/IMG_20140515_180022543_HDR.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;stock Kinesis keycaps (left), DSA keycaps (right)&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The keycaps from SP are much thicker than the stock keycaps.
And they are made from PBT plastic, which is denser than the more common keycap material, ABS.
What I like most about PBT caps is their texture.
The tops of the keys are usually slightly rough, somewhat pebbly.
It gives a little bit of grippiness,
and feels soothing on my fingers compared to featureless, flat plastic.
I also think that the sound of PBT keys being pressed is nicer.
It is slightly quieter, with a somewhat deeper tone.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEEAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAACA//aAAwDAQACEAMQAAABcnVnk9hE3//EABoQAQEBAAMBAAAAAAAAAAAAAAIBAwAREhP/2gAIAQEAAQUC0VmqVKwXvLQHv5C8Mgn/xAAWEQEBAQAAAAAAAAAAAAAAAAABAAL/2gAIAQMBAT8BMsX/xAAWEQEBAQAAAAAAAAAAAAAAAAABABL/2gAIAQIBAT8B0E3/xAAbEAACAwADAAAAAAAAAAAAAAAAAQIRMSEyQf/aAAgBAQAGPwKT8stMTemHUqPB/8QAGxAAAwACAwAAAAAAAAAAAAAAAAERIVExQYH/2gAIAQEAAT8hVSrvLy3ZaVyTKmzryPnV6RBB/9oADAMBAAIAAwAAABDb7//EABYRAQEBAAAAAAAAAAAAAAAAAAEAIf/aAAgBAwEBPxBBkENv/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAgEBPxBd0kLpf//EAB0QAQEAAgIDAQAAAAAAAAAAAAERACFRkTFB0WH/2gAIAQEAAT8QWkCGb6jOpgYQ78B4xWOyHkfky/qtsbe83xL9X3JGVsOc/9k=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;All of the stock keycaps have been removed, revealing Cherry MX Brown switches.&quot; title=&quot;All of the stock keycaps have been removed, revealing Cherry MX Brown switches.&quot; src=&quot;/static/8d9d8ced032e06fcc5250f81bd8c1b84/18e3b/IMG_20140515_182527431_HDR.jpg&quot; srcSet=&quot;/static/8d9d8ced032e06fcc5250f81bd8c1b84/46946/IMG_20140515_182527431_HDR.jpg 240w,/static/8d9d8ced032e06fcc5250f81bd8c1b84/55489/IMG_20140515_182527431_HDR.jpg 480w,/static/8d9d8ced032e06fcc5250f81bd8c1b84/18e3b/IMG_20140515_182527431_HDR.jpg 960w,/static/8d9d8ced032e06fcc5250f81bd8c1b84/5d68e/IMG_20140515_182527431_HDR.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;All of the stock keycaps have been removed, revealing Cherry MX Brown switches.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Kinesis Advantage comes with either &lt;a href=&quot;http://deskthority.net/wiki/Cherry_MX_Brown&quot; class=&quot;css-1od09yo&quot;&gt;Cherry MX Brown&lt;/a&gt; or &lt;a href=&quot;http://deskthority.net/wiki/Cherry_MX_Red&quot; class=&quot;css-1od09yo&quot;&gt;Cherry MX Red&lt;/a&gt; switches.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For anyone wondering how to remove keycaps from a keyboard with Cherry MX switches, here is a &lt;a href=&quot;https://www.youtube.com/watch?v=FlUYCAZNNOw&quot; class=&quot;css-1od09yo&quot;&gt;video&lt;/a&gt;.
What the video does not mention is that it is a good idea to wiggle the keycap puller while pulling up on the keycap.
That helps to avoid pulling with too much force, which could break a switch.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have another keyboard that also has Cherry MX Brown switches,
and I really liked the change in key feel after installing o-rings.
O-rings make typing a little quieter,
and add some springiness to the bottom of the key travel.
A tradeoff is that they reduce the length of key travel a bit.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAAB/8QAFQEBAQAAAAAAAAAAAAAAAAAAAgP/2gAMAwEAAhADEAAAAVjSea2yK//EABkQAAIDAQAAAAAAAAAAAAAAAAACARIhEf/aAAgBAQABBQJmrFm6Pouyf//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAEDAQE/AYf/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPwGn/8QAGBAAAgMAAAAAAAAAAAAAAAAAEBEAATH/2gAIAQEABj8CmM2x/8QAGRABAAMBAQAAAAAAAAAAAAAAAQARMSFR/9oACAEBAAE/IQoWmtjxqGxhpT2Jd4I6z//aAAwDAQACAAMAAAAQ5N//xAAWEQADAAAAAAAAAAAAAAAAAAABECH/2gAIAQMBAT8QFL//xAAXEQEAAwAAAAAAAAAAAAAAAAAAAREh/9oACAECAQE/EJwp/8QAHBABAAMAAgMAAAAAAAAAAAAAAQARIUFhgaHR/9oACAEBAAE/ENca1jCHwAhGr3PHyJuEA0OGOJaAdGs9qf/Z&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;installing o-rings in the new keycaps&quot; title=&quot;installing o-rings in the new keycaps&quot; src=&quot;/static/96b0abdd9954d14adbb67bf3d1963c04/18e3b/IMG_20140515_182759811.jpg&quot; srcSet=&quot;/static/96b0abdd9954d14adbb67bf3d1963c04/46946/IMG_20140515_182759811.jpg 240w,/static/96b0abdd9954d14adbb67bf3d1963c04/55489/IMG_20140515_182759811.jpg 480w,/static/96b0abdd9954d14adbb67bf3d1963c04/18e3b/IMG_20140515_182759811.jpg 960w,/static/96b0abdd9954d14adbb67bf3d1963c04/5d68e/IMG_20140515_182759811.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;installing o-rings in the new keycaps&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I used &lt;a href=&quot;http://www.wasdkeyboards.com/index.php/cherry-mx-rubber-o-ring-switch-dampeners-125pcs.html#ad-image-0&quot; class=&quot;css-1od09yo&quot;&gt;40A-R o-rings from WASD&lt;/a&gt;, which are relatively thick and soft.
But when I tried these out with the DSA keycaps I could not discern any difference
between a key with an o-ring and one without.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Comparing the underside of a DSA keycap to a typical DCS keycap reveals the issue:&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBP/EABYBAQEBAAAAAAAAAAAAAAAAAAACA//aAAwDAQACEAMQAAABaVVzrEKH/8QAGRAAAgMBAAAAAAAAAAAAAAAAAjEBEDJB/9oACAEBAAEFApXC06PX/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFhEAAwAAAAAAAAAAAAAAAAAAEBEh/9oACAECAQE/AYh//8QAFxAAAwEAAAAAAAAAAAAAAAAAABAhAf/aAAgBAQAGPwIyKv8A/8QAGhABAQEAAwEAAAAAAAAAAAAAAREAECFRMf/aAAgBAQABPyEBSU8xK13a5xaAnYz8nnB//9oADAMBAAIAAwAAABA0/wD/xAAXEQADAQAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/ENo7T//EABcRAAMBAAAAAAAAAAAAAAAAAAABEUH/2gAIAQIBAT8QWgpD/8QAGxABAAMAAwEAAAAAAAAAAAAAAQARMSFBUWH/2gAIAQEAAT8QKgsLtVwAQqCPDtG/I4CVYGlMz6JQ8m0//9k=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;blank DSA keycaps from SP (top), keycaps from a Leopold FC660M (bottom)&quot; title=&quot;blank DSA keycaps from SP (top), keycaps from a Leopold FC660M (bottom)&quot; src=&quot;/static/d011f18ecc565aa60aaac7c3ac879630/18e3b/IMG_20140515_180233205.jpg&quot; srcSet=&quot;/static/d011f18ecc565aa60aaac7c3ac879630/46946/IMG_20140515_180233205.jpg 240w,/static/d011f18ecc565aa60aaac7c3ac879630/55489/IMG_20140515_180233205.jpg 480w,/static/d011f18ecc565aa60aaac7c3ac879630/18e3b/IMG_20140515_180233205.jpg 960w,/static/d011f18ecc565aa60aaac7c3ac879630/5d68e/IMG_20140515_180233205.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;blank DSA keycaps from SP (top), keycaps from a Leopold FC660M (bottom)&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;DCS keycaps have cross-shaped supports under the cap,
which contact the top of the switch housing when the key is fully depressed.
O-rings sit between those supports and the switch housing,
absorbing some force from contact.
But the DSA keycaps lack those supports.
That means that the switch can reach the bottom of its travel
before the underside of the keycap contacts the switch housing.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I found that doubling up o-rings pushed the rubber high enough to be effective.
But I was concerned that two o-rings shorted key travel too much
and introduced too much squishiness.
In the end I left the o-rings out entirely.
I may take another shot using either thinner, firmer o-rings,
or with small washers in place of a second o-ring.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwT/xAAUAQEAAAAAAAAAAAAAAAAAAAAC/9oADAMBAAIQAxAAAAEazMFAgV//xAAaEAACAgMAAAAAAAAAAAAAAAAAARESITFB/9oACAEBAAEFAsxdQcez/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Bh//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/Aaf/xAAYEAACAwAAAAAAAAAAAAAAAAAAESAhQf/aAAgBAQAGPwKhbH//xAAaEAEAAgMBAAAAAAAAAAAAAAABABEQIWFB/9oACAEBAAE/IUFhfIdh5F3CACQ24//aAAwDAQACAAMAAAAQlO//xAAXEQADAQAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/EE1tMP/EABcRAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQIBAT8QaSQ0/8QAGhAAAgMBAQAAAAAAAAAAAAAAASEAETFBUf/aAAgBAQABPxAStQXTYECC5DaHYe0RPIEYEE1s3Z//2Q==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;two o-rings installed in one keycap&quot; title=&quot;two o-rings installed in one keycap&quot; src=&quot;/static/5024982bf696121969d433c723c6ea38/18e3b/IMG_20140515_183902311.jpg&quot; srcSet=&quot;/static/5024982bf696121969d433c723c6ea38/46946/IMG_20140515_183902311.jpg 240w,/static/5024982bf696121969d433c723c6ea38/55489/IMG_20140515_183902311.jpg 480w,/static/5024982bf696121969d433c723c6ea38/18e3b/IMG_20140515_183902311.jpg 960w,/static/5024982bf696121969d433c723c6ea38/5d68e/IMG_20140515_183902311.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;two o-rings installed in one keycap&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When I ordered my keycaps I got one ErgoDox Base set and one ErgoDox Modifier set.
I did not do enough checking - I assumed that the 1.5x keys in the Modifier set
would fit in the Kinesis.
But it turns out that the keys in the leftmost and rightmost columns of the Kinesis
take 1.25x keycaps.
The larger keycaps do not fit.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAAF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAECwPNmJYv/xAAcEAABAwUAAAAAAAAAAAAAAAABAAISAxEhMTL/2gAIAQEAAQUCbaQNIPkE3KHI1//EABURAQEAAAAAAAAAAAAAAAAAAAAS/9oACAEDAQE/AaU//8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8BiP/EABoQAAICAwAAAAAAAAAAAAAAAAABAhIgITH/2gAIAQEABj8CVnoVZS7gj//EABsQAQADAAMBAAAAAAAAAAAAAAEAESExQVFx/9oACAEBAAE/IU7IbteRLwZsxjdF2nFHfyn/2gAMAwEAAgADAAAAEEQv/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAR/9oACAEDAQE/EFBlu//EABcRAAMBAAAAAAAAAAAAAAAAAAABETH/2gAIAQIBAT8QSaSf/8QAGxABAAMBAAMAAAAAAAAAAAAAAQARITFBYYH/2gAIAQEAAT8QPKGLo8kIzkmZ/RdSw2IjvZkAKrkRS2ot+T//2Q==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;Whoops!  That was supposed to be a 1.25x key, not a 1.5x.&quot; title=&quot;Whoops!  That was supposed to be a 1.25x key, not a 1.5x.&quot; src=&quot;/static/74c5a30e91889384b5f7c2cfbb806416/18e3b/IMG_20140515_190504044_HDR.jpg&quot; srcSet=&quot;/static/74c5a30e91889384b5f7c2cfbb806416/46946/IMG_20140515_190504044_HDR.jpg 240w,/static/74c5a30e91889384b5f7c2cfbb806416/55489/IMG_20140515_190504044_HDR.jpg 480w,/static/74c5a30e91889384b5f7c2cfbb806416/18e3b/IMG_20140515_190504044_HDR.jpg 960w,/static/74c5a30e91889384b5f7c2cfbb806416/5d68e/IMG_20140515_190504044_HDR.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;Whoops!  That was supposed to be a 1.25x key, not a 1.5x.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have ordered some appropriately sized keycaps.
In the meantime, I am using 1x keycaps in the 1.25x positions.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQBAgP/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAABawamUKg//8QAGRAAAgMBAAAAAAAAAAAAAAAAAQIEEBES/9oACAEBAAEFAnfgCRrVtf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAEFAQAAAAAAAAAAAAAAAAEAEiAxQVH/2gAIAQEABj8CxANvkf/EABkQAQEBAQEBAAAAAAAAAAAAAAEAESFBUf/aAAgBAQABPyHsnWLj7c5v0phlW3//2gAMAwEAAgADAAAAEEgv/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QBn//xAAXEQEAAwAAAAAAAAAAAAAAAAABEBEx/9oACAECAQE/EFMqP//EABsQAQADAQEBAQAAAAAAAAAAAAEAESFBMVFh/9oACAEBAAE/EANsJ1HCNrob0L/K9Ja+xMcs8agQBq/kVbs//9k=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;stock 1.25x Tab key next to its intended, blank replacement&quot; title=&quot;stock 1.25x Tab key next to its intended, blank replacement&quot; src=&quot;/static/3c4ac7278d0c4f410d9ff2609813ca83/18e3b/IMG_20140515_190751151_HDR.jpg&quot; srcSet=&quot;/static/3c4ac7278d0c4f410d9ff2609813ca83/46946/IMG_20140515_190751151_HDR.jpg 240w,/static/3c4ac7278d0c4f410d9ff2609813ca83/55489/IMG_20140515_190751151_HDR.jpg 480w,/static/3c4ac7278d0c4f410d9ff2609813ca83/18e3b/IMG_20140515_190751151_HDR.jpg 960w,/static/3c4ac7278d0c4f410d9ff2609813ca83/5d68e/IMG_20140515_190751151_HDR.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;stock 1.25x Tab key next to its intended, blank replacement&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Even though the DSA keycaps are not the same shape as the stock caps,
they fit quite well on the Kinesis.
There are just two slightly problematic spots.
The photo below shows the one key that comes into contact with the edge of the keyboard case when it is not depressed.
Thankfully the operation of the key does not seem to be affected.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFBv/EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAABmsq6FJJTGf/EABoQAAMAAwEAAAAAAAAAAAAAAAECAwAEETP/2gAIAQEAAQUCgzBzr16IW5L0IGKBn//EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPwG1j//EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAgEBPwGNf//EAB4QAAIBAwUAAAAAAAAAAAAAAAABESFBUQIiMXGR/9oACAEBAAY/AtkS8j4dclvTT2MdLn//xAAZEAEAAwEBAAAAAAAAAAAAAAABABEhQaH/2gAIAQEAAT8hBnAxDpSdELFJjzZWz0mbTHP/2gAMAwEAAgADAAAAEKj/AP/EABYRAQEBAAAAAAAAAAAAAAAAAAARUf/aAAgBAwEBPxATj//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EBH/xAAcEAEBAAIDAQEAAAAAAAAAAAABEQAhMUFxYbH/2gAIAQEAAT8QHzTbQ24bUgINr77iIRNiee+s1+/6YSESM1OMtZQBSwz/2Q==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;The fit is tight in this corner.&quot; title=&quot;The fit is tight in this corner.&quot; src=&quot;/static/766011d122d8bb5cbc64c6803ca9e203/18e3b/IMG_20140515_190931944.jpg&quot; srcSet=&quot;/static/766011d122d8bb5cbc64c6803ca9e203/46946/IMG_20140515_190931944.jpg 240w,/static/766011d122d8bb5cbc64c6803ca9e203/55489/IMG_20140515_190931944.jpg 480w,/static/766011d122d8bb5cbc64c6803ca9e203/18e3b/IMG_20140515_190931944.jpg 960w,/static/766011d122d8bb5cbc64c6803ca9e203/5d68e/IMG_20140515_190931944.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;The fit is tight in this corner.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Due to small differences in switch positioning,
the key in the same position in the other well has a little bit of clearance.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The other problem is that two of my 2x keys overlap very slightly.
When I press the one on the right there is sometimes an extra &lt;em class=&quot;css-0&quot;&gt;click&lt;/em&gt;
as it pushes past its neighbor.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFAv/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAFRmNYTRPE//8QAGRAAAwEBAQAAAAAAAAAAAAAAAQIDABEh/9oACAEBAAEFAo8Z7gplr5rErIb/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAdEAABBAIDAAAAAAAAAAAAAAABABARMQISQUJR/9oACAEBAAY/AoOULYV4uGBHa2//xAAbEAEAAgMBAQAAAAAAAAAAAAABABEhYXFBUf/aAAgBAQABPyFgVx7KAdr5NxK2LpBTI5IotIpfYmp//9oADAMBAAIAAwAAABBn3//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EIj/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEBAQEBAQEAAAAAAAAAAAABEQBRQWGR/9oACAEBAAE/EDzKUiq4cdYOSlQeJ4+6AQDony50QJRPHPJF4qC39cYg7//Z&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;There is not quite enough space between these two keys.&quot; title=&quot;There is not quite enough space between these two keys.&quot; src=&quot;/static/83ef15d234210e5e5353fafa7c4b2145/18e3b/IMG_20140515_194515105.jpg&quot; srcSet=&quot;/static/83ef15d234210e5e5353fafa7c4b2145/46946/IMG_20140515_194515105.jpg 240w,/static/83ef15d234210e5e5353fafa7c4b2145/55489/IMG_20140515_194515105.jpg 480w,/static/83ef15d234210e5e5353fafa7c4b2145/18e3b/IMG_20140515_194515105.jpg 960w,/static/83ef15d234210e5e5353fafa7c4b2145/5d68e/IMG_20140515_194515105.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;There is not quite enough space between these two keys.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I am thinking of sanding down the corners of these keys a little bit to fix the problem.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This is another case where there is no problem with the keys in the same positions on the other side of the board.
It seems that the switches in the left thumb cluster just happen to be
a little too close together on my board.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEEAgP/xAAUAQEAAAAAAAAAAAAAAAAAAAAC/9oADAMBAAIQAxAAAAHpJZkJjCv/xAAZEAADAQEBAAAAAAAAAAAAAAAAAQIDERP/2gAIAQEAAQUCb6S+XlctecjygUSj/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEh/9oACAEDAQE/AY1//8QAFhEAAwAAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/Aahw/8QAHBAAAgICAwAAAAAAAAAAAAAAAAIBIRFBEjEy/9oACAEBAAY/Amw750ZZm47sm5m9nR5Kg//EABoQAQEBAQEBAQAAAAAAAAAAAAERACFhMYH/2gAIAQEAAT8hMpFcXmRIMsToMQ4fvcPXuWi1+4eR3//aAAwDAQACAAMAAAAQDO//xAAXEQADAQAAAAAAAAAAAAAAAAAAARFB/9oACAEDAQE/EKwUKH//xAAXEQEBAQEAAAAAAAAAAAAAAAABABEx/9oACAECAQE/EAOwTt//xAAdEAEAAgICAwAAAAAAAAAAAAABABEhQTHRUWFx/9oACAEBAAE/ELKAUVBfjGpcbNEQvDV5i06Cwood/YDgtnPuDiXtdylIOUF5n//Z&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;All done!&quot; title=&quot;All done!&quot; src=&quot;/static/9a134ee9add89b6fb738770cb2a45a4d/18e3b/IMG_20140515_194232464_HDR.jpg&quot; srcSet=&quot;/static/9a134ee9add89b6fb738770cb2a45a4d/46946/IMG_20140515_194232464_HDR.jpg 240w,/static/9a134ee9add89b6fb738770cb2a45a4d/55489/IMG_20140515_194232464_HDR.jpg 480w,/static/9a134ee9add89b6fb738770cb2a45a4d/18e3b/IMG_20140515_194232464_HDR.jpg 960w,/static/9a134ee9add89b6fb738770cb2a45a4d/5d68e/IMG_20140515_194232464_HDR.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;All done!&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Since I had to use 1u keycaps for the leftmost and rightmost columns,
I ended up not having enough keycaps to replace the two keycaps in the top of each thumb cluster.
But I think that having tall keycaps there makes them easier to press -
those positions are a bit difficult to reach otherwise.
So I may just keep the stock caps on those keys.
Or I might try to get tall, DCS profile, PBT caps for those positions.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBA//EABYBAQEBAAAAAAAAAAAAAAAAAAIAAf/aAAwDAQACEAMQAAABVxjIwH//xAAaEAACAgMAAAAAAAAAAAAAAAABAgMRACEj/9oACAEBAAEFAkhQmVFRLyyBF0at/wD/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEv/aAAgBAwEBPwFL/8QAFREBAQAAAAAAAAAAAAAAAAAAABL/2gAIAQIBAT8BU//EABsQAAEFAQEAAAAAAAAAAAAAAAEAAhARIRLw/9oACAEBAAY/AjbR4q2gDYdSPex//8QAGhABAAMBAQEAAAAAAAAAAAAAAQARITFxwf/aAAgBAQABPyGxMiwNbU5PRLKqb+zBxDLiBBwZ/9oADAMBAAIAAwAAABC8L//EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPxCM1//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EGP/xAAdEAEAAwABBQAAAAAAAAAAAAABABEhQTFhkbHh/9oACAEBAAE/EHFc7R3R6ggcCmmt+RI1f3LYsanVDcUdOG8eIS2gNn//2Q==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;closeup of one of the wells to show key spacing&quot; title=&quot;closeup of one of the wells to show key spacing&quot; src=&quot;/static/dc35e3e60282c839b7f9b2fc2b1675ec/18e3b/IMG_20140515_194415103_HDR.jpg&quot; srcSet=&quot;/static/dc35e3e60282c839b7f9b2fc2b1675ec/46946/IMG_20140515_194415103_HDR.jpg 240w,/static/dc35e3e60282c839b7f9b2fc2b1675ec/55489/IMG_20140515_194415103_HDR.jpg 480w,/static/dc35e3e60282c839b7f9b2fc2b1675ec/18e3b/IMG_20140515_194415103_HDR.jpg 960w,/static/dc35e3e60282c839b7f9b2fc2b1675ec/5d68e/IMG_20140515_194415103_HDR.jpg 1243w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;closeup of one of the wells to show key spacing&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The other positions where I think that DSA does not work really well are the four keys in the bottom row of each well.
I curl my fingers down to reach those;
and I tend to either hit the edges of the keys,
or to press them with my fingernail instead of with my finger.
The stock keycaps for those positions are angled toward the center of the well,
making it easier to reach the tops of the keys.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Those points aside,
I am very pleased with how these new keycaps worked out!
The DSA profile is quite comfortable.
I love the texture of the PBT keycaps.
And they make a more pleasant sound than the thinner ABS caps that came with the board.&lt;/p&gt;&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:960px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:56.25%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAABXXXANECP/8QAGhAAAwEAAwAAAAAAAAAAAAAAAQIDAAQRFP/aAAgBAQABBQIoy5RV381Dnq/Y5FdO1Cv/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwFH/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAEREv/aAAgBAgEBPwHMKf/EABkQAAIDAQAAAAAAAAAAAAAAAAABITEyEf/aAAgBAQAGPwKUyE+FFmzR/8QAHBAAAgICAwAAAAAAAAAAAAAAAAERMSGRQbHB/9oACAEBAAE/IbRxwoNwYsdxV/Im8NpEgbn/2gAMAwEAAgADAAAAEOMf/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/EJGH/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EJBl/8QAHhABAAICAQUAAAAAAAAAAAAAAQARITGRQVGB4fD/2gAIAQEAAT8QXt3ZU8LbE5mLwBrzuBDLfV9oojtVWm+ISp/HaZ6K5oCf/9k=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image css-0&quot; alt=&quot;shoe for science!&quot; title=&quot;shoe for science!&quot; src=&quot;/static/689d10524bf1b9e412dd3fa0bc30d84e/18e3b/IMG_20140518_185154.jpg&quot; srcSet=&quot;/static/689d10524bf1b9e412dd3fa0bc30d84e/46946/IMG_20140518_185154.jpg 240w,/static/689d10524bf1b9e412dd3fa0bc30d84e/55489/IMG_20140518_185154.jpg 480w,/static/689d10524bf1b9e412dd3fa0bc30d84e/18e3b/IMG_20140518_185154.jpg 960w,/static/689d10524bf1b9e412dd3fa0bc30d84e/87945/IMG_20140518_185154.jpg 1024w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot;/&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;shoe for science!&lt;/figcaption&gt;&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[Category Theory proofs in Idris]]></title><link>https://sitr.us/2014/05/05/category-theory-proofs-in-idris.html</link><guid isPermaLink="false">https://sitr.us/2014/05/05/category-theory-proofs-in-idris.html</guid><pubDate>Mon, 05 May 2014 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://www.idris-lang.org/&quot; class=&quot;css-1od09yo&quot;&gt;Idris&lt;/a&gt; is a programming language with dependent types.
It is similar to &lt;a href=&quot;http://wiki.portal.chalmers.se/agda/pmwiki.php&quot; class=&quot;css-1od09yo&quot;&gt;Agda&lt;/a&gt;, but hews more closely to &lt;a href=&quot;http://www.haskell.org/haskellwiki/Haskell&quot; class=&quot;css-1od09yo&quot;&gt;Haskell&lt;/a&gt;.
The goal of Idris is to bring dependent types to general-purpose programming.
It supports multiple compilation targets, including C and Javascript.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Dependent types provide an unprecedented level of type safety.
A quick example is a &lt;a href=&quot;https://www.youtube.com/watch?v=fVBck2Zngjo&quot; class=&quot;css-1od09yo&quot;&gt;type-safe printf implementation (video)&lt;/a&gt;.
They are also useful for theorem proving.
According to the &lt;a href=&quot;http://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence&quot; class=&quot;css-1od09yo&quot;&gt;Curry-Howard correspondence&lt;/a&gt;,
mathematical propositions can be represented in a program as types.
An implementation that satisfies a given type
serves as a proof of the corresponding proposition.
In other words, inhabited types represent true propositions.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Curry-Howard correspondence applies to every language with type checking.
But the type systems in most languages are not expressive enough
to build very interesting propositions.
On the other hand,
dependent types can express quantification
(i.e., the mathematical concepts of universal quantification (∀) and existential quantification (∃)).
This makes it possible to translate a lot of interesting math into machine-verified code.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This post is written in literate Idris.
The &lt;a href=&quot;/Cat.lidr&quot; class=&quot;css-1od09yo&quot;&gt;original markup&lt;/a&gt; can be compiled and type-checked.
Code blocks that are prefixed with greater-than symbols (&amp;gt;) in the markup are evaluated.
Blocks that are marked off with three backticks
are given for illustrative purposes and are not evaluated.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In Idris, partial functions are allowed by default.
A totality requirement can be specified per-function.
This line enforces totality checking by default for functions in this module.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A function that is &lt;em class=&quot;css-0&quot;&gt;total&lt;/em&gt; is guaranteed to terminate
and to return a well-typed output for every possible input.&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref css-1od09yo&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
A function that does not terminate,
or that throws a runtime error for some inputs,
is said to be &lt;em class=&quot;css-0&quot;&gt;partial&lt;/em&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A partial function can introduce a logical contradiction,
which would make proofs unreliable.
So totality checking is useful for theorem proving.&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;theorem-proving&quot; class=&quot;css-1bzbprl&quot;&gt;Theorem proving&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Consider the definition of the natural number type in the Idris standard library:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This defines a type, &lt;code class=&quot;css-0&quot;&gt;Nat&lt;/code&gt;,
with two constructors for producing values:
a number may be zero (&lt;code class=&quot;css-0&quot;&gt;Z&lt;/code&gt;),
or it may be one greater than another number (&lt;code class=&quot;css-0&quot;&gt;S Nat&lt;/code&gt;).&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A type can be &lt;em class=&quot;css-0&quot;&gt;indexed&lt;/em&gt; by another type.
That describes a type produced by a type constructor
that takes one or more values as parameters.
Here is a constructor for indexed types from the Idris standard library:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This declares that &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt; is a constructor that takes two &lt;code class=&quot;css-0&quot;&gt;Nat&lt;/code&gt; &lt;em class=&quot;css-0&quot;&gt;values&lt;/em&gt; as parameters,
and produces a concrete &lt;code class=&quot;css-0&quot;&gt;Type&lt;/code&gt;.
The types that &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt; constructs also happen to be propositions which state that
one natural number is less than or equal to another.
It should be read,
&amp;quot;the natural number n is less than or equal to the natural number m&amp;quot;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Two value constructors are given.
They are written so that a value that satisfies a given &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt; type be written
if and only if the &lt;code class=&quot;css-0&quot;&gt;n&lt;/code&gt; in that type is less than or equal to the &lt;code class=&quot;css-0&quot;&gt;m&lt;/code&gt;.
In this way, a value that satisfies a type of the form &lt;code class=&quot;css-0&quot;&gt;LTE n m&lt;/code&gt;
is a proof that &lt;code class=&quot;css-0&quot;&gt;n&lt;/code&gt; really is less than or equal to &lt;code class=&quot;css-0&quot;&gt;m&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt; is a singleton value - it is a constructor that takes no arguments.
But its type contains a variable; so it is polymorphic.
&lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt; can satisfy any type of the form, &lt;code class=&quot;css-0&quot;&gt;LTE Z n&lt;/code&gt;.
&lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt; is effectively an axiom, stating a fundamental property of natural numbers.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Given the definition of &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt; it is possible to write a proposition,
such as,
&amp;quot;zero is less than or equal to every natural number&amp;quot;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The proposition is written as a function that takes a number as input.
The &lt;em class=&quot;css-0&quot;&gt;value&lt;/em&gt; that is given is assigned to the variable &lt;code class=&quot;css-0&quot;&gt;n&lt;/code&gt;,
which is used to specify the return type.
Thus the return type of &lt;code class=&quot;css-0&quot;&gt;nonNegative&lt;/code&gt; &lt;em class=&quot;css-0&quot;&gt;depends&lt;/em&gt; on the input value.
Wherever you see &lt;code class=&quot;css-0&quot;&gt;(a : A)&lt;/code&gt; it can be read as,
&amp;quot;Some value of type &lt;code class=&quot;css-0&quot;&gt;A&lt;/code&gt; will be given here,
and that value will be assigned to the variable &lt;code class=&quot;css-0&quot;&gt;a&lt;/code&gt;.&amp;quot;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To write an implementation for &lt;code class=&quot;css-0&quot;&gt;nonNegative&lt;/code&gt;,
it is necessary to produce a value of the appropriate &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt; type
without any information about what input might be given -
other than the fact that it will be a natural number.
Totality checking is enabled,
so any implementation must be applicable to every possible input.
Thus a type of the form,
&lt;code class=&quot;css-0&quot;&gt;(x : A) -&amp;gt; P x&lt;/code&gt; describes universal quantification over the type &lt;code class=&quot;css-0&quot;&gt;A&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;nonNegative&lt;/code&gt; happens to be a restatement of the axiom, &lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt;.
So an implementation / proof is trivial:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;On the other hand, &lt;code class=&quot;css-0&quot;&gt;lteSucc&lt;/code&gt; maps a given proof to a proof of a related proposition.
It is used in proofs-by-induction.
For example, a proof that every number is less than or equal to itself:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The proof that zero is equal to itself is given by the axiom.
For every other number, the proof is given as an inductive step
using a proof for the next-smallest number.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Because the type of &lt;code class=&quot;css-0&quot;&gt;lteSucc&lt;/code&gt; is that of a function,
it can be read as a proposition involving logical implication:
&amp;quot;n &amp;lt;= m implies that n + 1 &amp;lt;= m + 1.&amp;quot;
In general, types of the form
&lt;code class=&quot;css-0&quot;&gt;P x -&amp;gt; Q y&lt;/code&gt;
can be read as logical implication.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We have seen that the input value to a function can be labelled and referenced
in the output type.
That is a special property of functions.
For example, it is not permissible to label the value in the first position of
a tuple type to reference it in the second position.
But there is a special construction, the dependent pair, which does allow this.
Dependent pairs are used to represent existential quantification.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A dependent pair type of the form
&lt;code class=&quot;css-0&quot;&gt;(x : A ** P x)&lt;/code&gt;
is read as existential quantification over the type &lt;code class=&quot;css-0&quot;&gt;A&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A proof of a proposition with existential quantification can be given as
a pair of an arbitrary value and a proof that the proposition holds for that value.
For example, here is a proof of the Archimedean Property of natural numbers,
&amp;quot;For every natural number, n, there exists a natural number, m, where m &amp;gt; n&amp;quot;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The quantified proposition uses &lt;code class=&quot;css-0&quot;&gt;(S n)&lt;/code&gt; instead of just &lt;code class=&quot;css-0&quot;&gt;n&lt;/code&gt;
to indicate that &lt;code class=&quot;css-0&quot;&gt;m&lt;/code&gt; must be strictly greater than &lt;code class=&quot;css-0&quot;&gt;n&lt;/code&gt; -
greater-than-or-equal-to is not sufficient.
The proof supplies &lt;code class=&quot;css-0&quot;&gt;S n&lt;/code&gt; as a &lt;em class=&quot;css-0&quot;&gt;witness&lt;/em&gt; -
a specific value that is used to prove that the quantified proposition holds.
Remember that &lt;code class=&quot;css-0&quot;&gt;S n&lt;/code&gt; is just another way of writing &lt;code class=&quot;css-0&quot;&gt;n + 1&lt;/code&gt;.
The second component of the dependent pair value must be a proof that the witness
is greater than or equal to &lt;code class=&quot;css-0&quot;&gt;S n&lt;/code&gt; - as is required by the type.
Since &lt;code class=&quot;css-0&quot;&gt;S n&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;S n&lt;/code&gt; are equal, &lt;code class=&quot;css-0&quot;&gt;lteReflexive&lt;/code&gt; suffices.&lt;/p&gt;&lt;h2 id=&quot;definition-of-category&quot; class=&quot;css-1bzbprl&quot;&gt;Definition of Category&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have been studying Category Theory;
so I decided to use that as a topic for exercises when learning about Idris.
If you are wondering what Category Theory is all about,
take a look at &lt;a href=&quot;http://rs.io/2014/04/30/why-category-theory-matters.html&quot; class=&quot;css-1od09yo&quot;&gt;Why Category Theory Matters&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is &lt;a href=&quot;https://github.com/copumpkin/categories&quot; class=&quot;css-1od09yo&quot;&gt;a much more complete description&lt;/a&gt;
of Category Theory concepts written in Agda.
The definition below was an exercise for me in learning about
Category Theory concepts myself.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A category is a set of objects
combined with a set of arrows that encode relations between objects.
When talking about all possible categories,
the concepts of &amp;quot;object&amp;quot; and &amp;quot;arrow&amp;quot; are very abstract.
They could be pretty much anything.
Descriptions of specific categories make specific statements
about what objects are and what arrows are.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Let&amp;#x27;s implement a type class to capture this definition.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Arrows are indexed by objects.
That is, the type of an arrow carries its domain
(the object that an arrow originates from)
and its codomain
(the object that an arrow points to).
Note that &lt;code class=&quot;css-0&quot;&gt;obj&lt;/code&gt; is given as an unqualified variable.
In some categories objects will be types - as in the Set category.
In others they will be plain values.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The methods of this type class will define the category laws.
For starters, there must be an id arrow for every object.
This line specifies that an instance of this type class must provide
a &lt;code class=&quot;css-0&quot;&gt;cId&lt;/code&gt; implementation with the given type:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;As was shown above,
a function type serves as a proposition with universal quantification.
So the type of &lt;code class=&quot;css-0&quot;&gt;cId&lt;/code&gt; states that every object must be both the domain
and codomain of at least one arrow.
The implementation will also provide a means to identify that arrow.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Arrows must be composable.
If one arrow points from objects &lt;code class=&quot;css-0&quot;&gt;a&lt;/code&gt; to &lt;code class=&quot;css-0&quot;&gt;b&lt;/code&gt;,
and another arrow points from &amp;#x27;b&amp;#x27; to &amp;#x27;c&amp;#x27;,
then it must be possible to combine them
to produce an arrow from &amp;#x27;a&amp;#x27; to &amp;#x27;c&amp;#x27;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Arguments in curly braces are implicit parameters.
In most cases the compiler will infer those values.
So they are generally not given as explicit arguments when invoking the function.
However, it is possible to provide implicit parameters explicitly when needed.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;cId&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;cComp&lt;/code&gt; are the only required functions that actually produce arrows.
But it is necessary to provide more specific rules about how they should behave.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;These propositions state that id arrows must not only have the same
object as domain and codomain -
they must also be identities under composition.
Implementations of &lt;code class=&quot;css-0&quot;&gt;cIdLeft&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;cIdRight&lt;/code&gt; will never be used at runtime -
they are just proofs that &lt;code class=&quot;css-0&quot;&gt;cId&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;cComp&lt;/code&gt; obey the category laws.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here &lt;code class=&quot;css-0&quot;&gt;(=)&lt;/code&gt; is a type constructor, very much like &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt;.
Its definition looks like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The parameters to &lt;code class=&quot;css-0&quot;&gt;(=)&lt;/code&gt; can be any expressions -
including plain values or types.
&lt;code class=&quot;css-0&quot;&gt;refl&lt;/code&gt; is another axiom, stating that equality is reflexive.
Basically, to prove that two expressions are equivalent
it is necessary to demonstrate to the type checker that
they have the same normal form.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This implies that there might be multiple arrows pointing between the same two objects;
and that those arrows are not necessarily equivalent.
When talking about all possible categories,
it is not possible to say what it is that makes arrows different or the same.
Rather, any specific category must have its own definition of equality of arrows.
Some categories will have many arrows between each pair of objects;
some will have at most one.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;One more proof is required to complete the &lt;code class=&quot;css-0&quot;&gt;Category&lt;/code&gt; type class.
Arrow composition must be associative.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have cheated slightly.
In the types for &lt;code class=&quot;css-0&quot;&gt;cIdLeft&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;cIdRight&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;cCompAssociative&lt;/code&gt;
I left the implicit arguments to &lt;code class=&quot;css-0&quot;&gt;cComp&lt;/code&gt; implicit.
But I was not able to get those definitions to type-check.
I actually had to list out the implicit parameters
when applying &lt;code class=&quot;css-0&quot;&gt;cComp&lt;/code&gt; in a type expression.
The working definitions are a bit more difficult to read:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It has been pointed out to me that the compiler is not able to determine
which implementation of &lt;code class=&quot;css-0&quot;&gt;cidLeft&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;cIdRight&lt;/code&gt;, or &lt;code class=&quot;css-0&quot;&gt;cCompAssociative&lt;/code&gt; should be invoked,
unless the implicit parameters are listed.
If Category were implemented as a record type instead of as a type class
this would probably not be necessary.&lt;/p&gt;&lt;h2 id=&quot;a-partial-ordering-category&quot; class=&quot;css-1bzbprl&quot;&gt;A partial ordering category&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;With the definition of Category in place,
it is possible to describe specific categories -
and to prove that they obey the category laws.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt; type constructor can be used to
describe a category where arrows are &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt; relations,
and objects are natural numbers.
In this category,
there are arrows that point from each number
to every other number that is larger,
and also back to the number itself.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An id arrow in this category is a proof that a number is less than or equal to itself.
This is the same thing that was proved by &lt;code class=&quot;css-0&quot;&gt;lteReflexive&lt;/code&gt;;
so the implementation is the same.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Arrow composition is a proof that the less-than-or-equal-to relation is transitive.
For reference, here is the type for &lt;code class=&quot;css-0&quot;&gt;cComp&lt;/code&gt; specialized for &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;And the proof construction:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In the first equation, the second input is &lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt;;
which implies that &lt;code class=&quot;css-0&quot;&gt;a&lt;/code&gt; is zero.
&lt;code class=&quot;css-0&quot;&gt;a&lt;/code&gt; is also the domain in the &lt;code class=&quot;css-0&quot;&gt;LTE&lt;/code&gt; result that we want to prove;
so the proof is trivial.
The second equation takes advantage of the fact that the &lt;code class=&quot;css-0&quot;&gt;lteSucc&lt;/code&gt;
constructors of input arrows can be recursively unwrapped,
until reaching the base case, where the second input is &lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is no pattern for a case where the first input is &lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt;
where the second is not.
It is not required, because that case is not allowed by the type of &lt;code class=&quot;css-0&quot;&gt;cComp&lt;/code&gt; -
and the type checker is able to confirm that.
If it were necessary to make explicit that this case is not possible,
that could be stated with a third equation:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The keyword &lt;code class=&quot;css-0&quot;&gt;impossible&lt;/code&gt; is one tool available for proofs of falsehood.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Now to prove the remaining category laws.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Demonstrating identity under composition of proofs is a little strange.
What we need to show is that composing an identity arrow with any other arrow
does not introduce new information.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In the base case of &lt;code class=&quot;css-0&quot;&gt;cIdLeft&lt;/code&gt;, if the given arrow, &lt;code class=&quot;css-0&quot;&gt;f&lt;/code&gt;, is &lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt;
then its domain must be zero.
Therefore the identity arrow it is composed with must also be &lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt;.
Those are the same normal form,
so the proof invokes &lt;code class=&quot;css-0&quot;&gt;refl&lt;/code&gt; - the axiom of reflexivity of equality.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The inductive step applies &lt;code class=&quot;css-0&quot;&gt;cong&lt;/code&gt;,
which is a proof of congruence of equal expressions.
It is defined in the standard library:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In this case the &lt;code class=&quot;css-0&quot;&gt;f&lt;/code&gt; that &lt;code class=&quot;css-0&quot;&gt;cong&lt;/code&gt; infers as its implicit parameter is &lt;code class=&quot;css-0&quot;&gt;lteSucc&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;cong&lt;/code&gt; is an example of a proof constructor:
it takes a proof as input and returns a proof of a related proposition.
Functions like &lt;code class=&quot;css-0&quot;&gt;cong&lt;/code&gt; are the building blocks of multi-step proofs.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You may notice that &lt;code class=&quot;css-0&quot;&gt;cong&lt;/code&gt; takes &lt;code class=&quot;css-0&quot;&gt;refl&lt;/code&gt; as an argument and returns it.
&lt;code class=&quot;css-0&quot;&gt;cong&lt;/code&gt; has equality proofs as its input and output.
By definition, the only possible value of an equality type is &lt;code class=&quot;css-0&quot;&gt;refl&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The base case of &lt;code class=&quot;css-0&quot;&gt;cIdRight&lt;/code&gt; is not as trivial as the base case of &lt;code class=&quot;css-0&quot;&gt;cIdLeft&lt;/code&gt;.
the codomain of &lt;code class=&quot;css-0&quot;&gt;lteZero&lt;/code&gt; is not necessarily zero;
so the identity arrow involved could be some &lt;code class=&quot;css-0&quot;&gt;lteSucc&lt;/code&gt; value.
However the compiler is able to do some normalization automatically.
That means that it is not necessary to spell out every step.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The proof of associativity follows a similar pattern.&lt;/p&gt;&lt;h2 id=&quot;a-monoid-as-a-category&quot; class=&quot;css-1bzbprl&quot;&gt;A monoid as a category&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The natural numbers form a &lt;a href=&quot;http://en.wikipedia.org/wiki/Monoid&quot; class=&quot;css-1od09yo&quot;&gt;monoid&lt;/a&gt; under addition.
In particular:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;Two numbers can be combined by addition to produce a third number.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;There is an additive identity (0).&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Addition is associative.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The monoid also forms a category with just one object,
(which will be arbitrarily represented with &lt;code class=&quot;css-0&quot;&gt;()&lt;/code&gt;)
where the arrows are integers,
and arrows are composed by addition.
Since there are an unbounded number of natural numbers,
in this category
there are an unbounded number of arrows pointing from &lt;code class=&quot;css-0&quot;&gt;()&lt;/code&gt; back to &lt;code class=&quot;css-0&quot;&gt;()&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This category is made a bit complicated by the requirement that
arrows are indexed by domain and codomain.
Those indexes will not be meaningful in a category with just one object.
But for the sake of generality,
a trivial higher-kinded wrapper around &lt;code class=&quot;css-0&quot;&gt;Nat&lt;/code&gt; is needed.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In Idris, as in Haskell,
&lt;code class=&quot;css-0&quot;&gt;()&lt;/code&gt; is a type with exactly one value,
which is also called &lt;code class=&quot;css-0&quot;&gt;()&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To make it clear that a &lt;code class=&quot;css-0&quot;&gt;NatArrow&lt;/code&gt; is really just a &lt;code class=&quot;css-0&quot;&gt;Nat&lt;/code&gt;,
we can prove that the two types are isomorphic.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An isomorphism is a bidirectional mapping that preserves information in both directions.
&lt;code class=&quot;css-0&quot;&gt;to&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;from&lt;/code&gt; specify the mapping;
&lt;code class=&quot;css-0&quot;&gt;toFrom&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;fromTo&lt;/code&gt; prove that information is preserved.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Now the definition of the Nat category:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This implementation uses &lt;code class=&quot;css-0&quot;&gt;(+)&lt;/code&gt; for arrow composition.
It reuses proofs of identity and associativity for &lt;code class=&quot;css-0&quot;&gt;(+)&lt;/code&gt;.
It is only necessary to apply &lt;code class=&quot;css-0&quot;&gt;cong&lt;/code&gt; to the predefined proofs
to show that applying the &lt;code class=&quot;css-0&quot;&gt;getNat&lt;/code&gt; constructor to both sides of an equality
does not change anything.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For another look at monoids in Idris,
see &lt;a href=&quot;https://www.youtube.com/watch?v=P82dqVrS8ik&quot; class=&quot;css-1od09yo&quot;&gt;Verifying the monoid laws using Idris (video)&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;category-of-sets&quot; class=&quot;css-1bzbprl&quot;&gt;Category of sets&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In the Set category, objects are sets and arrows are functions.
The implementation here will use &lt;code class=&quot;css-0&quot;&gt;Type&lt;/code&gt; as the type of objects.
In Idris, the type of every &lt;em class=&quot;css-0&quot;&gt;small type&lt;/em&gt; is &lt;code class=&quot;css-0&quot;&gt;Type&lt;/code&gt;.
For example, the type of &lt;code class=&quot;css-0&quot;&gt;Nat&lt;/code&gt; is &lt;code class=&quot;css-0&quot;&gt;Type&lt;/code&gt;.
The type of &lt;code class=&quot;css-0&quot;&gt;String&lt;/code&gt; is also &lt;code class=&quot;css-0&quot;&gt;Type&lt;/code&gt;.
The type of &lt;code class=&quot;css-0&quot;&gt;Type&lt;/code&gt; is &lt;code class=&quot;css-0&quot;&gt;Type 1&lt;/code&gt; -
meaning that &lt;code class=&quot;css-0&quot;&gt;Type&lt;/code&gt; is not a small type itself.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The definition of &lt;code class=&quot;css-0&quot;&gt;Category&lt;/code&gt; requires proofs of equivalence for arrows.
But there is no predefined comparison operator for functions in Idris.
All that you can really do with a function is to give it some inputs,
and check what its outputs are.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To produce a viable category, it is necessary to introduce
the axiom of &lt;em class=&quot;css-0&quot;&gt;function extensionality&lt;/em&gt;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If two functions produce the same output for all possible inputs,
then they are equivalent.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;funext&lt;/code&gt; takes as input two functions
and a proof that those functions produce the same output for all inputs.
Note the parentheses in that type:
As function types serve as universal quantifiers,
higher-order functions provide a means to nest quantification
inside of larger propositions.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I am told that it is not possible to prove &lt;code class=&quot;css-0&quot;&gt;funext&lt;/code&gt; in Idris -
and that that limitation is not unique to Idris.
Therefore, function extensionality must be given as an axiom:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;believe_me&lt;/code&gt; is a &amp;quot;proof&amp;quot; to use sparingly.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Implementations for an identity function and for composition of functions
are provided in the standard library.
In fact, Idris includes a &lt;code class=&quot;css-0&quot;&gt;Control.Category&lt;/code&gt; module
with a predefined &lt;code class=&quot;css-0&quot;&gt;Category&lt;/code&gt; type class.
But that definition does not include all of the category laws.
What remains to be defined are proofs of identity and associativity.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Proving that &lt;code class=&quot;css-0&quot;&gt;id (f x)&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;f (id x)&lt;/code&gt; reduce to &lt;code class=&quot;css-0&quot;&gt;f x&lt;/code&gt;
is sufficiently easy that the compiler can do most of the work on its own.
To make the next step to &lt;code class=&quot;css-0&quot;&gt;f x = f x -&amp;gt; f = f&lt;/code&gt;
is just a matter of applying &lt;code class=&quot;css-0&quot;&gt;funext&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In order to prove associativity, it is helpful to have a helper proof
that could be described as, &amp;quot;pointful composition&amp;quot;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The proof of associativity is a bit complicated.
So it is broken into steps here,
with proven intermediate propositions given for each step.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are two standard library functions used here that have not been introduced yet.
They are:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When a free, lowercase variable appears in a type expression,
Idris inserts an additional implicit parameter at the beginning of the expression.
It is up to the compiler to infer the types of the free variables.
After carrying out that expansion, the type of &lt;code class=&quot;css-0&quot;&gt;trans&lt;/code&gt; looks like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There may be more steps listed in &lt;code class=&quot;css-0&quot;&gt;compAssociative&lt;/code&gt; than are really required.
Hopefully including them helps to clarify the proof.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In Haskell, &lt;code class=&quot;css-0&quot;&gt;(-&amp;gt;)&lt;/code&gt; is an ordinary type constructor that could be used
as the arrow type in a type class like &lt;code class=&quot;css-0&quot;&gt;Category&lt;/code&gt;.
It seems that is not the case in Idris.
To work around that, the standard library includes a type, &lt;code class=&quot;css-0&quot;&gt;Morphism&lt;/code&gt;,
that is isomorphic to the function type.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The proofs above provide everything that is required to prove the validity of
a category of Types and functions.
But the definition here uses Morphism for arrows;
so a little extra translating is necessary.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The operator &lt;code class=&quot;css-0&quot;&gt;(~&amp;gt;)&lt;/code&gt; is an infix alias for &lt;code class=&quot;css-0&quot;&gt;Morphism&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;As with &lt;code class=&quot;css-0&quot;&gt;Nat&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;NatArrow&lt;/code&gt;,
the correspondence between &lt;code class=&quot;css-0&quot;&gt;(-&amp;gt;)&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;Morphism&lt;/code&gt; is made official
with a proof of isomorphism.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It is my hope that I can use these definitions to work out exercises
as I continue to explore Category theory.&lt;/p&gt;&lt;div class=&quot;footnotes css-0&quot;&gt;&lt;style data-emotion-css=&quot;1ldi06f&quot;&gt;.css-1ldi06f{background-color:var(--theme-ui-colors-muted,#e2e8f0);border:0;height:1px;margin:1rem;}&lt;/style&gt;&lt;hr class=&quot;css-1ldi06f&quot;/&gt;&lt;ol class=&quot;css-15rlv7r&quot;&gt;&lt;li id=&quot;fn-1&quot; class=&quot;css-0&quot;&gt;The &lt;a href=&quot;http://en.wikipedia.org/wiki/Halting_problem&quot; class=&quot;css-1od09yo&quot;&gt;Halting Problem&lt;/a&gt; states that there are programs that cannot be proven to terminate.  That does not mean that it is impossible to prove that any program terminates.  Idris and other languages with totality checking put some restrictions on the forms that functions are allowed to take so that totality checking is possible.&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref css-1od09yo&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Functional data structures in JavaScript with Mori]]></title><link>https://sitr.us/2013/11/04/functional-data-structures.html</link><guid isPermaLink="false">https://sitr.us/2013/11/04/functional-data-structures.html</guid><pubDate>Mon, 04 Nov 2013 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have a long-standing desire for a JavaScript library that provides
good implementations of functional data structures.  Recently I found
&lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://swannodette.github.io/mori/&quot; class=&quot;css-1od09yo&quot;&gt;Mori&lt;/a&gt;, and I think that it may be just the library that I have been
looking for.  Mori packages data structures from the &lt;a href=&quot;http://clojure.org/&quot; class=&quot;css-1od09yo&quot;&gt;Clojure&lt;/a&gt;
standard library for use in JavaScript code.&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;table-of-contents&quot; class=&quot;css-1bzbprl&quot;&gt;Table of Contents&lt;/h2&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;/ul&gt;&lt;h2 id=&quot;functional-data-structures&quot; class=&quot;css-1bzbprl&quot;&gt;Functional data structures&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A functional data structure (also called a persistent data structure)
has two important qualities: it is immutable and it can be updated by
creating a copy with modifications (copy-on-write).  Creating copies
should be nearly as cheap as modifying a comparable mutable data
structure in place.  This is achieved with structural sharing: pointers
to unchanged portions of a structure are shared between copies so that
memory need only be allocated for changed portions of the data
structure.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A simple example is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Linked_list&quot; class=&quot;css-1od09yo&quot;&gt;linked list&lt;/a&gt;.  A linked list is an object,
specifically a list node, with a value and a pointer to the next list
node, which points to the next list node.  (Eventually you get to the
end of the list where there is a node that points to the empty list.)
Prepending an element to the front of such a list is a constant-time
operation: you just create a new list element with a pointer to the
start of the existing list.  When lists are immutable there is no need
to actually copy the original list.  Removing an element from the front
of a list is also a constant-time operation: you just return a pointer
to the second element of the list.  Here is &lt;a href=&quot;http://anorwell.com/index.php?id=61&quot; class=&quot;css-1od09yo&quot;&gt;a slightly more-detailed
explanation&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Lists are just one example.  There are functional implementations of
maps, sets, and other types of structures.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Rich Hickey, the creator Clojure, describes functional data structures
as &lt;a href=&quot;http://clojure.org/state&quot; class=&quot;css-1od09yo&quot;&gt;decoupling state and time&lt;/a&gt;.  (Also available in
&lt;a href=&quot;http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey&quot; class=&quot;css-1od09yo&quot;&gt;video form&lt;/a&gt;.)  The idea is that code that uses
functional data structures is easier to reason about and to verify than
code that uses mutable data structures.&lt;/p&gt;&lt;h2 id=&quot;clojure-clojurescript-and-mori&quot; class=&quot;css-1bzbprl&quot;&gt;Clojure, ClojureScript, and Mori&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://clojure.org/&quot; class=&quot;css-1od09yo&quot;&gt;Clojure&lt;/a&gt; is a functional language that compiles to JVM bytecode.  It
is a Lisp dialect.  Among Clojure&amp;#x27;s innovations are implementations of
a number of functional data structures, old and new.  For example, other
Lisps tend to place prime importance on linked lists; but a lot of
Clojure code is based on &lt;code class=&quot;css-0&quot;&gt;PersistentVector&lt;/code&gt;, which supports efficient
random-access operations.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;https://github.com/clojure/clojurescript&quot; class=&quot;css-1od09yo&quot;&gt;ClojureScript&lt;/a&gt; is an alternative Clojure compiler that produces
JavaScript code instead of JVM bytecode.  The team behind ClojureScript
has ported Clojure collections to ClojureScript implementations - which
are therefore within reach of JavaScript code.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://swannodette.github.io/mori/&quot; class=&quot;css-1od09yo&quot;&gt;Mori&lt;/a&gt; incorporates the ClojureScript build tool and pulls out just
the standard library data structures.  It builds a JavaScript file that
can be used as a standalone library.  Mori adds some API customizations
to make the Clojure data structures easier to use in JavaScript - such
as helpers to convert between JavaScript arrays and Clojure structures.
Mori also includes a few helpers to make functional programming easier,
like &lt;code class=&quot;css-0&quot;&gt;identity&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;constant&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;sum&lt;/code&gt; functions.  These are the data
structures provided in the latest version of Mori, v0.2.4:&lt;/p&gt;&lt;table class=&quot;css-0&quot;&gt;&lt;thead&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;th class=&quot;css-0&quot;&gt;constructor&lt;/th&gt;&lt;th class=&quot;css-0&quot;&gt;Mori&lt;/th&gt;&lt;th class=&quot;css-0&quot;&gt;Clojure&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;list&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://swannodette.github.io/mori/#list&quot; class=&quot;css-1od09yo&quot;&gt;Mori docs&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#toc13&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;vector&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://swannodette.github.io/mori/#vector&quot; class=&quot;css-1od09yo&quot;&gt;Mori docs&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#toc15&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;range&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://swannodette.github.io/mori/#range&quot; class=&quot;css-1od09yo&quot;&gt;Mori docs&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojuredocs.org/clojure_core/clojure.core/range&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;hash_map&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://swannodette.github.io/mori/#hash_map&quot; class=&quot;css-1od09yo&quot;&gt;Mori docs&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#toc17&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#toc21&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;sorted_map&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#toc17&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;sorted_map_by&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#toc17&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;set&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://swannodette.github.io/mori/#set&quot; class=&quot;css-1od09yo&quot;&gt;Mori docs&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#toc22&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;sorted_set&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://swannodette.github.io/mori/#sorted_set&quot; class=&quot;css-1od09yo&quot;&gt;Mori docs&lt;/a&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#toc22&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;sorted_set_by&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#toc22&quot; class=&quot;css-1od09yo&quot;&gt;Clojure docs&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;All of these data structures are immutable and can be efficiently
updated via copying and structural sharing.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The documentation for Mori is pretty good.  But it does skip over some
of the available data structures and functions.  Since most of the stuff
provided by Mori comes from Clojure, if you cannot find information that
you need in the Mori docs you can also look at the Clojure docs.
I provided links to the Clojure documentation for each data structure
where more detailed descriptions are available.&lt;/p&gt;&lt;h2 id=&quot;installing-and-running&quot; class=&quot;css-1bzbprl&quot;&gt;Installing and running&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To get Mori, install the &lt;a href=&quot;https://docs.nodejitsu.com/articles/getting-started/npm/what-is-npm&quot; class=&quot;css-1od09yo&quot;&gt;npm&lt;/a&gt; package &lt;code class=&quot;css-0&quot;&gt;mori&lt;/code&gt;.  Then you can require
the module &lt;code class=&quot;css-0&quot;&gt;&amp;#x27;mori&amp;#x27;&lt;/code&gt; in a &lt;a href=&quot;http://nodejs.org/&quot; class=&quot;css-1od09yo&quot;&gt;Node.js&lt;/a&gt; project; or copy the file
&lt;code class=&quot;css-0&quot;&gt;mori.js&lt;/code&gt; from the installed package and drop it into a web browser.&lt;/p&gt;&lt;h2 id=&quot;examples&quot; class=&quot;css-1bzbprl&quot;&gt;Examples&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Let&amp;#x27;s take a look at the particular advantages of some of these
structures.&lt;/p&gt;&lt;style data-emotion-css=&quot;1u9n620&quot;&gt;.css-1u9n620{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.5rem;margin-top:1rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1u9n620{font-size:1.875rem;}}@media screen and (min-width:768px){.css-1u9n620{font-size:2.25rem;}}&lt;/style&gt;&lt;h3 id=&quot;vector&quot; class=&quot;css-1u9n620&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;vector&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A vector is an ordered, finite sequence that supports efficient
random-access lookups and updates.  Vectors are created using the
&lt;a href=&quot;http://swannodette.github.io/mori/#vector&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;vector&lt;/code&gt;&lt;/a&gt; function from the &lt;code class=&quot;css-0&quot;&gt;&amp;#x27;mori&amp;#x27;&lt;/code&gt; module.  Any
arguments given to the function become elements of a new vector.  In
&lt;a href=&quot;http://nodejs.org/&quot; class=&quot;css-1od09yo&quot;&gt;Node.js&lt;/a&gt; you can import Mori and create a vector like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Mori also works with &lt;a href=&quot;https://github.com/amdjs/amdjs-api/wiki/AMD&quot; class=&quot;css-1od09yo&quot;&gt;AMD&lt;/a&gt; implementations (such as &lt;a href=&quot;http://requirejs.org/&quot; class=&quot;css-1od09yo&quot;&gt;RequireJS&lt;/a&gt;) for
use in browser code:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Idiomatic Clojure is not object-oriented.  The convention in Clojure is
that instead of putting methods on objects / values, one defines
functions that take values as arguments.  Those functions are organized
into modules to group related functions together.  This approach makes
a lot of sense when values are mostly immutable; and it avoids name
collisions that sometimes come up in object-oriented code, since names
are scoped by module instead of by object.&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref css-1od09yo&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Since Mori is an adaptation of Clojure code, it uses the same
convention.  Data structures created with Mori do not have methods.
Instead all functionality is provided by functions exported by the
&lt;code class=&quot;css-0&quot;&gt;&amp;#x27;mori&amp;#x27;&lt;/code&gt; module.  That is why the code here uses expressions like
&lt;code class=&quot;css-0&quot;&gt;mori.count(v)&lt;/code&gt; instead of &lt;code class=&quot;css-0&quot;&gt;v.count()&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Existing vectors can be modified:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;conj&lt;/code&gt; is an idiom that is particular to Clojure.  It inserts one or
more values into a collection.  It behaves differently with different
collection types, using whatever insert strategy is most efficient for
the given collection.&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref css-1od09yo&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;h3 id=&quot;higher-order-functions&quot; class=&quot;css-1u9n620&quot;&gt;higher-order functions&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Mori provides a number of higher-order functions.  Here is an example
that computes the sum of the even values in a collection:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Or, borrowing from &lt;a href=&quot;http://swannodette.github.io/mori/#group_by&quot; class=&quot;css-1od09yo&quot;&gt;an example&lt;/a&gt; in the Mori documentation, one
might compute a sum for even values and a separate sum for odd values:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The example above returns a map created with &lt;a href=&quot;http://clojure.org/data_structures#toc21&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt;&lt;/a&gt;,
which is a map implementation that works well with a small number of
keys.&lt;/p&gt;&lt;h3 id=&quot;sorted_set_by&quot; class=&quot;css-1u9n620&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;sorted_set_by&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;JavaScript does not have its own set implementation.  (Though it looks
like &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set&quot; class=&quot;css-1od09yo&quot;&gt;one will be introduced&lt;/a&gt; in ECMAScript 6.)  Sets are
a feature that I often miss.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A sorted set is a heavenly blend of a sequence and a set.  Any duplicate
values that are added are ignored, and there is a specific ordering of
elements.  Unlike a list or a vector, ordering is not based on
insertion, but is determined by comparisons between elements.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;One possible use for a sorted set is to implement a priority queue.
Consider an example of a calendar application.  &lt;code class=&quot;css-0&quot;&gt;sorted_set_by&lt;/code&gt; takes
a comparison function that is used to to maintain an ordering of added
values.  With the appropriate comparison appointments are added and are
automatically sorted by date:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Like the underlying set, this calendar implementation is immutable.
When an appointment is added you get a new calendar value.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The comparison function for comparing appointments sorts appointments by
date, and uses title as a secondary sort in case there are appointments
with the same date and time.  The sorted set uses this function to
determine equality as well as ordering; so if it made comparisons using
only the date field then the calendar would not accept multiple
appointments with the same date and time.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Appointments can be added to a calendar and queried in date order:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Looks good!  Now let&amp;#x27;s add an undo feature.  In case the user changes
her mind about the last appointment that was added, the undo feature
should recreate the previous state without that appointment.  The
implementation of &lt;code class=&quot;css-0&quot;&gt;Calendar&lt;/code&gt; is the same as before except that the
constructor takes an additional optional argument, the &lt;code class=&quot;css-0&quot;&gt;add&lt;/code&gt; method
passes a reference from one calendar value to the next, and there is
a new &lt;code class=&quot;css-0&quot;&gt;undo&lt;/code&gt; method:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Assuming the same set of appointments, we can use the &lt;code class=&quot;css-0&quot;&gt;undo&lt;/code&gt; method to
step back to a state before the fourth appointment was added:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Immutability comes in handy in this scenario.  It is trivial to step
back in time.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Actually because &lt;code class=&quot;css-0&quot;&gt;Calendar&lt;/code&gt; is immutable, you don&amp;#x27;t necessarily need a special
method to get undo behavior:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I&amp;#x27;m sure that the Synesthesia Bike Tour is a lot of fun.  It&amp;#x27;s just that
when demonstrating an undo feature, something has to take the fall.
That&amp;#x27;s just the world that we live in, I suppose.&lt;/p&gt;&lt;h3 id=&quot;hash_map&quot; class=&quot;css-1u9n620&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;hash_map&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;All JavaScript objects are maps.  But those can only use strings as
keys.&lt;sup id=&quot;fnref-3&quot;&gt;&lt;a href=&quot;#fn-3&quot; class=&quot;footnote-ref css-1od09yo&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;  The &lt;code class=&quot;css-0&quot;&gt;hash_map&lt;/code&gt; provided by Mori can use any values as keys.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you use plain JavaScript objects as keys they will be compared by
reference identity.  If you use Mori data structures as keys they will
be compared by value using equality comparisons provided by
ClojureScript.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Mori maps are immutable; so there is never a need to create defensive
copies.  An update operation produces a new map.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The function &lt;code class=&quot;css-0&quot;&gt;assoc&lt;/code&gt; adds any number key-value pairs to a map; and
&lt;code class=&quot;css-0&quot;&gt;dissoc&lt;/code&gt; removes keys.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;All of this also applies to &lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;sorted_map&lt;/code&gt;, and
&lt;code class=&quot;css-0&quot;&gt;sorted_map_by&lt;/code&gt;.  See &lt;a href=&quot;#different-map-and-set-implementations&quot; class=&quot;css-1od09yo&quot;&gt;Different map and set implementations&lt;/a&gt;
below for information about those.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is a common pattern in JavaScript of passing options objects to
constructors to avoid having functions that take zillions of arguments.
It is also common to have a set of default values for certain options.
So code like this is pretty typical:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I usually put in an empty object as the first argument to the Underscore
&lt;a href=&quot;http://underscorejs.org/#extend&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;_.extend&lt;/code&gt;&lt;/a&gt; call so that I get a new object instead of modifying the
given options object in place.  Modifying the options object could cause
problems if it is reused somewhere outside of my constructor.  An
alternative to the defensive copy could be to use immutable Mori maps:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The function &lt;code class=&quot;css-0&quot;&gt;mori.into(coll, from)&lt;/code&gt; adds all of the members of &lt;code class=&quot;css-0&quot;&gt;from&lt;/code&gt;
into a copy of &lt;code class=&quot;css-0&quot;&gt;coll&lt;/code&gt;.  Both &lt;code class=&quot;css-0&quot;&gt;from&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;coll&lt;/code&gt; can by any Mori
collection.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;That does still involve copying the input &lt;code class=&quot;css-0&quot;&gt;options&lt;/code&gt; object into a new
Mori map.  It is also possible to provide a Mori sequence as input to
start with - &lt;code class=&quot;css-0&quot;&gt;js_to_clj&lt;/code&gt; will accept either a plain JavaScript object or
a Mori collection:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is probably no performance gain from using Mori in this situation.
It is unlikely to matter anyway, since the structures involved are
small.  In situations with larger data structures, or where data is
copied many times in a loop, Mori&amp;#x27;s ability to create copies faster
using less memory could make a difference.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But in my opinion applying immutability consistently - even where there
are no significant performance gains - can simplify things.&lt;/p&gt;&lt;h2 id=&quot;apples-to-apples&quot; class=&quot;css-1bzbprl&quot;&gt;Apples to apples&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The transformations that are available in Mori - &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;filter&lt;/code&gt;, etc.&lt;/p&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;return lazy sequences no matter what the type of the input collection
is.  (See &lt;a href=&quot;#laziness&quot; class=&quot;css-1od09yo&quot;&gt;Laziness&lt;/a&gt; below for an explanation of what laziness is).
This is advantageous because the other collection implementations are
not lazy.  But what if you want to do something like create a new set
based on an existing set?  The answer is that you feed a lazy sequence
into a new empty collection using the appropriate constructor or the
&lt;code class=&quot;css-0&quot;&gt;into&lt;/code&gt; function, which dumps all of the content from one collection into
another:&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Applying &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt; to the first set is lazy - but building the second set
with &lt;code class=&quot;css-0&quot;&gt;into&lt;/code&gt; is not.  So a good practice is to avoid building non-lazy
collection until the last possible moment.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An odd quirk in Mori is that the &lt;code class=&quot;css-0&quot;&gt;set&lt;/code&gt; constructor takes a collection
argument, but most of the other constructors take individual values or
key-value pairs.  The &lt;code class=&quot;css-0&quot;&gt;into&lt;/code&gt; function behaves more consistently across
data structure implementations.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You might want to write transformations that are polymorphic - that can
operate on any type of collection and that return a collection of the
same type.  To do that use &lt;code class=&quot;css-0&quot;&gt;mori.empty(coll)&lt;/code&gt; to get an empty version of
a given collection.  This makes it possible to build a new collection
without having to know which constructor was used to create the
original.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here is a function that removes &lt;code class=&quot;css-0&quot;&gt;null&lt;/code&gt; values from any Mori collection
and that returns a collection of the same type:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If the collection given is a map then the value of &lt;code class=&quot;css-0&quot;&gt;elem&lt;/code&gt; in the filter
callback will be a key-value pair.  So &lt;code class=&quot;css-0&quot;&gt;compact&lt;/code&gt; includes an &lt;code class=&quot;css-0&quot;&gt;is_map&lt;/code&gt;
check and extracts the value from that key-value pair if a map is given.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A nice advantage of &lt;code class=&quot;css-0&quot;&gt;empty&lt;/code&gt; is that if you use it on a &lt;code class=&quot;css-0&quot;&gt;sorted_set_by&lt;/code&gt;
or on a &lt;code class=&quot;css-0&quot;&gt;sorted_map_by&lt;/code&gt;, the new collection will inherit the same
comparison function that the original uses.&lt;/p&gt;&lt;h2 id=&quot;mori-pairs-well-with-bacon&quot; class=&quot;css-1bzbprl&quot;&gt;Mori pairs well with Bacon&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In a previous post, &lt;a href=&quot;/2013/05/22/functional-reactive-programming-in-javascript.html&quot; class=&quot;css-1od09yo&quot;&gt;Functional Reactive Programming in
JavaScript&lt;/a&gt;, I wrote about functional reactive programming (FRP)
using &lt;a href=&quot;https://github.com/raimohanska/bacon.js&quot; class=&quot;css-1od09yo&quot;&gt;Bacon.js&lt;/a&gt; and &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS&quot; class=&quot;css-1od09yo&quot;&gt;RxJS&lt;/a&gt;.  A typical assumption in FRP code is
that values contained in events and properties will never be updated in
place.  The immutable data structures that Mori provides are a perfect
fit.&lt;/p&gt;&lt;h2 id=&quot;list-versus-vector&quot; class=&quot;css-1bzbprl&quot;&gt;List versus Vector&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Linked lists are nice and simple - but become less appealing when it
becomes desirable to access elements at arbitrary positions in
a sequence, to append elements to the end of a sequence, or to update
elements at arbitrary indexes.  The running time for all of these
operations on lists is linear, and the append and update operations
require creating a full copy of the list up to the changed position.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Clojure&amp;#x27;s PersistentVector is a sequence, like a list.  But
under-the-hood it is implemented as a tree with 32-way branching.  That
means that any vector index can be looked up in O(log_32 n) time.
Appending and updating arbitrary elements also takes place in
logarithmic time.  For more details read &lt;a href=&quot;http://web.archive.org/web/20130119231848/http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation/&quot; class=&quot;css-1od09yo&quot;&gt;Understanding Clojure&amp;#x27;s
PersistentVector implementation&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Sequences that are implemented as mutable arrays of contiguous memory
support constant-time lookups and modification (not including array
resizing when array length grows).  If O(log_32 n) does not seem
appealing in comparison, consider that if you are using 32-bit integers
as keys:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Which means that your keyspace will run out before your vector&amp;#x27;s tree
becomes more than 7 layers deep.  Up to that point operations will be
O(7) or faster.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If your keys are JavaScript numbers, which are 64-bit floating point
values, the largest integer that you can count up to without skipping
any numbers is &lt;code class=&quot;css-0&quot;&gt;Math.pow(2, 53)&lt;/code&gt;.  The logarithm of that number is also
small:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;hash_map&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;set&lt;/code&gt; implementations in Mori are also implemented as
trees with 32-way branching.  The sorted map and set structures are
implemented as binary trees.&lt;/p&gt;&lt;h2 id=&quot;equality-ordering-and-hashing&quot; class=&quot;css-1bzbprl&quot;&gt;Equality, ordering, and hashing&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Map and set lookups are based on either hashing or ordering comparisons,
depending on the implementation.  JavaScript does not have built-in hash
functions - at least not that are accessible to library code.  It also
does not have defined ordering or equality for most non-primitive
values.  So Mori uses its own functions, which it gets from
ClojureScript.&lt;/p&gt;&lt;h3 id=&quot;hash&quot; class=&quot;css-1u9n620&quot;&gt;hash&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Every Mori value has a hash that identifies its content:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If a value is recreated with the same content, it has the same hash:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Mori&amp;#x27;s hash function delegates to a specific hash algorithm for each of
its data structures, which are ultimately based on internal algorithms
that Mori uses to compute hashes for primitive JavaScript values:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Since Mori also accommodates arbitrary JavaScript values as map keys and
set values, it also has a scheme for assigning hash values to other
JavaScript objects.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It seems that Mori has a monotonically increasing counter for object
hash values.  The first object that it computes a hash for gets the
value &lt;code class=&quot;css-0&quot;&gt;1&lt;/code&gt;; the second object gets &lt;code class=&quot;css-0&quot;&gt;2&lt;/code&gt;, and so on.  To keep track of
which object got which hash value, it stores the value on the object
itself:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are obvious hash-collision issues between non-Mori objects,
numbers, and &lt;code class=&quot;css-0&quot;&gt;true&lt;/code&gt;.  But Mori data structures can handle hash
collisions.  If a collection uses all of those types as keys it could
end up with one hash bucket with three entries.&lt;/p&gt;&lt;h3 id=&quot;equals&quot; class=&quot;css-1u9n620&quot;&gt;equals&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Mori has its own equals function, which also comes from ClojureScript.
As with hash, any two mori values that have the same content are
considered to be equal:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It works on primitive JavaScript values:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When applied to non-Mori JavaScript objects, &lt;code class=&quot;css-0&quot;&gt;equals&lt;/code&gt; works the same way
that the built-in &lt;code class=&quot;css-0&quot;&gt;===&lt;/code&gt; function does:&lt;/p&gt;&lt;h3 id=&quot;compare&quot; class=&quot;css-1u9n620&quot;&gt;compare&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;ClojureScript has a compare function, which Mori uses in its sorted data
structure implementations.  Mori does not export the compare function.
The function defines an ordering for Mori values and for primitive
JavaScript values - but not for other JavaScript objects.  So if you
want to put non-Mori objects into a sorted structure you will have to
use an implementation that accepts a custom comparison function.&lt;/p&gt;&lt;h2 id=&quot;different-map-and-set-implementations&quot; class=&quot;css-1bzbprl&quot;&gt;Different map and set implementations&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;hash_map&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;set&lt;/code&gt; use a hash function for lookups and have O(log_32
n) lookup times; the sorted variants use comparison functions for
lookups and have O(log_2 n) lookup times; and &lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt; is just an
array of key-value pairs, so it uses only an equality function for
lookups and has O(n) lookup times.&lt;/p&gt;&lt;table class=&quot;css-0&quot;&gt;&lt;thead&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;th class=&quot;css-0&quot;&gt;constructor&lt;/th&gt;&lt;th class=&quot;css-0&quot;&gt;insert time&lt;/th&gt;&lt;th class=&quot;css-0&quot;&gt;lookup time&lt;/th&gt;&lt;th class=&quot;css-0&quot;&gt;how keys/values are checked&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;hash_map&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_32 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_32 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;hash(key) === hash(target_key) &amp;amp;&amp;amp; equals(key, target_key)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;1&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;equals(key, target_key)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;sorted_map&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_2 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_2 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;compare(key, target_key)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;sorted_map_by&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_2 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_2 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;like &lt;code class=&quot;css-0&quot;&gt;sorted_map&lt;/code&gt; with a user-supplied comparison function&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;set&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_32 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_32 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;hash(val) === hash(target_val) &amp;amp;&amp;amp; equals(val, target_val)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;sorted_set&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_2 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_2 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;compare(val, target_val)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;css-0&quot;&gt;&lt;td class=&quot;css-0&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;sorted_set_by&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_2 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;log_2 n&lt;/td&gt;&lt;td class=&quot;css-0&quot;&gt;like &lt;code class=&quot;css-0&quot;&gt;sorted_set&lt;/code&gt; with a user-supplied comparison function&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;hash&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;equals&lt;/code&gt; are provided by Mori.  &lt;code class=&quot;css-0&quot;&gt;compare&lt;/code&gt; is part of
ClojureScript, but is not exported by Mori.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Note that the sorted structures do not perform &lt;code class=&quot;css-0&quot;&gt;equals&lt;/code&gt; checks - instead
they rely on a comparison function to determine whether values or keys
are the same.  On the other hand, the hash structures do perform
&lt;code class=&quot;css-0&quot;&gt;equals&lt;/code&gt; checks to handle hash collisions.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt; is unique among the map and set implementations in that it
preserves the order of keys and values based on the order in which they
were inserted.  If a value is inserted into an &lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt; and then the
map is converted to a sequence (for example, using &lt;code class=&quot;css-0&quot;&gt;mori.seq(m)&lt;/code&gt;) then
the last key-value pair inserted appears last in the resulting sequence.
The ordering in a new &lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt; is determined by the order of
key-value pairs given to the constructor.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt; is a good choice for small maps that are accessed
frequently.  The linear lookup time looks slower than other map
implementations on paper.  But there is no hashing involved and only
simple vector traversal - so each of those n steps is faster than each
of the log_32 n steps in a &lt;code class=&quot;css-0&quot;&gt;hash_map&lt;/code&gt; lookup.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt; implementation has an internal notion of the upper limit
on an efficient size.  Once the map reaches that threshold, inserting
another key-value pair produces a &lt;code class=&quot;css-0&quot;&gt;hash_map&lt;/code&gt; instead of a larger
&lt;code class=&quot;css-0&quot;&gt;array_map&lt;/code&gt;.&lt;sup id=&quot;fnref-4&quot;&gt;&lt;a href=&quot;#fn-4&quot; class=&quot;footnote-ref css-1od09yo&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The information here on implementations and running times mainly comes
from &lt;a href=&quot;http://www.infoq.com/articles/in-depth-look-clojure-collections&quot; class=&quot;css-1od09yo&quot;&gt;An In-Depth Look at Clojure Collections&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;laziness&quot; class=&quot;css-1bzbprl&quot;&gt;Laziness&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Many of the functions provided by Mori return what is called a lazy
sequence.  Being a sequence this is like a list and can be transformed
using functions like &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;filter&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;reduce&lt;/code&gt;.  What makes a
sequence lazy is that it is not actually computed right away.
Evaluation is deferred until some non-lazy function accesses one or more
elements of the transformed sequence.  At that point Mori computes and
memoizes transformations.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The results of a lazy evaluation are cached.  If the same sequence is
forced again the map function will not be called a second time:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Mori runs just enough deferred computation to get whatever result is
needed.  It is often not necessary to compute an entire lazy sequence:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In the above case there is an intermediate lazy sequence that
theoretically contains five values: the results of doubling values in
the original set.  But only the first two values in that sequence are
needed.  The other three are never computed.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Laziness means that it is possible to create infinite sequences without
needing unlimited memory or time:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you try this out in a REPL, such as node, be aware that when an
expression is entered a JavaScript REPL will usually try to print the
value of that expression, which has the effect of forcing evaluation of
lazy sequences.  If you enter a lazy sequence you will end up in an
infinite loop:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The solution to this is to be careful to assign infinite sequences in
&lt;code class=&quot;css-0&quot;&gt;var&lt;/code&gt; statements.  That prevents the REPL from trying to print the
sequence:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Powers of two are actually a terrible example of a lazy sequence: any
element in that sequence could be calculated more quickly using
&lt;code class=&quot;css-0&quot;&gt;Math.pow()&lt;/code&gt;.  It just happens that powers of two are simple to
demonstrate.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Algorithms that really do benefit from infinite sequences are those
where computation of any element requires references to earlier values
in the sequence.  A good example is computing &lt;a href=&quot;https://en.wikipedia.org/wiki/Fibonacci_number&quot; class=&quot;css-1od09yo&quot;&gt;Fibonacci numbers&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This implementation uses the &lt;a href=&quot;http://swannodette.github.io/mori/#iterate&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;iterate&lt;/code&gt;&lt;/a&gt; function, which takes
a function and an initial value.  It creates an infinite sequence by
repeatedly applying the given function.  The given starting value is
&lt;code class=&quot;css-0&quot;&gt;[0, 1]&lt;/code&gt;, and each invocation of the given function combines the second
value in the previous pair with the sum of the values in the previous
pair; so the resulting sequence is: &lt;code class=&quot;css-0&quot;&gt;([0, 1] [1, 1] [1, 2] [2, 3] [3, 5]
...)&lt;/code&gt;.  The &lt;a href=&quot;http://swannodette.github.io/mori/#map&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt;&lt;/a&gt; function is applied to that, taking the first
value from each pair.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Using this sequence allows us to ask the questions:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A lazy sequence might also contain lines from a large file or chunks of
data flowing into a network server.  At the time of this writing I was
not able to write a program that traversed a long sequence in constant
space.  But I have verified that this is possible in JavaScript.  I may
find a solution and post an update later.&lt;/p&gt;&lt;h3 id=&quot;efficiency&quot; class=&quot;css-1u9n620&quot;&gt;Efficiency&lt;/h3&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In procedural code running a sequence through multiple operations that
apply to every element would result in multiple iterations of the entire
sequence.  Because Mori operates lazily it can potentially collect
transformations for each element and apply them in a single pass:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If applying &lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt; to a collection twice resulted in two iterations we
would expect to see:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The fact that the first pass and second pass are interleaved suggests
that Mori collects transformations and applies all transformations to
a value at once.  This is the advantage of lazy evaluation: it
encourages writing code in a way that makes most logical sense rather
than thinking about performance.  You can write what are logically many
iterations over a collection and the library will rearrange computations
to minimize the actual work that is done.&lt;/p&gt;&lt;div class=&quot;footnotes css-0&quot;&gt;&lt;style data-emotion-css=&quot;1ldi06f&quot;&gt;.css-1ldi06f{background-color:var(--theme-ui-colors-muted,#e2e8f0);border:0;height:1px;margin:1rem;}&lt;/style&gt;&lt;hr class=&quot;css-1ldi06f&quot;/&gt;&lt;ol class=&quot;css-15rlv7r&quot;&gt;&lt;li id=&quot;fn-1&quot; class=&quot;css-0&quot;&gt;You might be wondering how Clojure handles polymorphism, since the convention is to use functions instead of methods.  Clojure has a feature called &lt;a href=&quot;http://clojure.org/protocols&quot; class=&quot;css-1od09yo&quot;&gt;protocols&lt;/a&gt; that permit multiple implementations for functions depending on the type of a given argument.  Elsewhere in the functional world, &lt;a href=&quot;http://www.haskell.org/haskellwiki/Haskell&quot; class=&quot;css-1od09yo&quot;&gt;Haskell&lt;/a&gt; and &lt;a href=&quot;http://www.scala-lang.org/&quot; class=&quot;css-1od09yo&quot;&gt;Scala&lt;/a&gt; provide a similar, yet more powerful feature, called &lt;a href=&quot;http://learnyouahaskell.com/types-and-typeclasses&quot; class=&quot;css-1od09yo&quot;&gt;type classes&lt;/a&gt;.&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref css-1od09yo&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id=&quot;fn-2&quot; class=&quot;css-0&quot;&gt;When &lt;code class=&quot;css-0&quot;&gt;conj&lt;/code&gt; is used on a list it prepends elements (like &lt;code class=&quot;css-0&quot;&gt;cons&lt;/code&gt;) because prepending is much cheaper than inserting at other possible positions.  Given a vector &lt;code class=&quot;css-0&quot;&gt;conj&lt;/code&gt; appends values.  Appending is often desired, and appending to a vector is just as efficient as inserting at any other position.  &lt;code class=&quot;css-0&quot;&gt;conj&lt;/code&gt; works on sets and maps too - but in those cases the idea of insertion position is not usually meaningful.&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref css-1od09yo&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id=&quot;fn-3&quot; class=&quot;css-0&quot;&gt;It does look like ECMAScript 6 will add &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map&quot; class=&quot;css-1od09yo&quot;&gt;a Map implementation&lt;/a&gt; and a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap&quot; class=&quot;css-1od09yo&quot;&gt;WeakMap&lt;/a&gt; to the language spec, both of which will take arbitrary objects as keys (only non-primitives in the WeakMap case).  But those implementations will not be immutable!&lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref css-1od09yo&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id=&quot;fn-4&quot; class=&quot;css-0&quot;&gt;&lt;a href=&quot;http://clojure.org/data_structures#Data%20Structures-ArrayMaps&quot; class=&quot;css-1od09yo&quot;&gt;http://clojure.org/data_structures#Data Structures-ArrayMaps&lt;/a&gt;&lt;a href=&quot;#fnref-4&quot; class=&quot;footnote-backref css-1od09yo&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Functional Reactive Programming in JavaScript]]></title><link>https://sitr.us/2013/05/22/functional-reactive-programming-in-javascript.html</link><guid isPermaLink="false">https://sitr.us/2013/05/22/functional-reactive-programming-in-javascript.html</guid><pubDate>Wed, 22 May 2013 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I had a great time at &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://nodepdx.org/&quot; class=&quot;css-1od09yo&quot;&gt;NodePDX&lt;/a&gt; last week.  There were many talks
packed into a short span of time and I saw many exciting ideas
presented.  One topic that seemed particularly useful to me was &lt;a href=&quot;http://lanyrd.com/2013/nodepdx/schbpc/&quot; class=&quot;css-1od09yo&quot;&gt;Chris
Meiklejohn&amp;#x27;s talk on Functional Reactive Programming (FRP)&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I have talked and written about how useful promises are.  See &lt;a href=&quot;/2012/07/31/promise-pipelines-in-javascript.html&quot; class=&quot;css-1od09yo&quot;&gt;Promise
Pipelines in JavaScript&lt;/a&gt;.  Promises are useful when you want
to represent the outcome of an action or a value that will be available
at some future time.  FRP is similar except that it deals with streams
of reoccurring events and dynamic values.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here is an example of using FRP to subscribe to changes to a text input.
This creates an event stream that could be used for a typeahead search
feature:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This creates an event stream from all &lt;code class=&quot;css-0&quot;&gt;keyup&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;change&lt;/code&gt; events on the
given input.  The stream is transformed into a stream of strings matching
the value of the input when each event occurs.  Then that stream is
filtered so that subscribers to &lt;code class=&quot;css-0&quot;&gt;inputs&lt;/code&gt; will only receive events if the
value of the input has a length greater than two.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Streams can be assigned to variables, shared, and used as inputs to
create more specific streams.  In the example above &lt;code class=&quot;css-0&quot;&gt;inputs&lt;/code&gt; is used to
create two more streams: one that limits the stream so that events are
emitted at most every 500 ms and another that takes the throttled
stream and drops duplicate values that appear consecutively.  So when
the final stream, &lt;code class=&quot;css-0&quot;&gt;distinct&lt;/code&gt;, is consumed later it is guaranteed that
events 1) will be non-empty strings with length greater than two, 2)
will not occur too frequently, and 3) will not include duplicates.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;That stream can be fed through a service via ajax calls to show a live
list of results:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here &lt;code class=&quot;css-0&quot;&gt;suggestions&lt;/code&gt; is a new stream that has been transformed from
strings to search results using the &lt;code class=&quot;css-0&quot;&gt;searchWikipedia&lt;/code&gt; function.  All
of jQuery&amp;#x27;s ajax methods return promises and &lt;code class=&quot;css-0&quot;&gt;Bacon.fromPromise()&lt;/code&gt;
turns a promise into an event stream.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;flatMapLatest&lt;/code&gt; transformer builds a new stream from an existing
stream and a stream factory - and it only emits events from the last
stream created.  This means that if the user types slowly and a lot of
ajax requests are made responses to all but the last request will be
disregarded.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;suggestions&lt;/code&gt; stream is ultimately used by calling its &lt;code class=&quot;css-0&quot;&gt;onValue&lt;/code&gt;
method.  That adds a subscriber that runs code for every event that
makes it all the way through the stream.  The result is a list of search
results that is updated live as the user types.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are some other tricks available.  It is possible to bind data from
an event stream to a DOM element:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This creates a new stream that is pared down to just the query used to
produce each set of results.  Whenever a result set is rendered the
corresponding query will also be output as the text content of the
&lt;code class=&quot;css-0&quot;&gt;&amp;#x27;#query&amp;#x27;&lt;/code&gt; element.  The new stream is converted to a property to make
this work.  A property is a value that varies over time.  The main
practical distinction between a property and an event stream is that
a property always has a value.  In other words a property is
&lt;em class=&quot;css-0&quot;&gt;continuous&lt;/em&gt; while an event stream is &lt;em class=&quot;css-0&quot;&gt;discrete&lt;/em&gt;.  This example provides
&lt;code class=&quot;css-0&quot;&gt;&amp;#x27;--&amp;#x27;&lt;/code&gt; as the initial value for the new property.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Notice that property binding as shown here is more general than some
data binding frameworks in that the destination is not limited to DOM
elements and the source is not limited to model instances.  This example
passes values to the &lt;code class=&quot;css-0&quot;&gt;text&lt;/code&gt; method of the given jQuery object.  It is
possible to push data to any method on any object.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Streams can be combined, split, piped, and generally manipulated in all
kinds of ways.  Properties can be bound, sampled, combined, transformed,
or whatever.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I put this code up on JSFiddle so you can try it out and play with it:
&lt;a href=&quot;http://jsfiddle.net/hallettj/SqrNT/&quot; class=&quot;css-1od09yo&quot;&gt;http://jsfiddle.net/hallettj/SqrNT/&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are several FRP implementations out there.  Two that seem to be
prominent are &lt;a href=&quot;https://github.com/raimohanska/bacon.js&quot; class=&quot;css-1od09yo&quot;&gt;Bacon.js&lt;/a&gt; and &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS&quot; class=&quot;css-1od09yo&quot;&gt;RxJS&lt;/a&gt;.  The examples above are code
from the RxJS documentation that I rewrote with Bacon.  That gave me an
opportunity to learn a bit about both libraries and to see how they
approach the same problem.  The original RxJS code is
&lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS#why-rxjs&quot; class=&quot;css-1od09yo&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;With FRP it is possible to describe complicated processes in a clean,
declarative way.  FRP is also a natural way to avoid certain classes of
race conditions.  When I wrote the initial version of the sample code
above it worked perfectly on the first try.  In my view that is a sign
of a very well-designed library.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you are interested in further reading I suggest the &lt;a href=&quot;http://nullzzz.blogspot.fi/2012/11/baconjs-tutorial-part-i-hacking-with.html&quot; class=&quot;css-1od09yo&quot;&gt;series of
tutorials&lt;/a&gt; from the author of Bacon.  And there is a great
deal of information on the RxJS and Bacon Github pages, including
documentation and more examples.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Monkey patching document.write()]]></title><link>https://sitr.us/2012/09/04/monkey-patching-document-write.html</link><guid isPermaLink="false">https://sitr.us/2012/09/04/monkey-patching-document-write.html</guid><pubDate>Tue, 04 Sep 2012 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This is one of the crazier workarounds that I have implemented.  I was
working on a web page that embeds third-party widgets.  The widgets are
drawn in the page document - they do not get their own frames.  And
sometimes the widgets are redrawn after page load.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We had a problem with one widget invoking &lt;code class=&quot;css-0&quot;&gt;document.write()&lt;/code&gt;.  In case
you are not familiar with it, if that method is called while the page is
rendering it inserts content into the DOM immediately after the script
tag in which the call is made.  But if &lt;code class=&quot;css-0&quot;&gt;document.write()&lt;/code&gt; is called
after page rendering is complete it erases the entire DOM.  When this
widget was redrawn after page load it would kill the whole page.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The workaround we went with was to disable &lt;code class=&quot;css-0&quot;&gt;document.write()&lt;/code&gt; after page
load by replacing it with a wrapper that checks whether the jQuery ready
event has fired.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The new implementation checks the value of &lt;code class=&quot;css-0&quot;&gt;jQuery.isReady&lt;/code&gt; and
delegates to the original &lt;code class=&quot;css-0&quot;&gt;document.write()&lt;/code&gt; implementation if the page
is not finished rendering yet.  Otherwise it does nothing other than to
output a warning message.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Disabling &lt;code class=&quot;css-0&quot;&gt;document.write()&lt;/code&gt; means that the problematic widget will not
be fully functional if it is redrawn after page load.  It happens that
in the case of this app that is ok.  The redrawn widget is only used as
a preview when editing widget layouts.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A particular problem came up with IE compatibility.  I wanted to use the
&lt;code class=&quot;css-0&quot;&gt;apply&lt;/code&gt; method that is implemented by all functions in JavaScript to
invoke the original &lt;code class=&quot;css-0&quot;&gt;document.write()&lt;/code&gt; implementation, like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But in older versions of Internet Explorer, &lt;code class=&quot;css-0&quot;&gt;document.write()&lt;/code&gt; is not
really a function.  There are a lot of examples in IE of native API
methods and properties that do not behave like regular JavaScript
values.  For example if you pass too many arguments to a DOM API method
in old IE you will get an exception.  Normal JavaScript functions just
silently ignore extra arguments.  If you look at the value of &lt;code class=&quot;css-0&quot;&gt;typeof
document.write&lt;/code&gt; the result is not &lt;code class=&quot;css-0&quot;&gt;&amp;quot;function&amp;quot;&lt;/code&gt;.  What is particularly
problematic in this case is that &lt;code class=&quot;css-0&quot;&gt;document.write&lt;/code&gt; does not implement
&lt;code class=&quot;css-0&quot;&gt;call&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;apply&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Fortunately I found that the Function prototype does implement both
&lt;code class=&quot;css-0&quot;&gt;call&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;apply&lt;/code&gt; and furthermore you can borrow those methods to use
on function-like values like &lt;code class=&quot;css-0&quot;&gt;document.write&lt;/code&gt;.  &lt;code class=&quot;css-0&quot;&gt;call&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;apply&lt;/code&gt; are
themselves real function values - so &lt;code class=&quot;css-0&quot;&gt;call&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;apply&lt;/code&gt; both implement
&lt;code class=&quot;css-0&quot;&gt;call&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;apply&lt;/code&gt;.  &lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In the workaround above I applied &lt;code class=&quot;css-0&quot;&gt;apply&lt;/code&gt; to &lt;code class=&quot;css-0&quot;&gt;document.write&lt;/code&gt; by taking
the &lt;code class=&quot;css-0&quot;&gt;Function.prototype.apply&lt;/code&gt; value and using its &lt;code class=&quot;css-0&quot;&gt;call&lt;/code&gt; method.  So
this expression,&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;is equivalent to this one,&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Except that the first version works in IE7.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you find this difficult to follow, you are not alone.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We have had this workaround in our code for a couple of years now.  So
far it is working nicely.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Promise Pipelines in JavaScript]]></title><link>https://sitr.us/2012/07/31/promise-pipelines-in-javascript.html</link><guid isPermaLink="false">https://sitr.us/2012/07/31/promise-pipelines-in-javascript.html</guid><pubDate>Tue, 31 Jul 2012 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;aside class=&quot;translations&quot;&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This page has been translated into &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://www.webhostinghub.com/support/es/misc/las-bases-de&quot; class=&quot;css-1od09yo&quot;&gt;Spanish&lt;/a&gt; language by Maria Ramos from &lt;a href=&quot;http://www.webhostinghub.com/&quot; class=&quot;css-1od09yo&quot;&gt;Webhostinghub.com&lt;/a&gt;.&lt;/p&gt;&lt;/aside&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Promises, also know as deferreds or futures, are a wonderful abstraction
for manipulating asynchronous actions.  Dojo has had &lt;a href=&quot;http://dojotoolkit.org/reference-guide/1.7/dojo/Deferred.html&quot; class=&quot;css-1od09yo&quot;&gt;Deferreds&lt;/a&gt;
for some time.  jQuery introduced &lt;a href=&quot;http://api.jquery.com/category/deferred-object/&quot; class=&quot;css-1od09yo&quot;&gt;its own Deferreds&lt;/a&gt;
in version 1.5 based on the CommonJS &lt;a href=&quot;http://wiki.commonjs.org/wiki/Promises/A&quot; class=&quot;css-1od09yo&quot;&gt;Promises/A&lt;/a&gt; specification.  I&amp;#x27;m
going to show you some recipes for working with jQuery Deferreds.  Use
these techniques to turn callback-based spaghetti code into elegant
declarative code.&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;the-basics-of-jquery-deferreds&quot; class=&quot;css-1bzbprl&quot;&gt;The basics of jQuery Deferreds&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A Deferred is an object that represents some future outcome.  Eventually
it will either resolve with one or more values if that outcome was
successful; or it will fail with one or more values if the outcome was
not successful.  You can get at those resolved or failed values by
adding callbacks to the Deferred.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In jQuery&amp;#x27;s terms a promise is a read-only view of a deferred.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here is a simple example of creating and then resolving a promise:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Callbacks can be added to a deferred or a promise using the &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt;
method.  The first callback is called on success, the second on failure:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For more information see the
&lt;a href=&quot;http://api.jquery.com/category/deferred-object/&quot; class=&quot;css-1od09yo&quot;&gt;jQuery Deferred documentation&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Note that if you are using a version of jQuery prior to 1.8 you will
have to use &lt;code class=&quot;css-0&quot;&gt;.pipe()&lt;/code&gt; instead of &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt;.  That goes for all
references to &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; in this article.&lt;/p&gt;&lt;h2 id=&quot;sequential-operations&quot; class=&quot;css-1bzbprl&quot;&gt;Sequential operations&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Actions, such as HTTP requests, need to be sequential if input to one
action depends on the output of another; or if you just want to make
sure that actions are performed in a particular order.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Consider a scenario where you have a post id and you want to display
information about the author of that post.  Your web services don&amp;#x27;t
support embedding author information in a post resource.  So you will
have to download data on the post, get the author id, and then make
another request to get data for the author.  To start with you will want
functions for downloading a post and a user:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In jQuery 1.5 and later all ajax methods return a promise that, on
a successful request, resolves with the data in the response, the
response status, and the XHR object representing the request.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; method produces a new promise that transforms the resolved
value of its input.  I used &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; here just because using &lt;code class=&quot;css-0&quot;&gt;$.when()&lt;/code&gt;
is simpler if each promise resolves to a single value.  We will get back
to that in parallel operations.  Since only one argument is provided to
&lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; in these cases the new promises will have the same error
values as the originals if an error occurs.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The result is that &lt;code class=&quot;css-0&quot;&gt;getUser()&lt;/code&gt; returns a promise that should resolve to
data representing the user profile for a given id.  And &lt;code class=&quot;css-0&quot;&gt;getPost()&lt;/code&gt;
works the same way for posts and post ids.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Now, to fetch that author information:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When &lt;code class=&quot;css-0&quot;&gt;authorForPost()&lt;/code&gt; is called it returns a new promise that resolves
with author information after both the post and author requests complete
successfully.  This is a straightforward way to get the job done.
Though it does not implement error handling; and it could be more
&lt;a href=&quot;https://en.wikipedia.org/wiki/Don&amp;#x27;t_repeat_yourself&quot; class=&quot;css-1od09yo&quot;&gt;DRY&lt;/a&gt;.  More on that in a bit.&lt;/p&gt;&lt;h2 id=&quot;parallel-operations&quot; class=&quot;css-1bzbprl&quot;&gt;Parallel operations&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Let&amp;#x27;s say that you want to fetch two user profiles to display
side-by-side.  Using the &lt;code class=&quot;css-0&quot;&gt;getUser()&lt;/code&gt; function from the previous section:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The requests for userA and userB&amp;#x27;s profiles will be made in parallel so
that you can get the results back as quickly as possible.  This function
uses &lt;code class=&quot;css-0&quot;&gt;$.when()&lt;/code&gt; to synchronize the promises for each profile so that
&lt;code class=&quot;css-0&quot;&gt;getTwoUsers()&lt;/code&gt; returns one promise that resolves with the data for both
profiles when both responses come back.  If either request fails, the
promise that &lt;code class=&quot;css-0&quot;&gt;getTwoUsers()&lt;/code&gt; returns will fail with information about
the first failed request.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You might use &lt;code class=&quot;css-0&quot;&gt;getTwoUsers()&lt;/code&gt; like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;getTwoUsers()&lt;/code&gt; promise resolves with two values, one for each
profile.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;We now have several well-defined functions that operate on asynchronous
actions.  Isn&amp;#x27;t this nicer than the big mess of nested callbacks one
might otherwise see?&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I mentioned above that using &lt;code class=&quot;css-0&quot;&gt;$.when()&lt;/code&gt; is simpler when each of its
input promises resolves to a single value.  That is because if an input
promise resolves to multiple values then the corresponding value in the
new promise that &lt;code class=&quot;css-0&quot;&gt;$.when()&lt;/code&gt; creates will be an array instead of a single
value.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Performing an arbitrary number of actions in parallel is similar:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This code fetches any number of posts in parallel.  I used &lt;code class=&quot;css-0&quot;&gt;apply&lt;/code&gt; to
pass the post promises to &lt;code class=&quot;css-0&quot;&gt;$.when()&lt;/code&gt; as though they are each a separate
argument.  The resulting promise resolves with a separate value for each
post.  It would be nicer if it resolved with an array of posts as one
value.  The use of &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; here takes those post values and transforms
them into an array.&lt;/p&gt;&lt;h2 id=&quot;combining-sequential-and-parallel-operations&quot; class=&quot;css-1bzbprl&quot;&gt;Combining sequential and parallel operations&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Let&amp;#x27;s take the previous examples to their logical conclusion by creating
a function that, given two post ids, will download information about the
authors of each post to display them side-by-side.  No problem!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;From the perspective of a function that calls &lt;code class=&quot;css-0&quot;&gt;authorForPost()&lt;/code&gt;, it does
not matter that two sequential requests are made.  Because
&lt;code class=&quot;css-0&quot;&gt;authorForPost()&lt;/code&gt; returns a promise that represents the eventual result
of both requests, that detail is encapsulated.&lt;/p&gt;&lt;h2 id=&quot;generalizing-sequential-operations&quot; class=&quot;css-1bzbprl&quot;&gt;Generalizing sequential operations&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are a couple of problems with the implementation of
&lt;code class=&quot;css-0&quot;&gt;authorForPost()&lt;/code&gt; as presented above.  We had to create a deferred by
hand, which should not be necessary.  And the promise that is returned
does not fail if any of the requests involved fail.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;These issues are not present in the parallel examples because &lt;code class=&quot;css-0&quot;&gt;$.when()&lt;/code&gt;
does a nice job of generalizing synchronizing multiple promises.  What
we need is a function that does a similar job of generalizing flattening
nested promises.  Meet flatMap:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This function takes a promise and a callback that returns another
promise.  When the first promise resolves, &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; invokes the
callback with the resolved values as arguments, which produces a new
promise.  When that new promise resolves, the promise that &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt;
returns also resolves with the same values.  On top of that,
&lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; forwards errors to the promise that it returns.  If either
the input promise or the promise returned by the callback fails then the
promise that &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; returns will fail with the same values.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Using &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; it is possible to write a function like
&lt;code class=&quot;css-0&quot;&gt;authorForPost()&lt;/code&gt; a bit more succinctly:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;By using &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; you also get error handling for free.  If the
request to fetch a post fails or the request to fetch the post&amp;#x27;s author
fails the promise that this version of &lt;code class=&quot;css-0&quot;&gt;authorForPost()&lt;/code&gt; returns will
also fail with the appropriate failure values.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Another potential problem is that &lt;code class=&quot;css-0&quot;&gt;authorForPost()&lt;/code&gt; does not give you
access to any of the information on the posts that it downloads.  You
can combine &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; to create a slightly different
function that exposes both the post and the author:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The promise that &lt;code class=&quot;css-0&quot;&gt;postWithAuthor()&lt;/code&gt; returns resolves to a post object
with an added author property containing author information.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It turns out that &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; leads a double life.  If the return value of
its callback is a promise, &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; behaves exactly like &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt;!
This is the sort of thing that only a dynamic language like JavaScript
can do.  So if you want to skip the custom function, you could write
&lt;code class=&quot;css-0&quot;&gt;postWithAuthor()&lt;/code&gt; like this:&lt;/p&gt;&lt;h2 id=&quot;other-uses-for-promises&quot; class=&quot;css-1bzbprl&quot;&gt;Other uses for promises&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The examples above focus on HTTP requests.  But promises can be used in
any kind of asynchronous code.  They even come in handy in synchronous
code from time to time.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here is an example of a promise used to represent the outcome of
a series of user interactions:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I suggest considering using promises anywhere you would otherwise pass
a callback as an argument.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot; class=&quot;css-1bzbprl&quot;&gt;Conclusion&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The promise transformations &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;$.when()&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt;
work together to build promise pipelines.  Using these functions you can
define arbitrary parallel and sequential operations with nice
declarative code.  Furthermore, small promise pipelines can be
encapsulated in helper functions which can be composed to form longer
pipelines.  This promotes reusability and maintainability in your code.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Use &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; to transform  individual promises.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Use &lt;code class=&quot;css-0&quot;&gt;$.when()&lt;/code&gt; to synchronize parallel operations.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Use &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; to create chains of sequential
operations.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Mix and match as desired.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I would like to thank &lt;a href=&quot;https://github.com/munro&quot; class=&quot;css-1od09yo&quot;&gt;Ryan Munro&lt;/a&gt; for coming up with the &amp;quot;pipeline&amp;quot; analogy.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;Update 2012-08-01:&lt;/em&gt; &lt;code class=&quot;css-0&quot;&gt;.pipe()&lt;/code&gt; was added in jQuery 1.6.  And it turns
out that it behaves like &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; when its callback returns
a promise.  In jQuery 1.8 &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; will be updated to behave exactly
like &lt;code class=&quot;css-0&quot;&gt;.pipe()&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;.pipe()&lt;/code&gt; will be deprecated.  So there is actually
no need to add a custom method - you can just use &lt;code class=&quot;css-0&quot;&gt;.pipe()&lt;/code&gt; or &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt;
instead of &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;em class=&quot;css-0&quot;&gt;Update 2013-01-30:&lt;/em&gt; jQuery 1.8 has been released, so I replaced
references to &lt;code class=&quot;css-0&quot;&gt;.pipe()&lt;/code&gt; with &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt;.  I also included a more
prominent explanation that &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; can do the same thing that
&lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; does.&lt;/p&gt;&lt;h2 id=&quot;promises-and-category-theory&quot; class=&quot;css-1bzbprl&quot;&gt;Promises and Category theory&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Good news!  If you are able to follow the examples in this post then you
have a working understanding of Monads.  Specifically, &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt; is
a &lt;a href=&quot;http://en.wikipedia.org/wiki/Monad_(functional_programming)&quot; class=&quot;css-1od09yo&quot;&gt;monad&lt;/a&gt; transformation, &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; with one argument is a &lt;a href=&quot;http://en.wikipedia.org/wiki/Functor&quot; class=&quot;css-1od09yo&quot;&gt;functor&lt;/a&gt;
transformation, and &lt;code class=&quot;css-0&quot;&gt;$.when()&lt;/code&gt; is almost a &lt;a href=&quot;http://en.wikipedia.org/wiki/Monoid&quot; class=&quot;css-1od09yo&quot;&gt;monoid&lt;/a&gt; transformation.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Monads, monoids, and functors are concepts from &lt;a href=&quot;http://en.wikipedia.org/wiki/Category_theory&quot; class=&quot;css-1od09yo&quot;&gt;category theory&lt;/a&gt; that
can be applied to functional programming.  Really they are just
generalizations of this idea of creating pipelines to transform values.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I bring this up because category theory can be useful, but is difficult
to explain.  My hope is that seeing examples of category theory in
action will help programmers to get a feel for the patterns involved.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For more information on category theory in programming I recommend
a series of blog posts titled &lt;a href=&quot;http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-1.html&quot; class=&quot;css-1od09yo&quot;&gt;Monads are Elephants&lt;/a&gt;.  If you have
read that and want to go further, I found the the book &lt;a href=&quot;http://learnyouahaskell.com/&quot; class=&quot;css-1od09yo&quot;&gt;Learn You
a Haskell for Great Good!&lt;/a&gt; to be very informative.  And as a bonus
it teaches you Haskell.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Those who are already into category theory will note that &lt;code class=&quot;css-0&quot;&gt;$.flatMap()&lt;/code&gt;
could also be defined in terms of &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; and a &lt;code class=&quot;css-0&quot;&gt;$.join()&lt;/code&gt; function:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Except that this won&amp;#x27;t actually work because &lt;code class=&quot;css-0&quot;&gt;.then()&lt;/code&gt; will join the
inner and outer promises before the result is passed to &lt;code class=&quot;css-0&quot;&gt;$.join()&lt;/code&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Installing a custom ROM on the Transformer Prime: A start-to-finish guide]]></title><link>https://sitr.us/2012/05/12/installing-a-custom-rom-on-the-transformer-prime.html</link><guid isPermaLink="false">https://sitr.us/2012/05/12/installing-a-custom-rom-on-the-transformer-prime.html</guid><pubDate>Sat, 12 May 2012 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This guide provides step-by-step instructions for installing the &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://www.virtuousrom.com/p/prime.html&quot; class=&quot;css-1od09yo&quot;&gt;Virtuous Prime&lt;/a&gt;
community ROM on your Asus Transformer Prime TF201 tablet.  This guide will
be useful to you if you do not have root access to your tablet.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Be aware that following the instructions here will void your warranty and
will wipe all of the data on your tablet.  There is also a danger that you
might brick your tablet.  Proceed at your own risk.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;So, why would you want to install a custom ROM on your tablet?  In my case
I wanted to gain root access, which allows one to do all sorts of nifty
things.  Community-made ROMS are also often customized to make the Android
experience more pleasant for power users.  And choosing your own ROM means
that you are no longer dependent on the company that sold you your device
to distribute firmware updates in a timely fashion.  But if you are reading
this then you probably already know why you want to install a custom ROM&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;so let&amp;#x27;s get on to the next step.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This guide specifically covers installing &lt;a href=&quot;http://www.virtuousrom.com/p/prime.html&quot; class=&quot;css-1od09yo&quot;&gt;Virtuous Prime&lt;/a&gt;, which is
based on the official Asus firmware.  If you like the features that Asus
provides, like the ability to switch between performance profiles and to
toggle IPS+ mode then this is probably the ROM for you.  Virtuous also adds
some features like root access, the ability to overclock the processors to 1.6 GHz,
and so on.  Note to the Virtuous ROM devs: it is really awesome to have you
all putting in the effort to make the Android experience better for
everybody.  It is especially amazing that you give your ROM away for free.
You are virtuous people indeed!  That said, these folks do accept
&lt;a href=&quot;https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;amp;hosted_button_id=DLXKH3V45FFYY&quot; class=&quot;css-1od09yo&quot;&gt;donations&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you would rather get a vanilla Ice Cream Sandwich experience then you
might check out &lt;a href=&quot;http://transformerprimeroot.com/transformer-prime-roms/aokp-ics-rom-for-rooted-transformer-prime/&quot; class=&quot;css-1od09yo&quot;&gt;AOKP&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you just want root access and you do not want to install a custom ROM
then there is a simpler procedure that you can follow using &lt;a href=&quot;http://forum.xda-developers.com/showthread.php?t=1526487&quot; class=&quot;css-1od09yo&quot;&gt;SparkyRoot&lt;/a&gt;.
The catch is that SparkyRoot does not work in firmware versions v9.4.2.21
or later.  Part of the reason that I went for a custom ROM is that
I upgraded the firmware on my Prime as soon as I got it - so I missed my
shot at using SparkyRoot.  Don&amp;#x27;t be like me: plan ahead!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you have the newest firmware version &lt;a href=&quot;http://forum.xda-developers.com/showthread.php?t=1622628&quot; class=&quot;css-1od09yo&quot;&gt;it is possible to downgrade&lt;/a&gt;
in order to use SparkyRoot.  I chose to install a custom ROM instead
because it seemed to me to be a safer option.  Be aware that if you follow
the directions here to install a custom ROM you will not be able to use the
downgrade procedure in that link.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In brief, here are the steps that we are going to follow:&lt;/p&gt;&lt;ol class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;Unlock the bootloader using the official Asus tool.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Install ClockworkMod Recovery.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Install the Virtuous Prime ROM, using ClockworkMod.&lt;/li&gt;&lt;/ol&gt;&lt;div style=&quot;margin-top:2em&quot; class=&quot;css-0&quot;&gt;&lt;/div&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;step-1-unlock-the-bootloader&quot; class=&quot;css-1bzbprl&quot;&gt;Step 1: Unlock the bootloader&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I am not completely sure that this step is necessary.  I did not try
installing ClockworkMod Recovery before unlocking.  But even if it is not
necessary, I imagine that there may come a time when I am glad to have an
unlocked bootloader.  You can try skipping this step; at worst nothing will
happen.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The unlocking tool is provided by Asus.  As you will see, Asus makes it
very clear that using the unlock tool will void your warranty.  But on
the upside it will not wipe your data or anything like that.  The only
noticeable change will be that every time you boot up the tablet there
will be a message in the upper-left corner of the screen that says, &amp;quot;The
device is UnLocked&amp;quot;.  I assume that is there so that customer service
representatives can see that they are not supposed to help you.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Download the unlock tool directly onto your tablet from &lt;a href=&quot;http://support.asus.com/Download.aspx?SLanguage=en&amp;amp;m=Eee+Pad+Transformer+Prime+TF201&amp;amp;p=20&amp;amp;s=16&quot; class=&quot;css-1od09yo&quot;&gt;the TF201 support
section&lt;/a&gt; of the Asus website.  Select &amp;quot;Android&amp;quot; as the OS
and grab the &amp;quot;Unlock Device App&amp;quot; from the &amp;quot;Utilities&amp;quot; section.  The file
that you get is an apk that you will install as an app.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you have not done so already, you will have to enable unknown software
sources in your tablet settings.  Go to Settings &amp;gt; Security &amp;gt; Device
Administration and check the box that says &amp;quot;Unknown Sources&amp;quot;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Use your file manager to find the downloaded unlock tool.  It is probably in
&lt;code class=&quot;css-0&quot;&gt;/sdcard/Download/UnLock_Device_App_V6.apk&lt;/code&gt;.  Tap it to install the app.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You will be prompted to confirm your Google account by entering your Google
password.  If you are using two-factor authentication on your Google
account you will have to set up an application-specific password for this.
You can revoke that password after your tablet is unlocked.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Next you will have to agree to a license and acknowledge a warning.
Again, Asus wants to make it really clear that you are about to void
your warranty.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;After you agree to everything your tablet will reboot and your bootloader
is now unlocked.&lt;/p&gt;&lt;h2 id=&quot;step-2-install-clockworkmod-recovery&quot; class=&quot;css-1bzbprl&quot;&gt;Step 2: Install ClockworkMod Recovery&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;ClockworkMod Recovery is a custom recovery image.  The Transformer Prime
comes with a recovery image provided by Asus that lets you do stuff like
manually install OS updates.  But the Asus recovery image will only let you
install updates that are digitally signed by Asus.  To install
a community-made ROM you need a recovery mode that will let you install
unsigned ROMs.  That is what ClockworkMod does.  It also provides extra
features, like the ability to back up and restore your whole OS.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Installing ClockworkMod will probably void your warranty - in case you
somehow got to this point with an intact warranty.  There is also some
danger that you could brick your tablet.  Proceed at your own risk.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;These instructions are adapted from
&lt;a href=&quot;http://transformerprimeroot.com/transformer-prime-recovery/how-to-install-clockworkmod-recovery-5-8-2-0-on-transformer-prime/&quot; class=&quot;css-1od09yo&quot;&gt;a guide on TransformerPrimeRoot.com&lt;/a&gt;.  I&amp;#x27;m going to
give you the slightly more complicated version that involves downloading
files directly from ClockworkMod and from Google; and I will provide tips
on what to do if the fastboot tool is not able to find your tablet.
I personally appreciate it when I can get files from sources that I know
I can trust.  But if you are not convinced that is necessary then feel free
to check out the TransformerPrimeRoot.com guide.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For this step you will need a computer.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Download the latest ClockworkMod Recovery image from
&lt;a href=&quot;http://www.clockworkmod.com/rommanager&quot; class=&quot;css-1od09yo&quot;&gt;ClockworkMod&amp;#x27;s downloads page&lt;/a&gt;.  As of this
writing the latest version for the Transformer Prime is 5.8.2.0.  According
to TransformerPrimeRoot.com earlier versions can put your device into
a reboot loop (which you can recover from but it is scary when it happens,
I imagine).&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Touch Recovery image should also work.  It comes with a nicer
touch-based UI.  But the guides that I have read call for the non-touch
version, so that is what I went with.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You will also need the fastboot tool from the Android SDK to install the
recovery image.  &lt;a href=&quot;https://developer.android.com/sdk/index.html&quot; class=&quot;css-1od09yo&quot;&gt;Download the SDK&lt;/a&gt; from Google and extract
it.  Run the &lt;code class=&quot;css-0&quot;&gt;android&lt;/code&gt; executable in the &lt;code class=&quot;css-0&quot;&gt;tools/&lt;/code&gt; directory to launch the
SDK package manager and use that tool to install the Android SDK
Platform-tools: check the box next to &amp;quot;Android SDK Platform-tools&amp;quot; and
click &amp;quot;Install packages...&amp;quot;.  After a few minutes that will add a new
executable, &lt;code class=&quot;css-0&quot;&gt;fastboot&lt;/code&gt;, in the &lt;code class=&quot;css-0&quot;&gt;platform-tools&lt;/code&gt; directory in the Android
SDK package.  You will use the fastboot program to send commands to your
tablet while the tablet is in fastboot mode.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Android devices have two boot modes apart from the normal boot-into-the-OS
option: &lt;a href=&quot;http://www.androidcentral.com/what-recovery-android-z&quot; class=&quot;css-1od09yo&quot;&gt;recovery mode&lt;/a&gt; and &lt;a href=&quot;http://www.androidcentral.com/android-z-what-fastboot&quot; class=&quot;css-1od09yo&quot;&gt;fastboot&lt;/a&gt;.  Fastboot is a low-level mode that
is used for flashing firmware.  You can use fastboot to replace the
recovery mode image - which is what we will be doing to install
ClockworkMod.  And you can use recovery mode to install a new OS.
Likewise, if your OS breaks you can fix it from recovery mode and if
recovery mode breaks you may be able to fix it from fastboot.  What happens
if fastboot breaks?  Try not to let that happen!  The Android devs have not
yet provided us with an extra-fast-boot.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you are on Windows then you will have to &lt;a href=&quot;http://transformerprimeroot.com/transformer-prime-root/how-to-install-transformer-prime-usb-drivers-on-windows/&quot; class=&quot;css-1od09yo&quot;&gt;install a USB driver&lt;/a&gt; before
proceeding.  Linux and Mac users do not need any special drivers.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Make sure that your battery is charged to at least 50%.  Bad things will
happen if your battery dies while you are flashing a recovery image.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Boot your tablet into fastboot by holding down the power and volume-down
buttons.  The tablet will power off and reboot.  Wait until you see several
lines of white text in the upper-left corner of the screen, then let go of
the power and volume buttons.  Then wait for five seconds and you will see
the fastboot options.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Press volume-down to highlight the USB icon and then press volume-up to
select it.  You have ten seconds to do this - after that the tablet will
cold-boot Android instead.  If that happens, don&amp;#x27;t worry.  Just start over
by holding down the power and volume-down buttons.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Plug the tablet into your computer using the USB cable that came with your
tablet.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;On your computer open a terminal.  Assuming that you have the
ClockworkMod Recovery image and the extracted Android SDK in the same
downloads folder, cd to that folder.  Run this fastboot command to make
sure that your computer is talking to your tablet:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;-i 0x0b05&lt;/code&gt; part tells fastboot which USB device to communicate with.
The number &lt;code class=&quot;css-0&quot;&gt;0b05&lt;/code&gt; is the Asus vendor id for USB interfaces.  If you want to
double-check that vendor id you can use the &lt;code class=&quot;css-0&quot;&gt;lsusb&lt;/code&gt; command on Linux.  On
my machine the output includes a line that looks like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The vendor id is the portion of the ID before the colon.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Anyway, if the fastboot command that you ran worked you should see output that
looks like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;On the other hand, if you see a message that says &lt;code class=&quot;css-0&quot;&gt;&amp;lt; waiting for device &amp;gt;&lt;/code&gt;,
and you wait a minute or two and nothing happens, then hit Ctrl-c to
cancel.  If you are in Linux you can fix this problem by creating a udev
rule.  Create a new file, &lt;code class=&quot;css-0&quot;&gt;/etc/udev/rules.d/99-android.rules&lt;/code&gt; and add this
line to it:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But make sure to replace &amp;quot;yourusername&amp;quot; with your user name.  Then
restart udev with this command:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Now try the fastboot command again.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Ok, is fastboot talking to your tablet?  Now for the next step: flashing
ClockworkMod.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I recommend checking the md5 checksum on the ClockworkMod Recovery image
to make sure that it has not been corrupted.  On Linux you can use this
command to do that:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It appears that ClockworkMod does not list md5 checksums on its
downloads page.  But here is the checksum that I got for version 5.8.2.0:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You should proceed only if the checksums match.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Run this command - and again make sure that the paths match the
locations of the fastboot and ClockworkMod Recovery image files:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If all goes well you should see some output like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Now you have ClockworkMod Recovery installed.&lt;/p&gt;&lt;h2 id=&quot;step-3-install-the-virtuous-prime-rom&quot; class=&quot;css-1bzbprl&quot;&gt;Step 3: Install the Virtuous Prime ROM&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Following the instructions here will wipe everything on your tablet.  And
you will void your warranty - again.  Proceed at your own risk.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are &lt;a href=&quot;http://rootzwiki.com/topic/21125-roms-series300312-virtuous-prime-94221-v1/&quot; class=&quot;css-1od09yo&quot;&gt;instructions on RootzWiki&lt;/a&gt; for installing Virtuous Prime.  I&amp;#x27;m
going to give you the same instructions but with a bit more detail.  But
I recommend reading the information on RootzWiki too as there is a lot
of useful background there.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Download the latest version of &lt;a href=&quot;http://www.virtuousrom.com/p/prime.html&quot; class=&quot;css-1od09yo&quot;&gt;Virtuous Prime&lt;/a&gt; directly onto your
tablet.  As of this writing that is Virtuous Prime 9.4.2.21 v1, which is
based on the Transformer Prime v9.4.2.21 firmware from Asus.  You will get
a zip file - don&amp;#x27;t unzip it!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Next to the download link there will be an MD5 checksum.  We will refer
back to that in a moment.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Use your file manager to move the zip file,
&lt;code class=&quot;css-0&quot;&gt;virtuous_prime_s_9.4.2.21_v1.zip&lt;/code&gt; from &lt;code class=&quot;css-0&quot;&gt;/sdcard/Download/&lt;/code&gt; to
&lt;code class=&quot;css-0&quot;&gt;/sdcard/&lt;/code&gt;.  I think that this step is superfluous; but it makes me feel
better.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This is optional, but highly recommended: install the free &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.fab.md5&quot; class=&quot;css-1od09yo&quot;&gt;MD5 Checker&lt;/a&gt;
app.  You can use this app to check the MD5 checksum of the file that you
downloaded to make sure that it was not corrupted.  Open MD5 Checker, click
on the button labelled &amp;quot;Load File 1&amp;quot;, browse to the Virtuous Prime zip
file, and wait for MD5 Checker to compute the file&amp;#x27;s checksum.  Make sure
that the checksum that you see in MD5 Checker is the same as the one from
the Virtuous Prime downloads page.  If the checksums do not match then do
not proceed!  Download the file again and check the checksum again.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Shut down your tablet.  But make sure that your battery is charged to at
least 50% first.  And make sure that the USB cable is unplugged.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Boot into recovery mode by holding both the power and volume-down buttons.
As with fastboot, when you see several lines of white text in the
upper-left corner of the screen let go of both buttons.  But this time
press volume-up right away.  If you do not press volume-up within five
seconds then the tablet will go into fastboot.  If that happens then just
start over by holding the power and volume-down buttons again.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;After a moment you should see the ClockworkMod menu.  You can use the
volume-up and volume-down buttons to highlight the different menu options
and the power button to select the option that you want.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Make a backup of your current ROM.  Select &amp;quot;backup and restore&amp;quot;, then
&amp;quot;backup&amp;quot;.  This will create a timestamped backup directory on your tablet
under &lt;code class=&quot;css-0&quot;&gt;/sdcard/clockwork/backup/&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is a lot of information on backing up and restoring your tablet
&lt;a href=&quot;http://www.androidpolice.com/2010/04/16/complete-guide-how-to-fully-back-up-and-restore-your-android-phone-using-nandroid-backup-and-clockworkmod-rom-manager/&quot; class=&quot;css-1od09yo&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The backup process will take a few minutes.  When it is done you will see
the ClockworkMod menu again.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Select &amp;quot;wipe data/factory reset&amp;quot;.  According to RootzWiki this step is
optional, but is highly recommended.  You will have to complete an
elaborate confirmation step to start the wipe.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Once all of your data has been wiped, select &amp;quot;install zip from sdcard&amp;quot;,
then &amp;quot;choose zip from sdcard&amp;quot;.  Browse to the Virtuous Prime ROM and select
it.  And confirm that you are really sure about this.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You will be taken through a guided install process in which you will be
prompted to choose between Typical, Complete, or Minimal install modes.
There is a list of the differences between the three modes on
&lt;a href=&quot;http://rootzwiki.com/topic/21125-roms-series300312-virtuous-prime-94221-v1/&quot; class=&quot;css-1od09yo&quot;&gt;RootzWiki&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When the installer is finished your tablet will reboot and you are done!
Congratulations!  Enjoy your new ROM!&lt;/p&gt;&lt;h2 id=&quot;more-resources&quot; class=&quot;css-1bzbprl&quot;&gt;More Resources&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is some useful information collected on &lt;a href=&quot;http://transformerprimeroot.com/&quot; class=&quot;css-1od09yo&quot;&gt;TransformerPrimeRoot.com&lt;/a&gt;.
Much of the information in my guide comes from that site.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If the worst should happen and your tablet becomes a brick, you may still
be able to recover.  Check out the &lt;a href=&quot;http://forum.xda-developers.com/showthread.php?t=1514088&quot; class=&quot;css-1od09yo&quot;&gt;recovery guide&lt;/a&gt; on
&lt;a href=&quot;http://forum.xda-developers.com/&quot; class=&quot;css-1od09yo&quot;&gt;XDA-Developers&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;update-2012-05-19&quot; class=&quot;css-1bzbprl&quot;&gt;Update 2012-05-19&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;After about a week of use, the Virtuous Prime ROM is working very well.
It does everything that the Asus firmware did and more.  But I did run
into some problems that I wanted to report along with some workarounds.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Hulu Plus app does not work for me anymore.  When I try to play
a video I get this message:&lt;/p&gt;&lt;style data-emotion-css=&quot;1gtwvjp&quot;&gt;.css-1gtwvjp{border-left-color:var(--theme-ui-colors-primary,#6b46c1);border-left-style:solid;border-left-width:6px;margin-left:0;margin-right:0;padding-left:2rem;}.css-1gtwvjp p{font-style:italic;}&lt;/style&gt;&lt;blockquote class=&quot;css-1gtwvjp&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Streaming Unavailable [91]&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Sorry, we are unable to stream this video. Please check your Internet
connection, ensure you have the latest official update for your
device, and try again.&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Hulu app did work for me before I unlocked my tablet.  There are
&lt;a href=&quot;http://forum.xda-developers.com/showthread.php?t=1571405&quot; class=&quot;css-1od09yo&quot;&gt;reports that unlocking the bootloader is what causes this problem&lt;/a&gt;.
This may not have anything to do with Virtuous Prime directly.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A helpful community member created
&lt;a href=&quot;http://forum.xda-developers.com/showthread.php?t=1449110&quot; class=&quot;css-1od09yo&quot;&gt;a modified version of the Hulu Plus app&lt;/a&gt; that does work.
From the first post in that thread you can download and install the
Landscape Mod HuluPlus.apk package.  You will need to enable &amp;quot;Unknown
Sources&amp;quot; to install the package.  Before you install this version
I suggest wiping the data of the original Hulu app and uninstalling it.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The home view in the app is distorted; but the queue view works fine.
This mod is based on a phone version of the original Hulu app rather
than a tablet version.  Video quality seems a bit low - I don&amp;#x27;t know
whether that is due to my connection or to the app.  With those caveats,
the modified app works great for me.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Netflix works perfectly.  Hooray for Netflix!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Amazon Kindle app crashes when I try to open a book.  I have tried
wiping the app&amp;#x27;s data and reinstalling multiple times.  I have also
tried different books.  And I have confirmed that I am running version 3.5.1.1,
which is the latest version available in the Play Store right now.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I managed to fix the Kindle app by following
&lt;a href=&quot;http://forum.xda-developers.com/showthread.php?p=26109294#post26109294&quot; class=&quot;css-1od09yo&quot;&gt;instructions to fix Machinarium&lt;/a&gt;, which simply
involves installing a missing font.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An alternative workaround is to use the &lt;a href=&quot;https://read.amazon.com&quot; class=&quot;css-1od09yo&quot;&gt;Cloud Reader&lt;/a&gt;.  Note that the
Cloud Reader will work in &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.android.chrome&quot; class=&quot;css-1od09yo&quot;&gt;Chrome for Android Beta&lt;/a&gt;, but will refuse
to run in the default Android browser.  You will not be able to install
the extension that allows Cloud Reader to work offline since the mobile
version of Chrome does not support extensions yet.  So you will have to
be connected to the internet to use the Cloud Reader.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Someone on the XDA Developers forum asked whether the game Machinarium
would install under Virtuous Prime.  I tested this and found that the
game will install - but it crashes on startup.  I did not test this game
before installing Virtuous Prime.  All other games that I have tested
have worked fine.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It turns out that the problem with Machinarium is that there is
a missing font in Virtuous Prime.  Specifically the Droid Sans and Droid
Sans Bold fonts are missing.  There is &lt;a href=&quot;http://forum.xda-developers.com/showthread.php?p=26109294#post26109294&quot; class=&quot;css-1od09yo&quot;&gt;a fix&lt;/a&gt; reported
in the XDA Developers forum.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;So with some digging it seems that I did not encounter any problems that
I could not fix.  Also, having root access to my tablet is excellent.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cookies are bad for you: Improving web application security]]></title><link>https://sitr.us/2011/08/26/cookies-are-bad-for-you.html</link><guid isPermaLink="false">https://sitr.us/2011/08/26/cookies-are-bad-for-you.html</guid><pubDate>Fri, 26 Aug 2011 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Most web applications today use browser cookies to keep a user logged in
while she is using the application.  Cookies are a decades-old device
and they do not stand up well to security threats that have emerged on
the modern web.  In particular, cookies are vulnerable to &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Cross-site_request_forgery&quot; class=&quot;css-1od09yo&quot;&gt;cross-site
request forgery&lt;/a&gt;.  Web applications can by made more secure by
using &lt;a href=&quot;http://oauth.net/&quot; class=&quot;css-1od09yo&quot;&gt;OAuth&lt;/a&gt; for session authentication.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This post is based on &lt;a href=&quot;http://opensourcebridge.org/sessions/663&quot; class=&quot;css-1od09yo&quot;&gt;a talk that I gave&lt;/a&gt; at &lt;a href=&quot;http://opensourcebridge.org/&quot; class=&quot;css-1od09yo&quot;&gt;Open Source
Bridge&lt;/a&gt; this year.  The slides for that talk are available
&lt;a href=&quot;/talks/cookies/&quot; class=&quot;css-1od09yo&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;img alt=&quot;cookie authentication&quot; src=&quot;/talks/cookies/svg/cookies.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When a user logs into a web application the application server sets
a cookie value that is picked up by the user&amp;#x27;s browser.  The browser
includes the same cookie value in every request sent to the same host
until the cookie expires.  When the application server receives
a request it can check whether the cookies attached to it contain
a value that identifies a specific user.  If such a cookie value exists
then the server can consider the request to be authenticated.&lt;/p&gt;&lt;img alt=&quot;attacks that target browser authentication&quot; src=&quot;/talks/cookies/svg/cookie-strengths.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are many types of attacks that can be performed against a web
application.  Three that specifically target authentication between the
browser and the server are &lt;a href=&quot;http://en.wikipedia.org/wiki/Man-in-the-middle_attack&quot; class=&quot;css-1od09yo&quot;&gt;man-in-the-middle (&lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt;)&lt;/a&gt;, &lt;a href=&quot;http://en.wikipedia.org/wiki/Cross-site_request_forgery&quot; class=&quot;css-1od09yo&quot;&gt;cross-site
request forgery (&lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt;)&lt;/a&gt;, and &lt;a href=&quot;http://en.wikipedia.org/wiki/Cross-site_scripting&quot; class=&quot;css-1od09yo&quot;&gt;cross-site scripting (&lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt;)&lt;/a&gt;.
Plain cookie authentication is vulnerable to all three.&lt;/p&gt;&lt;img alt=&quot;session hijacking&quot; src=&quot;/talks/cookies/svg/session_hijacking.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In a &lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt; attack the attacker is in a position to watch traffic that
passes between some user&amp;#x27;s browser and an application server.  If that
traffic is not encrypted the attacker could steal private information.
One of the most dangerous things that an attacker can do in this
position is to hijack the user&amp;#x27;s session by reading cookie data from an
HTTP request and including that cookie data in the attacker&amp;#x27;s own
requests to the same server.  This is a form of privilege escalation
attack.  Using this technique an attacker can convince an application
server that the attacker is actually the user who originally submitted
a given cookie.  Thus the attacker gains access to all of the user&amp;#x27;s
protected resources.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Last year a Firefox extension called &lt;a href=&quot;http://codebutler.com/firesheep&quot; class=&quot;css-1od09yo&quot;&gt;Firesheep&lt;/a&gt; made some waves when
it was released.  The purpose of Firesheep was to raise awareness of the
danger of &lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt; attacks.  Most web applications, at that time and today,
use cookie authentication without an encrypted connection between
browser and server.  Firesheep makes it easy to spy on anybody who is
using well known applications like Facebook and Twitter on a public
network.  With the click of a button you can perform a &lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt; attack
yourself, steal someone&amp;#x27;s cookies, and gain access to that person&amp;#x27;s
Facebook account.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Man-in-the-middle_attack&quot; class=&quot;css-1od09yo&quot;&gt;&lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt;&lt;/a&gt; attacks can be effectively blocked by using HTTPS to encrypt
any traffic that contains sensitive information or authentication
credentials.  When using HTTPS you will almost certainly want to set the
&amp;quot;secure&amp;quot; flag on any cookies used for authentication.  That flag
prevents the browser from transmitting cookies over an unencrypted
connection.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;More and more web applications are offering HTTPS - often as on opt-in
setting.  Any web site that requires a login should offer HTTPS - and
ideally it should be enabled by default.&lt;/p&gt;&lt;img alt=&quot;cross-site scripting&quot; src=&quot;/talks/cookies/svg/xss.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Cross-site_scripting&quot; class=&quot;css-1od09yo&quot;&gt;&lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt;&lt;/a&gt; attacks involve an attacker pushing malicious JavaScript code
into a web application.  When another user visits a page with that
malicious code in it the user&amp;#x27;s browser will execute the code.  The
browser has no way of telling the difference between legitimate and
malicious code.  Injected code is another mechanism that an attacker can
use for session hijacking: by default cookies stored by the browser can
be read by JavaScript code.  The injected code can read a user&amp;#x27;s cookies
and transmit those cookies to the attacker.  Just like in the &lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt;
scenario, the attacker can use those cookies to disguise herself as the
hapless user.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are other ways that &lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt; be used can be used to mess with a user - but
session hijacking is probably the most dangerous.  Session hijacking via
&lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt; can be prevented by setting an &amp;quot;httpOnly&amp;quot; flag on cookies that are
used for authentication.  The browser will not allow JavaScript code to
read or write any cookie that is flagged with &amp;quot;httpOnly&amp;quot;; but those
cookies will still be transmitted in request headers.&lt;/p&gt;&lt;img alt=&quot;cross-site request forgery&quot; src=&quot;/talks/cookies/svg/csrf.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Cross-site_request_forgery&quot; class=&quot;css-1od09yo&quot;&gt;&lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt;&lt;/a&gt; attacks authentication indirectly.  A malicious web page can
trick a browser into making cross-domain requests to another web site.
If a user visiting the malicious page is already logged in to that web
site then the malicious page can access the site resources as though it
were logged in as the unsuspecting user.  For example, if a malicious
page can trick the browser into making POST requests to a microblogging
site it can post updates with spam links that appear to have been
written by the victim.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you use Facebook you might have encountered attacks like this
yourself.  You see a post on a friend&amp;#x27;s wall with a button that says
&lt;a href=&quot;http://www.pcworld.com/businesscenter/article/182940/facebook_worm_spreads_with_a_lurid_lure.html&quot; class=&quot;css-1od09yo&quot;&gt;&amp;quot;Don&amp;#x27;t click the button!&amp;quot;&lt;/a&gt;  When you click on it you are
taken to another site and the same message ends up posted on your wall.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This works because the browser automatically sends cookies set on
a given domain with every request made to that domain, regardless of
where those requests originated.  The browser has no way of knowing that
the requests initiated by the malicious page are made without the user&amp;#x27;s
knowledge.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The malicious page could create a cross-domain request by including an
image with a &lt;code class=&quot;css-0&quot;&gt;src&lt;/code&gt; attribute pointing to a &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt; on the site that it is
trying to hack into.  The &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt; does not have to be an image - the browser
will make a GET request to that &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt; and will discard the response when
it determines that the response is not image data.  If that GET request
produced any side-effects, like posting a microblogging update, then the
malicious page has successfully performed an attack.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To make a cross-domain POST request the malicious site might include
a hidden HTML form with an &lt;code class=&quot;css-0&quot;&gt;action&lt;/code&gt; attribute pointing at the site to be
hacked.  The malicious page can use JavaScript to submit the form
without any interaction from the user.  This is another case where the
attacker cannot read the response that comes back but can trigger some
action in the user&amp;#x27;s account.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In some cases &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; attacks can also be used to read data.  Because &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt;
is a strict subset of JavaScript, &lt;a href=&quot;http://blog.archive.jpsykes.com/47/practical-csrf-and-json-security/&quot; class=&quot;css-1od09yo&quot;&gt;HTTP responses that contain &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; data
can be loaded into script tags and executed.&lt;/a&gt;  In some
browsers a malicious page can override the Object and Array constructors
to capture data from the &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; response as it is executed so that it can
be sent to an attacker.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The biggest problem with &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; is that cookies provide absolutely no
defense against this type of attack.  If you are using cookie
authentication you must also employ additional measures to protect
against &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt;.  The most basic precaution that you can take is to make
sure that your application never performs any side-effects in response
to GET requests.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To protect against cross-domain POST requests a commonly used option is
to use an &lt;a href=&quot;https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern&quot; class=&quot;css-1od09yo&quot;&gt;anti-forgery token&lt;/a&gt; that must be submitted with every POST,
PUT, or DELETE request.  The token is generally injected into the HTML
code for forms in such a way that malicious code on another site does
not have any way to access it.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; responses can be protected by pre-pending the &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; response with
some code that makes the response non-executable.  For example, you
could place a JavaScript loop at the beginning of the response that
never terminates.  Or you could put in a statement that throws an
exception.  Putting the whole &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; response inside of a comment block
also works.  The only way for a browser to read &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; data that has been
obfuscated like this is to fetch the resource using &lt;abbr title=&quot;XMLHttpRequest&quot;&gt;XHR&lt;/abbr&gt; and to remove
the extra code before parsing the actual &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; data.  &lt;abbr title=&quot;XMLHttpRequest&quot;&gt;XHR&lt;/abbr&gt; is limited by
the &lt;a href=&quot;http://en.wikipedia.org/wiki/Same_origin_policy&quot; class=&quot;css-1od09yo&quot;&gt;same-origin policy&lt;/a&gt;; so a malicious page cannot make a cross-site
&lt;abbr title=&quot;XMLHttpRequest&quot;&gt;XHR&lt;/abbr&gt; request.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Such multi-layered approaches to &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; defense work but are a pain to
implement.  I know from experience that the stateful nature of
anti-forgery tokens make them a constant source of bugs in Ajax-driven
applications where users might submit several requests to the server
without ever loading a new page.  It is too easy for the client and
server to get out of sync and to disagree about which anti-forgery
tokens are fresh.  And great care must be taken to include the
anti-forgery feature in every form and Ajax call in an application or
a security hole appears.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; obfuscation is easier to apply to every &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; response as a blanket
policy thanks to server-side filters and client-side hooks, such as
those in jQuery&amp;#x27;s Ajax stack.  But then you are not really serving &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; - you
are serving a &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt;-like type with a proprietary wrapper.  I find that
I spend a lot of time instructing people on the existence of
obfuscation, explaining why it is there, and explaining how to set up
hooks to remove it on the client side.&lt;/p&gt;&lt;img alt=&quot;protections provided by the &amp;quot;httpOnly&amp;quot; and &amp;quot;secure&amp;quot; flags&quot; src=&quot;/talks/cookies/svg/secure-cookie-plus-https-strengths.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;By combining the &amp;quot;secure&amp;quot; and &amp;quot;httpOnly&amp;quot; flags and using HTTPS you can
make your application authentication proof against &lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt; attacks and
against some &lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt; attacks.  But there is nothing that will make cookie
authentication resistant to &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; attacks.  The only way to protect
against &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; is to apply additional security measures.  Often multiple
measures are required to combat different possible &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; vectors.  And
those measures are not always simple or transparent.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In my opinion &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; stifles innovation on the web.  Because cross-domain
requests cannot be trusted, even if they appear to be authenticated, web
applications have to be thoroughly locked down to reject any
cross-origin traffic.  There is a relatively new specification for
making cross-origin &lt;abbr title=&quot;XMLHttpRequest&quot;&gt;XHR&lt;/abbr&gt; requests called &lt;a href=&quot;http://www.w3.org/TR/cors/&quot; class=&quot;css-1od09yo&quot;&gt;Cross-Origin Resource Sharing
(&lt;abbr title=&quot;Cross-Origin Resource Sharing&quot;&gt;CORS&lt;/abbr&gt;)&lt;/a&gt;.  This specification could allow for exciting new mashups
involving rich JavaScript applications and public APIs.  Most modern
browsers support &lt;abbr title=&quot;Cross-Origin Resource Sharing&quot;&gt;CORS&lt;/abbr&gt; too - including Internet Explorer 8.  But &lt;abbr title=&quot;Cross-Origin Resource Sharing&quot;&gt;CORS&lt;/abbr&gt; is
rarely used because it opens up a big hole that could be exploited by
&lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt;.  Existing &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; countermeasures rely on limiting &lt;abbr title=&quot;XMLHttpRequest&quot;&gt;XHR&lt;/abbr&gt; requests to
the same-origin policy.  For most web developers the risk is too great
to justify experimenting with new technology.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The way to make the web a safer place is to switch to authentication
mechanisms that provide strong protection against &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; at the most basic
level.  The key is to choose a mechanism that is controlled by the web
application, not the browser.  The web browser has no way of
distinguishing legitimate requests from forged ones - it will attach
cookies to both.  On the other hand, application code can be written to
be smarter.&lt;/p&gt;&lt;img alt=&quot;OAuth&quot; src=&quot;/talks/cookies/svg/Oauth_logo.svg&quot; style=&quot;width:50%;display:block;margin-left:auto;margin-right:auto&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are many authentication schemes that would work well.  I lean
toward OAuth 2.0.  OAuth has some nice advantages: it is standardized;
there are numerous server implementations; and the simplest form of the
&lt;a href=&quot;http://tools.ietf.org/html/draft-ietf-oauth-v2-20&quot; class=&quot;css-1od09yo&quot;&gt;OAuth 2.0 draft specification&lt;/a&gt; is pretty easy to implement.&lt;/p&gt;&lt;img alt=&quot;three-legged OAuth&quot; src=&quot;/talks/cookies/svg/oauth_auth_code_step_1.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In a traditional OAuth setup there are three parties: the authorization
server / resource server, the client and the resource owner.  Through
a series of steps the resource owner, typically a user working through
a web browser, submits a password to the authorization server and the
authorization server issues an access token to the client.  You can read
more about the OAuth protocol flow on the &lt;a href=&quot;http://oauth.net/2/&quot; class=&quot;css-1od09yo&quot;&gt;OAuth 2.0 web site&lt;/a&gt;.&lt;/p&gt;&lt;img alt=&quot;two-legged OAuth&quot; src=&quot;/talks/cookies/svg/oauth_auth_pass_step_1.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When applying OAuth to session authentication the picture becomes
simpler: the browser acts as both the resource owner and the client; so
some of the indirection of three-legged OAuth can be skipped.  Instead,
a web application can use a protocol flow that the OAuth 2.0
specification calls &lt;a href=&quot;http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.3&quot; class=&quot;css-1od09yo&quot;&gt;Resource Owner Password Credentials&lt;/a&gt; in which the
user enters her password into a login form, the password is submitted to
the application server directly, and the server responds to that request
with an access token.  You can think of this as &amp;quot;two-legged&amp;quot; OAuth.&lt;/p&gt;&lt;img alt=&quot;a request signed with OAuth&quot; src=&quot;/talks/cookies/svg/oauth_auth_pass_step_3.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In both the two- and three-legged flows requests are signed by adding an
&amp;quot;Authorization&amp;quot; header with one of two possible formats.  In the &lt;a href=&quot;http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-06&quot; class=&quot;css-1od09yo&quot;&gt;bearer
scheme&lt;/a&gt; the authorization header value is just the access
token that was given to the client.  For example:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;a href=&quot;http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-00&quot; class=&quot;css-1od09yo&quot;&gt;HMAC scheme&lt;/a&gt; is a bit
more complicated: in that case the client is given an access token id in
addition to the token itself and the authorization header includes the
token id and an HMAC-signed hash of the request &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt;, the request method,
a nonce, and possibly a nested hash of the request body.  The OAuth
access token is used as the key in the &lt;a href=&quot;http://en.wikipedia.org/wiki/HMAC&quot; class=&quot;css-1od09yo&quot;&gt;HMAC algorithm&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The advantage of the HMAC scheme is that it can provide some protection
against &lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt; attacks even if signed requests are not encrypted with
HTTPS.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I propose a design in which the browser submits credentials from a login
form to the server via &lt;abbr title=&quot;XMLHttpRequest&quot;&gt;XHR&lt;/abbr&gt;, gets an access token back, and uses that
access token to sign subsequent requests.  Full page requests and form
posts are difficult to sign with OAuth - hyperlinks and form tags do not
provide a way to specify an &amp;quot;Authorization&amp;quot; header.  So OAuth-signed
requests would probably be limited to &lt;abbr title=&quot;XMLHttpRequest&quot;&gt;XHR&lt;/abbr&gt;.  The browser could store the
OAuth access token in a persistent client-side store to give the user an
experience that is indistinguishable from a cookie-based application - but
that is more secure.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It is entirely possible for JavaScript code running in a web browser to
sign requests with HMAC.  There are pure JavaScript implementations
available of many cryptographic functions, including &lt;a href=&quot;http://jssha.sourceforge.net/&quot; class=&quot;css-1od09yo&quot;&gt;SHA-1 and
SHA-256&lt;/a&gt;, which are the hash functions that are used for OAuth HMAC
signing.  However, if your application uses HTTPS to protect every
request then the simpler bearer scheme is entirely sufficient.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In this design form posts would be eliminated.  Instead form data would
be serialized in JavaScript and submitted using Ajax.  That way all
requests that produce side-effects would be channeled through
OAuth-signed &lt;abbr title=&quot;XMLHttpRequest&quot;&gt;XHR&lt;/abbr&gt;.  I am not suggesting eliminating form tags though - form
tags are an essential tool for semantic markup and for accessibility.
I recommend that JavaScript be used to intercept form &amp;quot;submit&amp;quot; events.&lt;/p&gt;&lt;img alt=&quot;the BigPipe design&quot; src=&quot;/talks/cookies/svg/bigpipe_step_1.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are a couple of options for dealing with full page loads.  One
possibility is to not require any authentication for requests for HTML
pages and to design your application so that HTML responses do not
include any protected information.  Such an application would serve
pages as skeletons, with empty areas that to be filled in with dynamic
and protected content after page load using Ajax.  The dynamic responses
could be HTML fragments that are protected by OAuth, or they could be
&lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; responses that are rendered as HTML using client-side templates.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Facebook uses a process like this which they call &lt;a href=&quot;https://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919&quot; class=&quot;css-1od09yo&quot;&gt;BigPipe&lt;/a&gt;.
Facebook&amp;#x27;s rationale for BigPipe is actually performance, not security.
In my opinion the BigPipe approach gives a best-of-both-worlds blend of
performance and security.  Plus, it lets you put caching headers on full
page responses, even in apps with lots of dynamic content.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A downside of BigPipe is that content that is loaded via Ajax generally
cannot be indexed by search engines.  Google&amp;#x27;s recently published
specification for &lt;a href=&quot;http://code.google.com/web/ajaxcrawling/&quot; class=&quot;css-1od09yo&quot;&gt;making Ajax applications crawlable&lt;/a&gt; may provide
a solution to that problem.  Or you might choose to use the BigPipe
approach everywhere in your application except for publicly accessible
pieces of content.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Another way to handle full page loads would be to continue using cookie
authentication for HTML resources.  HTML responses are less vulnerable
to &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; snooping than &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; because HTML is not executable in script
tags.  In this case you should still require OAuth signing on requests
for &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; resources and on any requests that could produce side-effects.
But allowing cookie authentication on non-side-effect-producing GET
requests for HTML resources should be safe.&lt;/p&gt;&lt;img alt=&quot;strengths of OAuth&quot; src=&quot;/talks/cookies/svg/oauth-plus-https-strengths.svg&quot; style=&quot;width:100%&quot; class=&quot;css-0&quot;/&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Using JavaScript to manage access tokens rather than relying on
a built-in browser function makes &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt; attacks impractical.  A malicious
third-party site can no longer rely on browsers to automatically attach
authentication credentials to requests that it triggers.  Client-side
storage implementations are generally protected by the &lt;a href=&quot;http://en.wikipedia.org/wiki/Same_origin_policy&quot; class=&quot;css-1od09yo&quot;&gt;same origin
policy&lt;/a&gt; - so only code running in your application can retrieve an
access token and produce an authenticated request.  And if you combine
OAuth with HTTPS then you are also protected against &lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt; attacks.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A drawback is that you lose the &lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt; protection that the &amp;quot;httpOnly&amp;quot;
cookie flag provides with cookie authentication.  An application that
uses OAuth will have to use other methods to block &lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt;.  But in my
opinion there are better options for dealing with &lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt; than there are for
dealing with &lt;abbr title=&quot;cross-site request forgery&quot;&gt;CSRF&lt;/abbr&gt;.  By consistently sanitizing user-generated content
you can effectively block &lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt; at the presentation layer of your
application.  That would be necessary anyway, since &amp;quot;httpOnly&amp;quot; only
prevents &lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt;-based privilege escalation attacks and by itself does not
prevent other &lt;abbr title=&quot;cross-site scripting&quot;&gt;XSS&lt;/abbr&gt; shenanigans.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To track a session using OAuth applications will need some way to store
access tokens for the duration of a user&amp;#x27;s session.  There are various
ways to do that:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In the simplest case you can store the token in memory by assigning it
to a JavaScript variable.  This might be useful in a single page
application.  The user will have to log in again if she goes to
another page or opens your app in a new window.&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en/dom/storage#localStorage&quot; class=&quot;css-1od09yo&quot;&gt;localStorage&lt;/a&gt; can be used to store a token so that is persistent
even if the user closes and re-opens the browser.  Data stored in
localStorage is available to all windows on the same domain.  You will
probably want to include a hook to clear local storage when the user
logs out of your application.&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en/dom/storage#sessionStorage&quot; class=&quot;css-1od09yo&quot;&gt;sessionStorage&lt;/a&gt; works like localStorage, except that data is only
accessible from the same window that stored it and the whole store for
a given window is wiped when the user closes that window.  So the user
does not have to log in again if she goes to another page; but she
does have to log in again if she opens your app in a new window.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;sessionStorage can be a more secure option than localStorage - especially
on a shared or a public computer.  If you decide to use a storage
option that does not expire automatically when the browser is closed
I suggest including a &amp;quot;remember me&amp;quot; checkbox in your login form and
using sessionStorage instead when the user does not check that box.&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Although I have been arguing that cookies are not the best option for
authentication, storing an access token in a cookie works just fine.
The key is that the server should not consider the cookie to be
sufficient for authentication.  Instead it should require that the
access token be copied from the cookie value into an OAuth header.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For the cookie option to be secure you should set the &amp;quot;secure&amp;quot; flag so
that it is not transmitted over a connection that could be read via
a &lt;abbr title=&quot;man-in-the-middle&quot;&gt;MITM&lt;/abbr&gt; attack.  You should not set the &amp;quot;httpOnly&amp;quot; flag because the
cookie needs to be accessible from JavaScript.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A nice advantage of the cookie option is that users have been trained
that they can delete cookies to reset a session.  On the other hand,
most users do not know about localStorage and most browsers do not
provide an obvious way to clear localStorage.  So the cookie option is
likely to conform best to user expectations.  Cookies can also be
configured to expire when the browser is closed or to persist for
a long period of time.&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Other options include &lt;a href=&quot;https://developer.mozilla.org/en/IndexedDB&quot; class=&quot;css-1od09yo&quot;&gt;IndexedDB&lt;/a&gt;, which is a more sophisticated
store that is similar to localStorage, &lt;a href=&quot;http://en.wikipedia.org/wiki/Flash_cookies&quot; class=&quot;css-1od09yo&quot;&gt;Flash cookies&lt;/a&gt;, and
&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx&quot; class=&quot;css-1od09yo&quot;&gt;userData&lt;/a&gt; in IE.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is a good summary of client-side storage implementations and how
to use them on &lt;a href=&quot;http://diveintohtml5.info/storage.html&quot; class=&quot;css-1od09yo&quot;&gt;Dive Into HTML5&lt;/a&gt;.  Or if you want
a pre-built solution that avoids most cross-browser headaches you can
use &lt;a href=&quot;http://pablotron.org/software/persist-js/&quot; class=&quot;css-1od09yo&quot;&gt;PersistJS&lt;/a&gt; or a similar tool.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Web applications that rely on cookie authentication can often be
designed to degrade gracefully, so that if JavaScript is disabled or is
not available the application will still work.  With OAuth that is not
possible.  I can imagine this being a major objection to ditching cookie
authentication.  Some people prefer to disable JavaScript for security
or for privacy reasons.  Many of the more basic mobile and text-only web
browsers do not support JavaScript.  And in the past screen readers have
not handled JavaScript-driven web apps well.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In my opinion the requirement that JavaScript be enabled to use an
application is generally worthwhile.  Mobile browsers that do support
JavaScript are rapidly pushing out those that do not.  Text-only
browsers will have to start supporting JavaScript sooner or later to
keep up.  The people who designed your web browser took great care to
ensure that your security and privacy are protected even when JavaScript
is enabled.  Screen readers are much better than they used to be at
making JavaScript-driven web sites accessible.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You should consider your target audience, your application requirements,
and your security needs and decide for yourself whether dropping the
noscript option is the right choice for your application.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;No security protocol is bulletproof.  Do lots of research and use common
sense whenever you are working on an application that needs to be
secure.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Image credits:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;a href=&quot;http://en.wikipedia.org/wiki/File:Oauth_logo.svg&quot; class=&quot;css-1od09yo&quot;&gt;OAuth logo&lt;/a&gt; by Chris Messina is licensed under the
&lt;a href=&quot;http://en.wikipedia.org/wiki/en:Creative_Commons&quot; class=&quot;css-1od09yo&quot;&gt;Creative Commons&lt;/a&gt; &lt;a href=&quot;http://creativecommons.org/licenses/by-sa/3.0/deed.en&quot; class=&quot;css-1od09yo&quot;&gt;Attribution-Share Alike 3.0 Unported&lt;/a&gt; license.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Other images used in diagrams are from the &lt;a href=&quot;http://en.wikipedia.org/wiki/Open_Clip_Art_Library&quot; class=&quot;css-1od09yo&quot;&gt;Open Clip Art Library&lt;/a&gt; and
are in the public domain.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How Mobile Safari emulates mouse events]]></title><link>https://sitr.us/2011/07/28/how-mobile-safari-emulates-mouse-events.html</link><guid isPermaLink="false">https://sitr.us/2011/07/28/how-mobile-safari-emulates-mouse-events.html</guid><pubDate>Thu, 28 Jul 2011 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When you are adapting web apps to touchscreen devices particular
challenges come up around events like &lt;code class=&quot;css-0&quot;&gt;mouseover&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;mouseout&lt;/code&gt;.
Touchscreen devices like the iPad do not have a cursor, so the user
cannot exactly move the mouse over an HTML element.  However, Mobile
Safari, the web browser that comes with the iPhone and iPad, has
a fallback for websites that require hovering or cursor movement.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Usually when you tap on an element on a link or other clickable element
Mobile Safari translates that into a regular &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt; event.  The browser
also produces some touch events that do not exist in a lot of browsers.
But from the perspective of a web page that was not designed with
a touchscreen in mind, what you get is a plain &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt;.  More
specifically, the browser fires &lt;code class=&quot;css-0&quot;&gt;mousedown&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;mouseup&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt; in
that order.  But if a clickable element also does something on
&lt;code class=&quot;css-0&quot;&gt;mouseover&lt;/code&gt; then tapping on that element will trigger a &lt;code class=&quot;css-0&quot;&gt;mouseover&lt;/code&gt; event
instead of a &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt;.  Tapping on the same element again will produce
a &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt; event.  A random example of a page that exhibits this behavior
is &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://opensourcebridge.org/events/2011/schedule&quot; class=&quot;css-1od09yo&quot;&gt;the schedule page&lt;/a&gt; from the &lt;a href=&quot;http://opensourcebridge.org/&quot; class=&quot;css-1od09yo&quot;&gt;Open Source Bridge&lt;/a&gt;
website.  Try tapping on session titles and see what happens.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Mobile Safari will only produce mouse events when the user taps on
a clickable element, like a link.  You can make an element clickable by
adding an onClick event handler to it, even if that handler does
nothing.  On tap Mobile Safari fires the events &lt;code class=&quot;css-0&quot;&gt;mouseover&lt;/code&gt;,
&lt;code class=&quot;css-0&quot;&gt;mousemove&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;mousedown&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;mouseup&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt; in that order - with
some caveats which are explained below.  Those events all fire together
after the user lifts her finger.  You might expect the &lt;code class=&quot;css-0&quot;&gt;mousedown&lt;/code&gt; event
to fire as soon as the user presses her finger to the screen - but it
does not.  When the user taps on another clickable element the browser
fires a &lt;code class=&quot;css-0&quot;&gt;mouseout&lt;/code&gt; event on the first element in addition to firing the
aforementioned events on the new element.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;So how do we get to the behavior where one tap emulates &lt;code class=&quot;css-0&quot;&gt;mouseover&lt;/code&gt; and
a second tap emulates &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt;?  It turns out that after any &lt;code class=&quot;css-0&quot;&gt;mouseover&lt;/code&gt;
event handlers run Safari checks the DOM for changes and if the content
has changed it skips the &lt;code class=&quot;css-0&quot;&gt;mousedown&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;mouseup&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt; events.  So
these events do not fire.  When the user taps on the same element again
the &lt;code class=&quot;css-0&quot;&gt;mouseover&lt;/code&gt; event does not fire again, so the browser goes ahead
with the other events.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The &lt;code class=&quot;css-0&quot;&gt;mousemove&lt;/code&gt; event behaves in a similar way: if the DOM has changed
after any &lt;code class=&quot;css-0&quot;&gt;mousemove&lt;/code&gt; handlers are finished running then Mobile Safari
skips the remaining events.&lt;/p&gt;&lt;figure&gt;&lt;figcaption&gt;Diagram from &lt;a href=&quot;http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html&quot; class=&quot;css-1od09yo&quot;&gt;Apple&amp;#x27;s documentation&lt;/a&gt; demonstrating how the browser determines which mouse events to fire.&lt;/figcaption&gt;&lt;img src=&quot;/images/events_1_finger.jpg&quot; alt=&quot;mouse event firing diagram&quot; class=&quot;css-0&quot;/&gt;&lt;/figure&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Safari does not accept just any change to a DOM element as a &amp;quot;content
change&amp;quot; though.  Through testing I discovered that adding a regular
element to the DOM or showing a previously hidden element in
a &lt;code class=&quot;css-0&quot;&gt;mouseover&lt;/code&gt; handler would prevent the &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt; event from firing.  But
removing an element, hiding an element, or changing the content of
a text node do not prevent the &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt; event.  I also tried adding class
names to elements - which Safari also did not treat as a &amp;quot;content
change&amp;quot;.  As far as I can tell only adding or showing an element will
cause the &lt;code class=&quot;css-0&quot;&gt;mousedown&lt;/code&gt;, &lt;code class=&quot;css-0&quot;&gt;mouseup&lt;/code&gt;, and &lt;code class=&quot;css-0&quot;&gt;click&lt;/code&gt; events to be skipped.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I created some fiddles on &lt;a href=&quot;http://jsfiddle.net/&quot; class=&quot;css-1od09yo&quot;&gt;jsfiddle.net&lt;/a&gt; to test Mobile Safari
behavior.  For your investigative pleasure I have
&lt;a href=&quot;http://jsfiddle.net/hallettj/pgpLA/&quot; class=&quot;css-1od09yo&quot;&gt;an example of a &lt;code class=&quot;css-0&quot;&gt;mouseover&lt;/code&gt; handler that adds elements to the DOM&lt;/a&gt;,
&lt;a href=&quot;http://jsfiddle.net/hallettj/4wjgk/&quot; class=&quot;css-1od09yo&quot;&gt;another that shows a hidden element&lt;/a&gt;,
and &lt;a href=&quot;http://jsfiddle.net/hallettj/m5EXk/&quot; class=&quot;css-1od09yo&quot;&gt;a third that makes no changes to the DOM at all&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[CouchDB Notes]]></title><link>https://sitr.us/2009/09/13/couchdb-notes.html</link><guid isPermaLink="false">https://sitr.us/2009/09/13/couchdb-notes.html</guid><pubDate>Sun, 13 Sep 2009 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Recently I gave a talk at &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://pdxruby.org/&quot; title=&quot;Portland Ruby Brigade&quot; class=&quot;css-1od09yo&quot;&gt;Portland Ruby Brigade&lt;/a&gt; meeting on
&lt;a href=&quot;http://couchdb.apache.org/&quot; title=&quot;CouchDB&quot; class=&quot;css-1od09yo&quot;&gt;CouchDB&lt;/a&gt;, a document-oriented database.  I thought I would share my notes
from that talk.  In some respects this was a followup to &lt;a href=&quot;http://groups.google.com/group/pdxruby/browse_thread/thread/6f8734846d3e16d3&quot; title=&quot;Comparing MongoDB, Tokyo Tyrant, and CouchDB&quot; class=&quot;css-1od09yo&quot;&gt;an earlier talk that
Igal Koshevoy gave comparing various post-relational databases&lt;/a&gt;.
Igal also wrote &lt;a href=&quot;http://groups.google.com/group/pdxruby/browse_thread/thread/7865318fbc65d0d1&quot; title=&quot;Ruby Persistence with CouchDB&quot; class=&quot;css-1od09yo&quot;&gt;some additional notes on my talk&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In summary, some of the distinguishing features of CouchDB are:&lt;/p&gt;&lt;style data-emotion-css=&quot;15rlv7r&quot;&gt;.css-15rlv7r li{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-15rlv7r li{font-size:1rem;}}@media screen and (min-width:768px){.css-15rlv7r li{font-size:1.25rem;}}&lt;/style&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;Schema-less data store stores documents containing arbitrary JSON data.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Incrementally updated map-reduce views provide fast access to data, support powerful data processing, and eliminate lookup penalties for data in large or deeply nested documents.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Map-reduce views - which are again, incrementally updated - provide fast access to aggregate data, such as sums or averages of document attributes.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Schema-less design means no schema migrations are ever required.  And new map-reduce views can be installed with no downtime.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;&amp;quot;Crash-only&amp;quot; design protects data integrity in almost every crash scenario.  No recovery process is required when rebooting a crashed database server.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Lock-free design means that read requests never have to wait for other read or write requests to finish.  Writes are only serialized at the point where data is actually written to the disk.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Integrated, robust master-master replication with automatic conflict handling&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;MVCC, or &amp;quot;optimistic locking&amp;quot;, prevents data loss from multiple writes to the same document from different sources.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;RESTful interface makes it easy to integrate CouchDB with any environment that speaks HTTP.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Documents can contain binary attachments.  Attachment support combined with the HTTP interface means that CouchDB can serve HTML, JavaScript, images, and anything else required to host a web application directly from the database.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;More detailed information on all of the above points can be found in &lt;a href=&quot;http://couchdb.apache.org/docs/overview.html&quot; title=&quot;CouchDB Technical Overview&quot; class=&quot;css-1od09yo&quot;&gt;CouchDB&amp;#x27;s
technical overview&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Some of the downsides:&lt;/p&gt;&lt;ul class=&quot;css-15rlv7r&quot;&gt;&lt;li class=&quot;css-0&quot;&gt;Writes and single-document lookups are slower than other databases due to HTTP overhead and frequent disk access.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;CouchDB optimizes CPU and RAM use by using lots of disk space.  The same data set will take up a lot more space in CouchDB than in other database systems.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;You must create map-reduce views in advance for any queries you want to run.  SQL users are used to processing data at query time; but this is not allowed by the CouchDB design (assuming you are not using temporary views in production, which you should not do.)&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;There is a serious learning curve when learning to think in terms of map-reduce views.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Map-reduce views, though very powerful, are not as flexible as SQL queries.  There may be cases where it is necessary to push data processing to an asynchronous job or to the client.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;CouchDB is a young project and its API is undergoing rapid changes.&lt;/li&gt;&lt;li class=&quot;css-0&quot;&gt;Documentation can be sparse - especially when very new features are involved.  &lt;/li&gt;&lt;/ul&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;ruby-interfaces-to-couchdb&quot; class=&quot;css-1bzbprl&quot;&gt;Ruby Interfaces to CouchDB&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I also talked about some of the high-level interfaces to CouchDB that are
available for Ruby.  As ActiveRecord did for SQL, the idea behind these
libraries is to abstract away as much of the database behavior as possible
without sacrificing the powerful features that CouchDB provides.  The term
&amp;quot;ORM&amp;quot; does not quite apply to CouchDB because it is not relational.  The term I
am using for the time being is &amp;quot;object-document mapping&amp;quot;.  The code examples I
showed are all available in &lt;a href=&quot;http://gist.github.com/161472&quot; title=&quot;ODM Code Examples&quot; class=&quot;css-1od09yo&quot;&gt;a gist&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Sadly I don&amp;#x27;t think I can say that any of these libraries are production ready
as is.  If you use one expect to write some patches as you go.  That said I
think that all three show some exciting potential.  And they all provide a
better starting point for your CouchDB project than writing your own ODM or
using a low-level interface.  I plan to submit a few patches to CouchPotato as
I get to know it better.  With some more help I imagine we can turn one or more
of these interfaces into a nicely polished library.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The winner in my mind is &lt;a href=&quot;http://upstream-berlin.com/2008/10/27/couch-potato-unleashed-a-couchdb-persistence-layer-in-ruby/&quot; title=&quot;CouchPotato&quot; class=&quot;css-1od09yo&quot;&gt;CouchPotato&lt;/a&gt;.  The philosophy behind CouchPotato is
to do things differently than ActiveRecord does.  Though it does borrow
features from ActiveRecord, for example dirty attribute tracking, life cycle
callbacks, and validations.  The biggest innovation in CouchPotato in my
opinion is the extensible system for defining views.  As with the other
libraries, support for declaring simple views is built in:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;But similar shortcuts for more sophisticated types of views can be added by
creating new view classes and passing a &lt;code class=&quot;css-0&quot;&gt;:type&lt;/code&gt; option to the &lt;code class=&quot;css-0&quot;&gt;view&lt;/code&gt;
declaration method.  For example, it might be possible to declare views like
this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;My runner-up is &lt;a href=&quot;http://github.com/jchris/couchrest&quot; title=&quot;CouchRest&quot; class=&quot;css-1od09yo&quot;&gt;CouchRest&lt;/a&gt;.  CouchRest is a widely used low-level interface
to CouchDB.  But it also includes a high-level interface called
CouchRest::ExtendedDocument.  A neat feature of this library is that you can
declare a different database to use for each model.  It also supports declaring
simple views with dynamic methods for querying those views:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://github.com/georgepalmer/couch_foo&quot; title=&quot;CouchFoo&quot; class=&quot;css-1od09yo&quot;&gt;CouchFoo&lt;/a&gt; is another strong contender.  The goal of CouchFoo is to provide
an API that is as close to ActiveRecord&amp;#x27;s as possible.  The project may even be
porting a large amount of ActiveRecord code for this purpose.  The intention is
to make migrating to CouchDB as painless as possible.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An interesting feature is that CouchFoo will create views automatically on
demand.  For example this query will automatically create a view that indexes
Post documents by title:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;On-demand view creation could be convenient.  But my instinct is that it is a
bad thing to do.  Adding a new view to a large database comes with an expensive
initial build step.  It seems to me that that type of thing should only be done
explicitly.&lt;/p&gt;&lt;h2 id=&quot;testimonials&quot; class=&quot;css-1bzbprl&quot;&gt;Testimonials&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;I mentioned a case where one team found that they got great performance
improvements by pushing some data reporting tasks from a SQL database to
CouchDB.  That story was written up in a series of blog posts.  An explanation
of why this team went with CouchDB is presented in &lt;a href=&quot;http://johnpwood.net/2009/07/10/couchdb-views-%E2%80%93-the-advantages/&quot; title=&quot;CouchDB: Views - The Advantages&quot; class=&quot;css-1od09yo&quot;&gt;part 3 of that series&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is &lt;a href=&quot;http://wiki.apache.org/couchdb/CouchDB_in_the_wild&quot; title=&quot;Sites using CouchDB in production&quot; class=&quot;css-1od09yo&quot;&gt;a list on the CouchDB Wiki of sites that are currently using CouchDB
in production&lt;/a&gt;.  A couple of notable examples not on the list that
have used CouchDB are the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2009/speakers/endafarrell&quot; title=&quot;The BBC on CouchDB&quot; class=&quot;css-1od09yo&quot;&gt;BBC&lt;/a&gt; and possibly &lt;a href=&quot;http://code.google.com/p/couchdb-lounge/&quot; title=&quot;CouchDB Lounge&quot; class=&quot;css-1od09yo&quot;&gt;Meebo&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Of note for Ubuntu fans:  Canonical is working on a project called &lt;a href=&quot;http://www.kryogenix.org/days/2009/09/03/desktop-couch-irc-talk&quot; title=&quot;Desktop Couch&quot; class=&quot;css-1od09yo&quot;&gt;Desktop
Couch&lt;/a&gt; which will be installed by default in Karmic Koala.  The idea is to
create a portable store for stuff like browser bookmarks, contacts, music
playlists and ratings, and so on.  There are already plugins to allow Firefox
and Evolution to store &lt;a href=&quot;http://www.kryogenix.org/days/2009/07/06/firefox-bookmarks-in-couchdb&quot; title=&quot;Firefox and CouchDB&quot; class=&quot;css-1od09yo&quot;&gt;bookmarks&lt;/a&gt; and &lt;a href=&quot;http://blogs.gnome.org/rodrigo/2009/06/19/couchdb-contacts-in-evolution&quot; title=&quot;Evolution and CouchDB&quot; class=&quot;css-1od09yo&quot;&gt;contact data&lt;/a&gt; in CouchDB.  Desktop
Couch will provide CouchDB databases for every user to store this information,
and will include tools for &amp;quot;pairing&amp;quot; computers on the same network.  Desktop
Couch will use CouchDB&amp;#x27;s built-in replication features to automatically
replicate data between paired computers; so you will get the same bookmarks and
contacts on all of your computers.  This will all integrate with &lt;a href=&quot;https://ubuntuone.com/&quot; title=&quot;Ubuntu One&quot; class=&quot;css-1od09yo&quot;&gt;Ubuntu One&lt;/a&gt;
too.  Desktop Couch will be able to replicate your data to Ubuntu One&amp;#x27;s servers
so that you can replicate that data back down to computers on a different
network.&lt;/p&gt;&lt;h2 id=&quot;exploring-further&quot; class=&quot;css-1bzbprl&quot;&gt;Exploring Further&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The best resource for learning more about CouchDB is probably CouchDB: The
Definitive Guide.  This is a book that J. Chris Anderson, Jan Lehnardt, and
Noah Slater are writing for O&amp;#x27;Reilly.  It is still a work in progress, but the
&lt;a href=&quot;http://books.couchdb.org/relax/&quot; title=&quot;CouchDB: The Definitive Guide&quot; class=&quot;css-1od09yo&quot;&gt;latest draft&lt;/a&gt; is available online.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For API reference my source is the &lt;a href=&quot;http://wiki.apache.org/couchdb/&quot; title=&quot;CouchDB Wiki&quot; class=&quot;css-1od09yo&quot;&gt;CouchDB Wiki&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Finally, as the CouchDB developers will tell you, the most up-to-date reference
for the latest CouchDB features is the included test suite.  This is a set of
tests written in JavaScript that you can run from CouchDB&amp;#x27;s web interface to
verify that your build of the latest SVN checkout is working correctly.  These
tests are run externally and access the database server via its HTTP API; so
you don&amp;#x27;t have to know any nitty gritty Erlang stuff to understand what the
tests are doing.  When any changes to the API are introduced this test suite is
updated accordingly.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to install Haskell "Batteries Included" Platform on Ubuntu Jaunty]]></title><link>https://sitr.us/2009/07/02/how-to-install-haskell-platform-on-ubuntu-jaunty.html</link><guid isPermaLink="false">https://sitr.us/2009/07/02/how-to-install-haskell-platform-on-ubuntu-jaunty.html</guid><pubDate>Thu, 02 Jul 2009 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Just for kicks I thought I would take another shot at some Haskell programming.
To get all of the common libraries and the automated package installer, cabal,
I set up the &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://hackage.haskell.org/platform/&quot; class=&quot;css-1od09yo&quot;&gt;Haskell Platform&lt;/a&gt;. Here is how I did it.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Ubuntu Jaunty includes a package for the Haskell compiler, ghc, at version 6.8.
The Haskell Platform installer will roll its eyes at you if you try to proceed
with this version of ghc. So the first step is to install ghc 6.10.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Paste these lines into &lt;code class=&quot;css-0&quot;&gt;/etc/apt/sources.list.d/haskell.list&lt;/code&gt;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To get the key to verify packages from that PPA, run this optional command:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Then update your package list and install Haskell:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The Haskell Platform website does not list a package for Ubuntu yet. So
&lt;a href=&quot;http://hackage.haskell.org/platform/&quot; class=&quot;css-1od09yo&quot;&gt;download the source installer&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Before you run the installer you will want to install the necessary build
dependencies:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Please leave a comment if you discover that I have left out any dependencies.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To perform the final installation step you will also need to have checkinstall
installed:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Unpack the source installer wherever you like:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Finally &lt;code class=&quot;css-0&quot;&gt;cd&lt;/code&gt; into the installer directory and run the generic installation
procedure:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This will build and install a deb package called &lt;code class=&quot;css-0&quot;&gt;haskell-platform&lt;/code&gt;.  If you
ever want to remove Haskell Platform just uninstall that package.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If all of the above worked, you should be good to go. You compile Haskell code
with &lt;code class=&quot;css-0&quot;&gt;ghc&lt;/code&gt;. You can run an interactive read-eval-print-loop with &lt;code class=&quot;css-0&quot;&gt;ghci&lt;/code&gt;. And
you can install Haskell libraries with &lt;code class=&quot;css-0&quot;&gt;cabal&lt;/code&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Database Queries the CouchDB Way]]></title><link>https://sitr.us/2009/06/30/database-queries-the-couchdb-way.html</link><guid isPermaLink="false">https://sitr.us/2009/06/30/database-queries-the-couchdb-way.html</guid><pubDate>Tue, 30 Jun 2009 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://couchdb.apache.org/&quot; class=&quot;css-1od09yo&quot;&gt;CouchDB&lt;/a&gt; is a document-oriented database. It has no rows or tables. Instead
CouchDB is a collection of JSON documents. It uses a map-reduce pattern to
index data. Queries in CouchDB pull data from what are essentially stored
procedures called views. A view is made up of a map function and optionally a
reduce function.  Ninety percent of the time all you need is the map function,
so I will focus on map-only views here.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A map function is a JavaScript function that takes a single document as an
argument and emits any number of key/value pairs. Both the key and the value
can be any JSON value you choose. The map function is run on every document in
the database individually and the emitted key/value pairs are used to construct
an index of your data.&lt;/p&gt;&lt;style data-emotion-css=&quot;1bzbprl&quot;&gt;.css-1bzbprl{font-family:inherit;font-weight:700;line-height:1.25;margin:0;margin-bottom:0.25rem;font-size:1.875rem;margin-top:0.5rem;color:var(--theme-ui-colors-heading,#000);}@media screen and (min-width:640px){.css-1bzbprl{font-size:2.25rem;}}@media screen and (min-width:768px){.css-1bzbprl{font-size:3rem;}}&lt;/style&gt;&lt;h2 id=&quot;a-simple-example&quot; class=&quot;css-1bzbprl&quot;&gt;A Simple Example&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Imagine you have a database with user records and you want a view of those
records using the last name of each user as keys.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The above map function will produce and index something like the one below.
Because &lt;code class=&quot;css-0&quot;&gt;doc&lt;/code&gt; is used as a value for each entry the entire content of each JSON
document will be accessible as the indexed values.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Notice that the map function checks each document for a &lt;code class=&quot;css-0&quot;&gt;last_name&lt;/code&gt; attribute
before emitting a key/value pair. There may be documents in the database that
are not user records. By performing that check the view excludes any
non-user-record documents from the resulting index.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If you include the &lt;a href=&quot;http://github.com/halorgium/couchdb/blob/2c5f780d8284be5e2cb39f7f61acc5ef8d6fb50d/share/www/script/couch.js&quot; class=&quot;css-1od09yo&quot;&gt;couch.js&lt;/a&gt; library in a web page you can create
client-side queries to pull data from CouchDB over HTTP.  The function below
will fetch all of the user records from your database by returning the &lt;code class=&quot;css-0&quot;&gt;value&lt;/code&gt;
of each key/value pair emitted by the view above.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Map functions operate on one document at a time and cannot access data from
other documents. The advantage of this is that the functions can process data
in any order and can run on any piece of a data set independent of the rest of
the set.  CouchDB builds static indexes from the output of view map functions
so that queries against those views will run quickly.  When any documents
change CouchDB can incrementally rebuild the indexes for just those documents
without having to rebuild entire indexes from scratch.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The CouchDB design gets you great performance on large data sets. But it means
that you cannot pass dynamic parameters to your map function when you run a
query. You cannot ask for it to emit only user records with a given last name
unless you want to maintain a special view for that particular last name. In
most cases it is not practical to build separate views for every query that you
might want to run someday. So what you can do is to run a query against the
general purpose view above and request only key/value pairs that match a
particular key.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This client code creates a query that requests data from the &lt;code class=&quot;css-0&quot;&gt;last_names&lt;/code&gt; view
with a &lt;code class=&quot;css-0&quot;&gt;key&lt;/code&gt; parameter. CouchDB will only send back key/value pairs with keys
that match the &lt;code class=&quot;css-0&quot;&gt;key&lt;/code&gt; parameter. In this case the query will return all user
records with last names matching the &lt;code class=&quot;css-0&quot;&gt;last_name&lt;/code&gt; argument.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In a more advanced case you may want to take the first few letters of a last
name and look up user records that match.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Data returned by a query is always sorted by key. In the case where the keys
are strings the sorting will be lexicographic.  The &lt;code class=&quot;css-0&quot;&gt;startkey&lt;/code&gt; and &lt;code class=&quot;css-0&quot;&gt;endkey&lt;/code&gt;
parameters restrict the results of the query to key/value pairs that fall in
the given range according to CouchDB&amp;#x27;s sort order.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When the above function is given the query string &amp;quot;Ha&amp;quot;, it will fetch documents
with keys sorted after &amp;quot;Ha&amp;quot; lexicographically, e.g. &amp;quot;Hallett&amp;quot;, &amp;quot;Hathaway&amp;quot;, and
&amp;quot;Hazzold&amp;quot;. The &lt;code class=&quot;css-0&quot;&gt;endkey&lt;/code&gt; is created by appending &amp;quot;\u9999&amp;quot; to the &lt;code class=&quot;css-0&quot;&gt;startkey&lt;/code&gt;.
&amp;quot;\u9999&amp;quot; is a unicode character that comes after most other characters in
lexicographic order and that is unlikely to appear in a data set. Effectively
the string &amp;quot;Ha\u9999&amp;quot; sorts after every other string that begins with &amp;quot;Ha&amp;quot;, but
before any string that starts with &amp;quot;Hb&amp;quot;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Note that CouchDB uses the &lt;a href=&quot;http://www.unicode.org/unicode/reports/tr10/&quot; class=&quot;css-1od09yo&quot;&gt;Unicode Collation Algorithm&lt;/a&gt; to sort strings.
Sorting comes out differently than you may be used to if your are accustomed to
the ASCII way. UCA collation is intended to mimic the order of strings you
would see in a dictionary. For example, two strings that differ only in case
will appear together in sorted order. The lower-case string will appear
immediately before the upper-case string. So if you force your &lt;code class=&quot;css-0&quot;&gt;startkey&lt;/code&gt; to
lower-case and your &lt;code class=&quot;css-0&quot;&gt;endkey&lt;/code&gt; to upper-case you will get case-insensitive
matches. See the &lt;a href=&quot;http://wiki.apache.org/couchdb/View_collation&quot; class=&quot;css-1od09yo&quot;&gt;CouchDB wiki&lt;/a&gt; for more details.&lt;/p&gt;&lt;h2 id=&quot;search-by-keyword&quot; class=&quot;css-1bzbprl&quot;&gt;Search by Keyword&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Indexing text content can be a hard problem because you need a large index if
you want to query data by arbitrary keywords or substrings.  Fortunately
CouchDB excels at managing large indexes.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Here is a map function that creates an index of all the words that appear in
the text field of every document in a database.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The code splits text on word boundaries stripping out non-alphanumeric
characters. It runs the resulting list of tokens through a unique filter so
that only one index key is produced for each word in the text of a single
document.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This is an example of a view that emits more than one key/value pair for each
document. The values in each pair are the same for the same document. But a
different key is recorded for each word in the document&amp;#x27;s text attribute. The
index that would be created by this view for a document with the text &amp;quot;Live
long and prosper&amp;quot; is below.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To look up documents that contain a given keyword you just need to create a
query with that keyword as the &lt;code class=&quot;css-0&quot;&gt;key&lt;/code&gt; parameter. But what if you want to look
for documents that contain a list of keywords? You could create index keys for
every combination of words in each document. But that index would grow
exponentially and might get to be unreasonably large. A better way might be to
pick one keyword to perform your query and then to use client code to select
the documents that match all of the keywords out of the results returned by
CouchDB.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When you want more dynamic data processing than you can get with pure-CouchDB
views, some client-side processing can make up the difference nicely. From a
perspective of deploying applications leveraging your users&amp;#x27; CPU cycles for
data processing can really help getting your application to scale.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There is another approach to full text search documented &lt;a href=&quot;http://wiki.apache.org/couchdb/Full_text_index_with_view&quot; class=&quot;css-1od09yo&quot;&gt;on the CouchDB
wiki&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;search-by-substring&quot; class=&quot;css-1bzbprl&quot;&gt;Search by Substring&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Maybe keyword search isn&amp;#x27;t good enough. Maybe you need to be able to search for
occurrences of any substring in a data set.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For each document, this map function emits a key/value pair for every possible
substring that runs to the end of the document&amp;#x27;s text. The idea is that the
beginning of any substring in the data set can be lined up with the beginning
of one of these keys.  Here is an example of the index created for a document
with the text &amp;quot;Hello, world!&amp;quot;:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The substring that you want to search for can begin at any character within
some document&amp;#x27;s text. It may or may not run until the end of that document&amp;#x27;s
text field. We have an index with the beginning characters for every possible
substring. So we can create a query that asks for a key range that encompasses
both the given substring and a substring that runs all the way to the end of
the document text.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;CouchDB does not just sort data when responding to queries. In its internal
representation indexes are always sorted by key.  So a query with a key range
targets a contiguous block of data from the database.  Because of that CouchDB
can serve up a key range very efficiently.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Just as with the keyword search, if you want to search for documents that match
a list of substrings then get the matches for one of the substrings from
CouchDB and use client code to select results that contain all of the rest of
the given substrings.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;If the text of your documents tends to be very long you can avoid a lot of
really long keys by limiting the lengths of substring keys in your view. In
that case make sure to truncate the key parameters in your queries so that they
are not longer than the index keys.&lt;/p&gt;&lt;h2 id=&quot;appendix-helper-function-definitions&quot; class=&quot;css-1bzbprl&quot;&gt;Appendix: Helper Function Definitions&lt;/h2&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Some of the functions that I used in the examples above are not built into
JavaScript or &lt;a href=&quot;http://github.com/halorgium/couchdb/blob/2c5f780d8284be5e2cb39f7f61acc5ef8d6fb50d/share/www/script/couch.js&quot; class=&quot;css-1od09yo&quot;&gt;couch.js&lt;/a&gt;. Here are definitions of those functions for
reference.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Given an attribute name, &lt;code class=&quot;css-0&quot;&gt;dot&lt;/code&gt; returns a function that when given an object
returns the value of the specified attribute. This function is used in examples
above to get the &lt;code class=&quot;css-0&quot;&gt;value&lt;/code&gt; attributes of rows fetched by CouchDB queries.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Map_%28higher-order_function%29&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;map&lt;/code&gt;&lt;/a&gt; is an Array method that given a function that takes a single
argument returns a new array formed by applying the given function to every
element of the original array in turn.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Reduce_%28higher-order_function%29&quot; class=&quot;css-1od09yo&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;reduce&lt;/code&gt;&lt;/a&gt; is an Array method that given an initial value and a
function that takes two arguments returns a single value produced by applying
the given function to every element of the array in turn with the value from
the previous function invocation and returning the last result.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;select&lt;/code&gt; is an Array method that given a test function returns a new array made
up of only elements in the original array that pass the test.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;code class=&quot;css-0&quot;&gt;uniq&lt;/code&gt; is an Array method that returns a new array with any duplicate values
from the original array removed.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to use RSpec to describe a Sinatra application]]></title><link>https://sitr.us/2008/07/29/how-to-use-rspec-to-describe-a-sinatra-application.html</link><guid isPermaLink="false">https://sitr.us/2008/07/29/how-to-use-rspec-to-describe-a-sinatra-application.html</guid><pubDate>Tue, 29 Jul 2008 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;This information was written a long time ago and has become pretty outdated.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://sinatrarb.com/&quot; class=&quot;css-1od09yo&quot;&gt;Sinatra&lt;/a&gt; is a fun little web application microframework. Recently I
started working on an application using Sinatra - and since I am
working on good programming habits, before I dove into any coding I
sat down to work out how to write specs for a Sinatra application.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Sinatra comes bundled with support for &lt;a href=&quot;http://chneukirchen.org/repos/testspec/README&quot; class=&quot;css-1od09yo&quot;&gt;test/spec&lt;/a&gt;: a spec framework
that builds on top of Rail&amp;#x27;s own Test::Unit to provide support for
writing specs. Which is a really neat idea. But I have been using
&lt;a href=&quot;http://rspec.info/&quot; class=&quot;css-1od09yo&quot;&gt;RSpec&lt;/a&gt; for my other work, and I wanted to continue doing so.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It turns out that RSpec takes a little bit of manual work to get
going with Sinatra. I read a &lt;a href=&quot;http://www.gittr.com/index.php/archive/sinatra-rspec-integration-without-a-patch-with-examples/&quot; class=&quot;css-1od09yo&quot;&gt;helpful article on gittr.com&lt;/a&gt; that
pointed me in the right direction. The article advised me to add these
lines to my spec files:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The first line loads your application; the second line loads RSpec;
the third loads an RSpec-Test::Unit compatibility layer; and the
fourth loads Sinatra&amp;#x27;s test helpers, which are written for
Test::Unit.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Contrary to Sinatra&amp;#x27;s instructions for writing tests, you
want to avoid loading &amp;#x27;sinatra/test/spec&amp;#x27;, which defines Sinatra&amp;#x27;s
helper methods for test/spec, because that would load test/spec itself
which conflicts with RSpec.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Those instructions mostly worked. I could write and run specs. But I
had trouble with matchers. For example, this example:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Would give an error message like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Which I&amp;#x27;m sure you can imagine is pretty annoying.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;It was easy enough to identify &amp;#x27;sinatra/test/unit&amp;#x27; as the root of the
problem. When I removed that line RSpec&amp;#x27;s matchers worked fine; but
then I didn&amp;#x27;t get Sinatra&amp;#x27;s test helpers, which make spec-writing much
easier. So that wasn&amp;#x27;t a great solution either.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Examining Sinatra&amp;#x27;s code, I found that all &amp;#x27;sinatra/test/unit&amp;#x27; does is
to load &amp;#x27;sinatra/test/methods&amp;#x27; - the actual helper methods - and mixes
them into Test::Unit::TestCase. So I bypassed &amp;#x27;sinatra/test/unit&amp;#x27; by
copying and adapting some code from it to make the top of my spec file
look like this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Since that is a fair amount of setup, I moved all of it into a
separate file called spec_helper.rb, which I loaded into my actual
spec files. Because I am weird enough to write a Sinatra application
that is split into multiple files and multiple spec files.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Anyway, now my specs run just as they should, with Sinatra&amp;#x27;s helpers
and everything:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The next challenge was to write specs for Sinatra helpers. Although
Sinatra actions and helpers generally appear in the outermost
namespace, the DSL methods that define them actually bind the helpers
to Sinatra::EventContext. You can&amp;#x27;t invoke helper methods directly
from an example context; you have to create an instance of
Sinatra::EventContext and send the method call to that - much the same
way Rails instantiates a subclass of ActionController::Base to handle a
controller action. Here is the code you will want in your example
groups:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A &lt;code class=&quot;css-0&quot;&gt;body=&lt;/code&gt; method has to be defined on the response mock to prevent an
error. But it doesn&amp;#x27;t have to actually do anything. With that setup
code in place, you can do this:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;and write something like this in your application:&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;And now you have a reasonably complete speccing setup. There are still
a couple of issues though. For one thing, the spec helpers are missing
a much needed &lt;code class=&quot;css-0&quot;&gt;assigns[]&lt;/code&gt; method. As it stands there is no good way to
pry apart the behavior of an action if there is no convenient method
call to stub. You can only define the parameters that are passed to
it, and read response. On the upside, this does help to enforce good
behavior-driven development.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The other issue is more of an annoyance than a serious problem. It
seems that somewhere in all of this there are one or two
&lt;code class=&quot;css-0&quot;&gt;method_missing&lt;/code&gt; definitions that bounce calls back and forth. If call
a method that is not defined, you generally won&amp;#x27;t get an &amp;quot;undefined
method&amp;quot; error, you will get a &amp;quot;stack level too deep&amp;quot; error
instead. This is particularly unhelpful because it does not tell you
what class received the undefined method, or which method is
undefined. So a little extra manual stack tracing is required when
this happens.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[International Phonetic Alphabet]]></title><link>https://sitr.us/2007/10/02/international-phonetic-alphabet.html</link><guid isPermaLink="false">https://sitr.us/2007/10/02/international-phonetic-alphabet.html</guid><pubDate>Tue, 02 Oct 2007 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;In &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;/2007/09/24/anatomy-of-a-syllable.html&quot; class=&quot;css-1od09yo&quot;&gt;last week&amp;#x27;s post&lt;/a&gt; I provided phonetic
transcriptions of some example words using the International Phonetic
Alphabet, or IPA for short. I thought it would be helpful to follow
that up with some information about what the IPA is, and how to read
it. And as a bonus, after learning about IPA transcription you will be
able to better read pronunciation guides on Wikipedia.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;You have probably seen many phonetic transcriptions before -
especially as pronunciation guides in dictionaries. In dictionaries it
is common to see a transcription convention that uses English spelling
conventions to represent sounds. For example, the word &amp;quot;elucidate&amp;quot;
might be transcribed as (ee-LOO-suh-date). That system is handy
because it is immediately familiar to anyone who has experience
reading stuff in English. But it has drawbacks too. There are a few
main problems that are especially important for linguists; there are
lots of linguists all over the world who are used to languages with
entirely different spelling conventions. For example, the sound in
English that is represented as &amp;quot;y&amp;quot; - the consonant, not the vowel - is
written in Icelandic as &amp;quot;j&amp;quot;. There are also a lot of languages with
sounds that just don&amp;#x27;t exist in English. Linguists need to be able to
transcribe those sounds; but since they don&amp;#x27;t exist in English there
is no spelling convention to represent them. In fact there is no one
language with enough spelling conventions to represent every sound in
every language in the world. And finally, English spelling is
ambiguous, as is the spelling of almost any language. That is already
demonstrated by the need to add a note to distinguish &amp;quot;y&amp;quot; the
consonant from &amp;quot;y&amp;quot; the vowel or any of the other vowel sounds
represented by &amp;quot;y&amp;quot;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To solve all these problems, a group of linguistics developed the
International Phonetic Alphabet in the late nineteenth century. The
first standardized version was created in 1888. But it has been
revised somewhat since then. The purpose of IPA is to provide a
standard set of symbols that are used to represent sounds so that the
same symbols always represent the sounds, even to people from
different language backgrounds. Using the IPA it is theoretically
possible to represent every sound in every language in the
world. Though occasionally a new language is discovered with a new
sound that we didn&amp;#x27;t know anything about before, which has to be
quickly added to the IPA.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;IPA was originally developed by French and British linguists, so it
uses characters derived from the Latin alphabet and symbols are
matched to sounds in a ways that are generally familiar to
Europeans. But even in the Western world people have different ideas
about how to spell things, so the IPA adopts some different phonetic
conventions from different languages. For example, I mentioned earlier
that the sound represented by &amp;quot;y&amp;quot; in English is spelled &amp;quot;j&amp;quot; in, among
others languages, Icelandic; and in fact the symbol for that sound in
IPA is [j]. Here is &lt;a href=&quot;http://www.linguiste.org/phonetics/ipa/chart/&quot; class=&quot;css-1od09yo&quot;&gt;the complete IPA chart in its most recent
revision&lt;/a&gt;. And by the way: in IPA, the word
&amp;quot;elucidate&amp;quot; is transcribed [i&amp;#x27;lu.sə.deɪt].&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The sound an IPA symbol represents is described by the &lt;em class=&quot;css-0&quot;&gt;place of
articulation&lt;/em&gt; of the sound, an the &lt;em class=&quot;css-0&quot;&gt;manner of articulation&lt;/em&gt;. When you
voice a consonant you, you press your tongue against the roof of your
mouth, or in some cases you press your lips together or put the tip of
your tongue between your teeth. The exact spot on where you press your
tongue is the place of articulation. For example, when you say [t] you
press the tip of your tongue against a spot towards the front of your
mouth called the alveolar ridge; whereas when you say [k] you press
the back of your tongue against your soft palate, which linguists call
the velum. [t] and [k] have the same manner of articulation - they are
both plosives, which means that they are articulated by completely
stopping air from flowing out of the mouth for a moment. But they have
different places of articulation. Early on in a linguist&amp;#x27;s career he
learns all about the structures of the mouth, throat, and nose to
learn how each can be used to produce various sounds. I will explain
some of the details in future articles; in the meantime it would
probably be easiest to look up IPA symbols using &lt;a href=&quot;http://en.wikipedia.org/wiki/IPA_Chart_for_English&quot; class=&quot;css-1od09yo&quot;&gt;this IPA chart on
Wikipedia&lt;/a&gt;, which is specially designed for
English speakers and provides example words to illustrate each sound.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Wikipedia provides the transcription for the English &amp;quot;r&amp;quot; as [ɹ]. This
is the most technically correct transcription, since in IPA [r]
represents a rolled &amp;quot;r&amp;quot;, which is heard in Spanish. In linguistics
speak, [r] is a trill and [ɹ] is a liquid. However, English doesn&amp;#x27;t
have a trill, so people transcribing English often use [r] instead of
[ɹ] because the former is more familiar - and easier to type. It is
considered acceptable to make substitutions like that in cases where
readers are unlikely to be confused by the switch. So when I
transcribe an English word using [r] instead of [ɹ] I&amp;#x27;m not actually
trying to make you practice pronouncing trills.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;As an aside, the rolled &amp;quot;r&amp;quot; in French is not the same sound as the
rolled &amp;quot;r&amp;quot; in Spanish. The French &amp;quot;r&amp;quot; is transcribed [ʀ]. Both sounds
are trills, so they share the same manner of articulation; but they
have different places of articulation. [ɹ] is alveolar, meaning that
to pronounce it the tip of the tongue is positioned on the alveolar
ridge, in the same spot it is placed when pronouncing [t]. The sound
is produced by vibrating the tongue against the roof of the mouth,
which is what makes it a trill. To pronounce [ʀ], the back of the
tongue is placed against the uvula and vibrates against that.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;To make the alphabet more flexible, IPA employs a number of
diacritics. Diacritics are small marks placed above or below a
character. In IPA they are used to describe characteristics of a sound
that differ slightly from the sound usually represented by a bare
character. Last week I talked about consonants that are used as
syllable nuclei, which are called syllabic consonants. Syllabicity is
considered a phonological feature, so it is indicated with a
diacritic. For example, the syllabic version of [n] is [n̩].&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Another example of a diacritic is ʰ, which is used to indicate
aspiration. Did you know that the letter &amp;quot;p&amp;quot; in English is used to
represent two different sounds? The &amp;quot;p&amp;quot; sounds in &amp;quot;pull&amp;quot; and &amp;quot;stop&amp;quot;
are slightly different. You can tell if you put your hand in front of
your mouth, less than an inch away, and say each word. You will feel a
puff of air on your hand when you say the &amp;quot;p&amp;quot; in &amp;quot;pull&amp;quot;; there is a
puff when you say &amp;quot;stop&amp;quot; too, but it is not nearly as strong. A sound
that makes that strong puff of air is said to be &lt;em class=&quot;css-0&quot;&gt;aspirated&lt;/em&gt;, and is
marked with a diacritic that looks like a superscripted &amp;quot;h&amp;quot;. So the
IPA representation for an aspirated &amp;quot;p&amp;quot; is /pʰ/ and an unaspirated &amp;quot;p&amp;quot;
is simply /p/.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Even though they are technically different sounds, English speakers
treat /p/ and /pʰ/ as being the same. So they are written with the
same letter. And in IPA they are usually both transcribed as [p] for
simplicity. But on occasion it is important to have the ability to
distinguish the two. The difference between sounds that are exactly
the same and sounds that are treated as the same by language speakers
is a very important consideration in linguistics - and it has to do
with the sudden switch from square brackets to slashes surrounding the
transcriptions above. I will talk about that in a future article too.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Anatomy of a Syllable]]></title><link>https://sitr.us/2007/09/24/anatomy-of-a-syllable.html</link><guid isPermaLink="false">https://sitr.us/2007/09/24/anatomy-of-a-syllable.html</guid><pubDate>Mon, 24 Sep 2007 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The syllable is a constant feature in every spoken language in the
world. Each language has its own rules about what kinds of syllables
are allowed, and what kinds aren&amp;#x27;t - but the general structure is the
same everywhere.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;A syllable has as many as three parts: &lt;em class=&quot;css-0&quot;&gt;onset&lt;/em&gt;, &lt;em class=&quot;css-0&quot;&gt;nucleus&lt;/em&gt;, and
&lt;em class=&quot;css-0&quot;&gt;coda&lt;/em&gt;. The onset and the coda are consonants, or consonant clusters,
that appear at the beginning and the end of the syllable
respectively. The nucleus forms the the core of the syllable; it is
most often a vowel, or a combination of vowels - but there are many
exceptions to that. If you examine enough languages you can find
almost every kind phone used as a syllable nucleus. In the word &amp;quot;far&amp;quot;,
[f] is the syllable onset, [a] is the nucleus, and [r] the coda. If a
coda is present in a syllable, the nucleus and the coda form a single
unit called a &lt;em class=&quot;css-0&quot;&gt;rhyme&lt;/em&gt;; otherwise the nucleus makes up the rhyme by
itself. Looking at &amp;quot;far&amp;quot; again, [ar] forms the rhyme. A syllable does
not necessarily have to have an onset or a coda - depending on the
language - but a nucleus is always present.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Even in English, syllable nuclei are not restricted to vowels. For
example, in the monosyllabic word, &amp;quot;hmm&amp;quot;, the syllable nucleus is [ṃ],
which is a consonant but is more specifically a &lt;em class=&quot;css-0&quot;&gt;nasal&lt;/em&gt;. Another
nasal, [n], can be seen as a syllable nucleus in the word &amp;quot;isn&amp;#x27;t&amp;quot;,
which is transcribed into the &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/International_Phonetic_Alphabet&quot; class=&quot;css-1od09yo&quot;&gt;International Phonetic Alphabet (IPA)&lt;/a&gt;
as [ɪzzṇt]. In this case there are two syllables, and [ṇ] forms the
nucleus of the second syllable.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The small dot underneath the characters ṇ and ṃ indicates that the
sound represented is a &lt;em class=&quot;css-0&quot;&gt;syllabic consonant&lt;/em&gt;, which is any consonant
that forms a syllable nucleus. Vowels are not marked with the same
diacritic because they are always considered to be syllabic. Usually
syllabicity is marked in IPA with a vertical stroke under the character
instead of a dot; but in this case a dot was as close as I could get.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Anyone following closely may notice that it is difficult to decide
exactly how to divide &amp;quot;isn&amp;#x27;t&amp;quot; into syllables. It seems like it should
be either [&amp;#x27;ɪz.ṇt], or [&amp;#x27;ɪ.zṇt], where the dot (.) represents a
syllable boundary and the apostrophe (&amp;#x27;) represents the beginning of a
stressed syllable; but it is tricky to figure out whether the [z] is
the coda of the first syllable or the onset of the second
syllable. That is because in the [z] in &amp;quot;isn&amp;#x27;t&amp;quot; demonstrates
&lt;em class=&quot;css-0&quot;&gt;ambisyllabicity&lt;/em&gt;, a common feature in English that I will write about
in a future article. The short explanation is that [z] is both an
onset and a coda, which is why I transcribed the word with two
[z]&amp;#x27;s. Anyway, in both analyses [ṇ] forms the nucleus of the second
syllable, so we don&amp;#x27;t need to worry about the placement of [z] for
now.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are also arguably cases where a &lt;em class=&quot;css-0&quot;&gt;liquid&lt;/em&gt; forms the nucleus of a
syllable in English. The liquids in English are [l] and [r]. Consider
the word &amp;quot;sir&amp;quot; and the second syllable of &amp;quot;apple&amp;quot;. If it is the case
that the liquids in these syllables are the nuclei, then the words
would be transcribed as [sṛ] and [æppḷ] respectively. However, not
everybody agrees with that interpretation, and so these words are
often analyzed as [sər] and [æppəl]. That would make the syllable
nuclei [ə], which is a vowel. And by the way, the [p] in &amp;quot;apple&amp;quot; is
also ambisyllabic.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The reason vowels are so likely to form syllable nuclei is that they
are the most &lt;em class=&quot;css-0&quot;&gt;sonorous&lt;/em&gt; sounds available in spoken language. It is a
general rule that syllable nuclei are formed by sonorous
phones. Liquids and nasals are right behind vowels in that they are
more sonorous than any other type of consonant. But it is possible to
find syllable nuclei in other languages that are considerably less
sonorant than any of the above. For example, there is at least one
&lt;a href=&quot;http://en.wikipedia.org/wiki/Berber_languages&quot; class=&quot;css-1od09yo&quot;&gt;Berber language&lt;/a&gt; that contains syllables like [tḳt], where the
nucleus of the syllable is [ḳ].&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;The onset and coda are always made up of consonants. Many languages
have strict rules about how many consonants can appear, and what sort
of order they appear in. English is relatively lax in this respect, so
we can see syllables with several consonants clustered together in
both positions. For example, &amp;quot;scrumptious&amp;quot;, transcribed as
[&amp;#x27;skrʌmp.ʃəs], has three distinct consonant phones in the onset and
two in the coda of the first syllable. But notice that you would never
see a word in English like [&amp;#x27;rksʌpm.ʃəs], which would probably be
spelled &amp;quot;rksupmtious&amp;quot;. Try pronouncing that and see what happens - and
remember that it&amp;#x27;s cheating to make the [r] or the [m] into separate
syllables!&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Consonants in syllable onsets and codas are also governed by
sonority. There is a rule, called the &lt;em class=&quot;css-0&quot;&gt;sonority sequencing
generalization&lt;/em&gt;, that says that the sonority of a syllable peaks at
the nucleus and decreases toward either boundary. So the sonority of
consonants in the onset is supposed to increase going forward, and the
sonority in the coda is supposed to fall of. This is a generalization,
so there are exceptions - but if you deviate too much from the rule the
result becomes difficult to pronounce. In the made-up word above I
reversed the order of the consonants in the onset and coda of the
first syllable, thus making the sonority sequence &amp;quot;wrong&amp;quot;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Every sound in a language has a place somewhere in the sonority
hierarchy. And every language has its own sonority hierarchy
ordering. So for example [star] is easy for English speakers to
pronounce, but you don&amp;#x27;t see a word like [tsar]: [t] is more sonorous
than [s] in English. But in Russian it is not uncommon to see a word
like [tsar].&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Restrictions on what is allowed in a syllable vary from language to
language. Not all languages allow as many consonants to be clustered
in a syllable onset as English does, while some allow more. Some
languages require every syllable to have an onset, while others allow
naked nuclei. Syllable codas are especially restricted. For example,
in Mandarin Chinese the only syllable codas that are allowed are
nasals. There are no languages that forbid onsets, but many languages
don&amp;#x27;t allow syllable codas to appear at all.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;For more information, and some neat diagrams, visit &lt;a href=&quot;http://www.sil.org/LINGUISTICS/GlossaryOfLinguisticTerms/WhatIsASyllable.htm&quot; class=&quot;css-1od09yo&quot;&gt;What is a
syllable?&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Syllable&quot; class=&quot;css-1od09yo&quot;&gt;the Wikipedia entry for &amp;quot;Syllable&amp;quot;&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Deixis]]></title><link>https://sitr.us/2007/09/17/deixis.html</link><guid isPermaLink="false">https://sitr.us/2007/09/17/deixis.html</guid><pubDate>Mon, 17 Sep 2007 00:00:00 GMT</pubDate><content:encoded>&lt;style data-emotion-css=&quot;1wtp46m&quot;&gt;body{--theme-ui-colors-transparent:var(--theme-ui-colors-transparent,transparent);--theme-ui-colors-black:var(--theme-ui-colors-black,#000);--theme-ui-colors-white:var(--theme-ui-colors-white,#fff);--theme-ui-colors-gray-0:var(--theme-ui-colors-gray-0,null);--theme-ui-colors-gray-1:var(--theme-ui-colors-gray-1,#f7fafc);--theme-ui-colors-gray-2:var(--theme-ui-colors-gray-2,#edf2f7);--theme-ui-colors-gray-3:var(--theme-ui-colors-gray-3,#e2e8f0);--theme-ui-colors-gray-4:var(--theme-ui-colors-gray-4,#cbd5e0);--theme-ui-colors-gray-5:var(--theme-ui-colors-gray-5,#a0aec0);--theme-ui-colors-gray-6:var(--theme-ui-colors-gray-6,#718096);--theme-ui-colors-gray-7:var(--theme-ui-colors-gray-7,#4a5568);--theme-ui-colors-gray-8:var(--theme-ui-colors-gray-8,#2d3748);--theme-ui-colors-gray-9:var(--theme-ui-colors-gray-9,#1a202c);--theme-ui-colors-red-0:var(--theme-ui-colors-red-0,null);--theme-ui-colors-red-1:var(--theme-ui-colors-red-1,#fff5f5);--theme-ui-colors-red-2:var(--theme-ui-colors-red-2,#fed7d7);--theme-ui-colors-red-3:var(--theme-ui-colors-red-3,#feb2b2);--theme-ui-colors-red-4:var(--theme-ui-colors-red-4,#fc8181);--theme-ui-colors-red-5:var(--theme-ui-colors-red-5,#f56565);--theme-ui-colors-red-6:var(--theme-ui-colors-red-6,#e53e3e);--theme-ui-colors-red-7:var(--theme-ui-colors-red-7,#c53030);--theme-ui-colors-red-8:var(--theme-ui-colors-red-8,#9b2c2c);--theme-ui-colors-red-9:var(--theme-ui-colors-red-9,#742a2a);--theme-ui-colors-orange-0:var(--theme-ui-colors-orange-0,null);--theme-ui-colors-orange-1:var(--theme-ui-colors-orange-1,#fffaf0);--theme-ui-colors-orange-2:var(--theme-ui-colors-orange-2,#feebc8);--theme-ui-colors-orange-3:var(--theme-ui-colors-orange-3,#fbd38d);--theme-ui-colors-orange-4:var(--theme-ui-colors-orange-4,#f6ad55);--theme-ui-colors-orange-5:var(--theme-ui-colors-orange-5,#ed8936);--theme-ui-colors-orange-6:var(--theme-ui-colors-orange-6,#dd6b20);--theme-ui-colors-orange-7:var(--theme-ui-colors-orange-7,#c05621);--theme-ui-colors-orange-8:var(--theme-ui-colors-orange-8,#9c4221);--theme-ui-colors-orange-9:var(--theme-ui-colors-orange-9,#7b341e);--theme-ui-colors-yellow-0:var(--theme-ui-colors-yellow-0,null);--theme-ui-colors-yellow-1:var(--theme-ui-colors-yellow-1,#fffff0);--theme-ui-colors-yellow-2:var(--theme-ui-colors-yellow-2,#fefcbf);--theme-ui-colors-yellow-3:var(--theme-ui-colors-yellow-3,#faf089);--theme-ui-colors-yellow-4:var(--theme-ui-colors-yellow-4,#f6e05e);--theme-ui-colors-yellow-5:var(--theme-ui-colors-yellow-5,#ecc94b);--theme-ui-colors-yellow-6:var(--theme-ui-colors-yellow-6,#d69e2e);--theme-ui-colors-yellow-7:var(--theme-ui-colors-yellow-7,#b7791f);--theme-ui-colors-yellow-8:var(--theme-ui-colors-yellow-8,#975a16);--theme-ui-colors-yellow-9:var(--theme-ui-colors-yellow-9,#744210);--theme-ui-colors-green-0:var(--theme-ui-colors-green-0,null);--theme-ui-colors-green-1:var(--theme-ui-colors-green-1,#f0fff4);--theme-ui-colors-green-2:var(--theme-ui-colors-green-2,#c6f6d5);--theme-ui-colors-green-3:var(--theme-ui-colors-green-3,#9ae6b4);--theme-ui-colors-green-4:var(--theme-ui-colors-green-4,#68d391);--theme-ui-colors-green-5:var(--theme-ui-colors-green-5,#48bb78);--theme-ui-colors-green-6:var(--theme-ui-colors-green-6,#38a169);--theme-ui-colors-green-7:var(--theme-ui-colors-green-7,#2f855a);--theme-ui-colors-green-8:var(--theme-ui-colors-green-8,#276749);--theme-ui-colors-green-9:var(--theme-ui-colors-green-9,#22543d);--theme-ui-colors-teal-0:var(--theme-ui-colors-teal-0,null);--theme-ui-colors-teal-1:var(--theme-ui-colors-teal-1,#e6fffa);--theme-ui-colors-teal-2:var(--theme-ui-colors-teal-2,#b2f5ea);--theme-ui-colors-teal-3:var(--theme-ui-colors-teal-3,#81e6d9);--theme-ui-colors-teal-4:var(--theme-ui-colors-teal-4,#4fd1c5);--theme-ui-colors-teal-5:var(--theme-ui-colors-teal-5,#38b2ac);--theme-ui-colors-teal-6:var(--theme-ui-colors-teal-6,#319795);--theme-ui-colors-teal-7:var(--theme-ui-colors-teal-7,#2c7a7b);--theme-ui-colors-teal-8:var(--theme-ui-colors-teal-8,#285e61);--theme-ui-colors-teal-9:var(--theme-ui-colors-teal-9,#234e52);--theme-ui-colors-blue-0:var(--theme-ui-colors-blue-0,null);--theme-ui-colors-blue-1:var(--theme-ui-colors-blue-1,#ebf8ff);--theme-ui-colors-blue-2:var(--theme-ui-colors-blue-2,#bee3f8);--theme-ui-colors-blue-3:var(--theme-ui-colors-blue-3,#90cdf4);--theme-ui-colors-blue-4:var(--theme-ui-colors-blue-4,#63b3ed);--theme-ui-colors-blue-5:var(--theme-ui-colors-blue-5,#4299e1);--theme-ui-colors-blue-6:var(--theme-ui-colors-blue-6,#3182ce);--theme-ui-colors-blue-7:var(--theme-ui-colors-blue-7,#2b6cb0);--theme-ui-colors-blue-8:var(--theme-ui-colors-blue-8,#2c5282);--theme-ui-colors-blue-9:var(--theme-ui-colors-blue-9,#2a4365);--theme-ui-colors-indigo-0:var(--theme-ui-colors-indigo-0,null);--theme-ui-colors-indigo-1:var(--theme-ui-colors-indigo-1,#ebf4ff);--theme-ui-colors-indigo-2:var(--theme-ui-colors-indigo-2,#c3dafe);--theme-ui-colors-indigo-3:var(--theme-ui-colors-indigo-3,#a3bffa);--theme-ui-colors-indigo-4:var(--theme-ui-colors-indigo-4,#7f9cf5);--theme-ui-colors-indigo-5:var(--theme-ui-colors-indigo-5,#667eea);--theme-ui-colors-indigo-6:var(--theme-ui-colors-indigo-6,#5a67d8);--theme-ui-colors-indigo-7:var(--theme-ui-colors-indigo-7,#4c51bf);--theme-ui-colors-indigo-8:var(--theme-ui-colors-indigo-8,#434190);--theme-ui-colors-indigo-9:var(--theme-ui-colors-indigo-9,#3c366b);--theme-ui-colors-purple-0:var(--theme-ui-colors-purple-0,null);--theme-ui-colors-purple-1:var(--theme-ui-colors-purple-1,#faf5ff);--theme-ui-colors-purple-2:var(--theme-ui-colors-purple-2,#e9d8fd);--theme-ui-colors-purple-3:var(--theme-ui-colors-purple-3,#d6bcfa);--theme-ui-colors-purple-4:var(--theme-ui-colors-purple-4,#b794f4);--theme-ui-colors-purple-5:var(--theme-ui-colors-purple-5,#9f7aea);--theme-ui-colors-purple-6:var(--theme-ui-colors-purple-6,#805ad5);--theme-ui-colors-purple-7:var(--theme-ui-colors-purple-7,#6b46c1);--theme-ui-colors-purple-8:var(--theme-ui-colors-purple-8,#553c9a);--theme-ui-colors-purple-9:var(--theme-ui-colors-purple-9,#44337a);--theme-ui-colors-pink-0:var(--theme-ui-colors-pink-0,null);--theme-ui-colors-pink-1:var(--theme-ui-colors-pink-1,#fff5f7);--theme-ui-colors-pink-2:var(--theme-ui-colors-pink-2,#fed7e2);--theme-ui-colors-pink-3:var(--theme-ui-colors-pink-3,#fbb6ce);--theme-ui-colors-pink-4:var(--theme-ui-colors-pink-4,#f687b3);--theme-ui-colors-pink-5:var(--theme-ui-colors-pink-5,#ed64a6);--theme-ui-colors-pink-6:var(--theme-ui-colors-pink-6,#d53f8c);--theme-ui-colors-pink-7:var(--theme-ui-colors-pink-7,#b83280);--theme-ui-colors-pink-8:var(--theme-ui-colors-pink-8,#97266d);--theme-ui-colors-pink-9:var(--theme-ui-colors-pink-9,#702459);--theme-ui-colors-grayDark:var(--theme-ui-colors-grayDark,#2d3748);--theme-ui-colors-text:var(--theme-ui-colors-text,#2d3748);--theme-ui-colors-background:var(--theme-ui-colors-background,#fff);--theme-ui-colors-primary:var(--theme-ui-colors-primary,#6b46c1);--theme-ui-colors-primaryHover:var(--theme-ui-colors-primaryHover,#2c5282);--theme-ui-colors-secondary:var(--theme-ui-colors-secondary,#5f6c80);--theme-ui-colors-muted:var(--theme-ui-colors-muted,#e2e8f0);--theme-ui-colors-success:var(--theme-ui-colors-success,#9ae6b4);--theme-ui-colors-info:var(--theme-ui-colors-info,#63b3ed);--theme-ui-colors-warning:var(--theme-ui-colors-warning,#faf089);--theme-ui-colors-danger:var(--theme-ui-colors-danger,#feb2b2);--theme-ui-colors-light:var(--theme-ui-colors-light,#f7fafc);--theme-ui-colors-dark:var(--theme-ui-colors-dark,#2d3748);--theme-ui-colors-textMuted:var(--theme-ui-colors-textMuted,#718096);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-toggleIcon,#2d3748);--theme-ui-colors-heading:var(--theme-ui-colors-heading,#000);--theme-ui-colors-divide:var(--theme-ui-colors-divide,#cbd5e0);color:var(--theme-ui-colors-text,var(--theme-ui-colors-text,#2d3748));background-color:var(--theme-ui-colors-background,var(--theme-ui-colors-background,#fff));}body.theme-ui-dark{--theme-ui-colors-text:var(--theme-ui-colors-modes-dark-text,#cbd5e0);--theme-ui-colors-primary:var(--theme-ui-colors-modes-dark-primary,#9f7aea);--theme-ui-colors-secondary:var(--theme-ui-colors-modes-dark-secondary,#7f8ea3);--theme-ui-colors-toggleIcon:var(--theme-ui-colors-modes-dark-toggleIcon,#cbd5e0);--theme-ui-colors-background:var(--theme-ui-colors-modes-dark-background,#1A202C);--theme-ui-colors-heading:var(--theme-ui-colors-modes-dark-heading,#fff);--theme-ui-colors-divide:var(--theme-ui-colors-modes-dark-divide,#2d3748);}&lt;/style&gt;&lt;style data-emotion-css=&quot;1bkk5q4&quot;&gt;.css-1bkk5q4{font-size:1rem;-webkit-letter-spacing:-0.003em;-moz-letter-spacing:-0.003em;-ms-letter-spacing:-0.003em;letter-spacing:-0.003em;line-height:1.625;--baseline-multiplier:0.179;--x-height-multiplier:0.35;}@media screen and (min-width:640px){.css-1bkk5q4{font-size:1rem;}}@media screen and (min-width:768px){.css-1bkk5q4{font-size:1.25rem;}}&lt;/style&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;When someone says to you, &amp;quot;here&amp;quot; or &amp;quot;now&amp;quot;, you probably know what he
means. &amp;quot;Here&amp;quot; might the room that you are both sitting in. &amp;quot;Now&amp;quot; would
be the span of time you spent sitting together. But if either word
were uttered under different circumstances, it could mean something
very different. For example, if I called you from the Andes and I used
the word, &amp;quot;here&amp;quot;, it would mean a mountainside somewhere - possibly
thousands of miles away from the aforementioned room. The same word
can mean both the room and the mountainside because of &lt;style data-emotion-css=&quot;1od09yo&quot;&gt;.css-1od09yo{color:var(--theme-ui-colors-primary,#6b46c1);-webkit-text-decoration:none;text-decoration:none;}.css-1od09yo:hover{-webkit-text-decoration:underline;text-decoration:underline;}&lt;/style&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Deixis&quot; class=&quot;css-1od09yo&quot;&gt;deixis&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Deixis is a form of &lt;a href=&quot;http://en.wikipedia.org/wiki/Exophora&quot; class=&quot;css-1od09yo&quot;&gt;exophora&lt;/a&gt;, which is an utterance that is given
meaning by the context it is uttered in. Specifically, deixis
represents the speech event itself: deictic expressions reference the
speaker, the speaker&amp;#x27;s utterances, the speaker&amp;#x27;s location, and the
time at which the speech event occurs. There are different types of
deictic expressions that refer to each particular aspect.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Personal_pronoun&quot; class=&quot;css-1od09yo&quot;&gt;Personal pronouns&lt;/a&gt;, such as &amp;quot;I&amp;quot;, &amp;quot;you&amp;quot;, &amp;quot;he&amp;quot;, and &amp;quot;she&amp;quot;, are deictic
expressions. &amp;quot;I&amp;quot; refers simply to the speaker. The other pronouns are
defined by the relation the referenced person has to the speaker in
the discourse. As the role of speaker switches from person to person,
the context represented by deixis in understood by participants of the
discourse to change too.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Location and time are a little more complicated. Whenever someone
speaks - or writes, or is quoted, etc. - there is an implicit deictic
center that is formed in the minds of the speaker and any listeners. A
deictic center is the point in space and time that spatial and
temporal deictic expressions refer to. &amp;quot;Here&amp;quot; and &amp;quot;now&amp;quot; are the
simplest examples. The verbs &amp;quot;to come&amp;quot; and to &amp;quot;to go&amp;quot; are also spatial
deictic expressions: &amp;quot;to come&amp;quot; means motion towards the deictic
center, and &amp;quot;to go&amp;quot; means motion away.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Based on this analysis, the phrase &amp;quot;come here&amp;quot; seems redundant at
first. But actually the addition of &amp;quot;here&amp;quot; can be important, due to a
phenomenon called &lt;em class=&quot;css-0&quot;&gt;sympathetic deixis&lt;/em&gt;. It is not uncommon, especially
in phone conversations, to hear an utterance like, &amp;quot;I will come by
later&amp;quot;. For the speaker to move toward the deictic center is almost
nonsensical when the speaker is the deictic center. But in this case
sympathetic deixis causes the deictic center to shift from the
speaker&amp;#x27;s location to the listener&amp;#x27;s location. So motion towards the
deictic center actually translates to motion toward the listener.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;Note that the shift caused by sympathetic deixis does not necessarily
affect all the aspects of deixis. In the example above the meanings of
personal pronouns do not change after the deictic center has shifted;
in a similar sentence, &amp;quot;I will come to see you&amp;quot;, &amp;quot;I&amp;quot; refers to the
speaker, and &amp;quot;you&amp;quot; refers to the listener - as would be expected with
or without a shifted deictic center.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;An especially fun deictic puzzle is often heard in answering machine
messages like, &amp;quot;I&amp;#x27;m not here right now&amp;quot;. It isn&amp;#x27;t possible that the
deictic center is fixed upon the speaker during this message, because
it is not possible for the speaker to not occupy his own location. So
there must be sympathetic deixis in effect to shift the deictic center
either spatially or temporally. As someone who has been on the
receiving end of this message, I expect &amp;quot;here&amp;quot; to mean the location
where the message was recorded, and &amp;quot;now&amp;quot; to be the time when I am
calling. By that analysis the spatial location of the deictic center
remains fixed on the location of the speech event, while the temporal
aspect shifts to the listening event via sympathetic deixis. There is
another possible analysis where both spatial and temporal sympathetic
deictic shifts occur. In that case the spatial shift does not move the
location of the deictic center to the listener, but instead moves it
to the place where the listener expects it to be: in this case
wherever the speaker&amp;#x27;s phone is. Under either analysis, the answering
machine example demonstrates that deictic shifts can be temporal as
well as spatial.&lt;/p&gt;&lt;p class=&quot;css-1bkk5q4&quot;&gt;There are other forms of deictic expressions. There is a more complete
list in &lt;a href=&quot;http://en.wikipedia.org/wiki/Deixis&quot; class=&quot;css-1od09yo&quot;&gt;the Wikipedia article on deixis&lt;/a&gt;.&lt;/p&gt;</content:encoded></item></channel></rss>