<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Sam Frances' Blog</title><link href="https://samfrances.co.uk/" rel="alternate"></link><link href="https://samfrances.co.uk/feeds/all.atom.xml" rel="self"></link><id>https://samfrances.co.uk/</id><updated>2021-08-19T13:55:00+01:00</updated><subtitle></subtitle><entry><title>"Smart constructors" in Typescript</title><link href="https://samfrances.co.uk/posts/2021/Aug/19/smart-constructors-in-typescript/" rel="alternate"></link><published>2021-08-19T13:55:00+01:00</published><updated>2021-08-19T13:55:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2021-08-19:/posts/2021/Aug/19/smart-constructors-in-typescript/</id><summary type="html">&lt;p&gt;A useful pattern for implementing complex guarantees with the help of the type system&lt;/p&gt;</summary><content type="html">&lt;p&gt;I want to share a small but very useful pattern which I discovered on the pages
of two highly recommended books:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/programming-with-types"&gt;Programming with Types&lt;/a&gt; by &lt;a href="https://vladris.com/"&gt;Vlad Riscutia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pragprog.com/titles/swdddf/domain-modeling-made-functional/"&gt;Domain Modelling Made Functional&lt;/a&gt; by &lt;a href="https://fsharpforfunandprofit.com/"&gt;Scott Wlaschin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In functional languages this pattern generally gets called "smart constructors", and it is the partial fulfilment of a wish that I have had for some time:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I wish the type system could enforce more interesting constraints than "this must be a string" or "this must be a number", like "this must be a number between 1 and 5" or "this string must be a valid email address".&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;A motivating example&lt;/h3&gt;
&lt;p&gt;At &lt;a href="https://www.cydarmedical.com/"&gt;Cydar&lt;/a&gt;, we use a distributed content-addressable storage system we call "The Disthashbin", which lets you store and access files using their sha256 hash - or as we say around here, its "hashbin ref".&lt;/p&gt;
&lt;p&gt;Here's a very simplified outline of the client interface.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;HashbinClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Getting blob: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You use it as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;HashbinClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;hb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In reality it would return some sort of Blob object with the content of the file, but you get the idea.&lt;/p&gt;
&lt;p&gt;An obvious issue with this code is that it will just as happily accept a string which is not a valid sha256 hash. The following will compile just fine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;badRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;this is not a valid sha256 hash&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;hb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;badRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;A validation function&lt;/h3&gt;
&lt;p&gt;If you're anything like me, your first instinct is to write a function like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;validateRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sha256regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^[A-Fa-f0-9]{64}$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="ne"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Invalid ref: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;HashbinClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;validateRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Getting blob: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, now if we extend the API of the &lt;code&gt;HashbinClient&lt;/code&gt; class, we have to remember to use the &lt;code&gt;validateRef&lt;/code&gt; function in every public method that accepts a hashbin ref.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;HashbinClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;validateRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Getting blob: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;validateRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Getting file: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;validateRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Checking if ref exists: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;validateRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Deleting: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We are also faced with a dilemma when it comes to helper methods. Should a helper method assume that the ref has already been validated at in the API method that calls it, or should it revalidate?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;HashbinClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;validateRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Getting blob: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// validateRef(ref);   // Yay or nay?&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Do I revalidate or don&amp;#39;t I?&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ultimately the only way to be sure is to check every caller of the helper function, or if that caller itself receives a ref, each of the caller's callers, and so on.&lt;/p&gt;
&lt;p&gt;Eventually, you (or another developer who is less familiar with the codebase) will forget to validate a ref in the right place, and you'll end up with an invalid hashbin ref somewhere deeper in your call stack.&lt;/p&gt;
&lt;h3&gt;A smart constructor&lt;/h3&gt;
&lt;p&gt;The first step towards a better solution is to stop thinking in terms of "strings which are valid hashbin refs", and instead think of hashbin refs as their own type. In other words, we need to get over our &lt;a href="https://refactoring.guru/smells/primitive-obsession"&gt;primitive obsession&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We move our validation code into the &lt;code&gt;Ref&lt;/code&gt; constructor, making it impossible to create an invalid hashbin ref.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// ref.ts&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^[A-Fa-f0-9]{64}$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="ne"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Invalid ref: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;We then update our &lt;code&gt;HashbinClient&lt;/code&gt; accordingly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;HashbinClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Getting blob: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Getting file: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Checking if ref exists: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Deleting: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This falls short of the dream of enforcing validity in the type-system itself, but it does use a combination of type-checking and runtime validation to ensure that you can never encounter an invalid hashbin ref.&lt;/p&gt;
&lt;p&gt;In other words, if you have a function (method, class etc.) which requires a &lt;code&gt;Ref&lt;/code&gt; instance, you can be sure that the body of that function will never run with an invalid hashbin ref, because to do that you would have to first create an invalid hashbin ref to pass to the function, and we've made that impossible.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validRefString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;badRefString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;this is not a valid hashbin ref&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This runs fine&lt;/span&gt;
&lt;span class="nx"&gt;hb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validRefString&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// This throws an error *before* get_file() has a chance to run&lt;/span&gt;
&lt;span class="nx"&gt;hb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;badRefString&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// This won&amp;#39;t compile&lt;/span&gt;
&lt;span class="nx"&gt;hb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;badRefString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Making sure values stay valid&lt;/h3&gt;
&lt;p&gt;You've probably already spotted a flaw in the code above, for although we have prevented you from creating an invalid hashbin ref, you can take a valid ref and then make it invalid.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;validRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validRefString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;validRef&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badRefString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In functional languages, this wouldn't be an issue, since data structures are generally immutable by default. But this is easy to fix with Typescript's &lt;code&gt;readonly&lt;/code&gt; modifier.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="ne"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Invalid ref: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Structural typing gotchas&lt;/h3&gt;
&lt;p&gt;Typescript's structural type system also presents another escape hatch. This is because the type checker isn't checking whether items passed to a &lt;code&gt;Ref&lt;/code&gt; parameter are instances of the &lt;code&gt;Ref&lt;/code&gt; class. It only checks if they have the same structure as an instance of the &lt;code&gt;Ref&lt;/code&gt; class, which in this case just means having a field called &lt;code&gt;value&lt;/code&gt; which stores a &lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// This compiles&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sneakyRef&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;not a valid ref&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To get around this, we have to use a neat trick with &lt;code&gt;unique symbol&lt;/code&gt; to simulate nominal typing.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// ref.ts&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^[A-Fa-f0-9]{64}$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RefType&lt;/span&gt;: &lt;span class="kt"&gt;unique&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RefType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="ow"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="ne"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Invalid ref: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;This &lt;code&gt;unique symbol&lt;/code&gt; type is private to the &lt;code&gt;ref&lt;/code&gt; module. As long as we don't export it, nothing outside of the &lt;code&gt;ref&lt;/code&gt; module can create something using that symbol.&lt;/p&gt;
&lt;p&gt;Now, the following will not compile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// This won&amp;#39;t compile any more&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sneakyRef&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;not a valid ref&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The great thing about this technique is that it only applies at the type level. If you look at the generated JavaScript code, the &lt;code&gt;[RefType]&lt;/code&gt; field is removed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sha256regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^[A-Fa-f0-9]{64}$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="ne"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Invalid ref: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means that if you need to use instances of &lt;code&gt;Ref&lt;/code&gt; in contexts where you only want to use serializable values (e.g. as part of &lt;a href="https://redux.js.org/"&gt;redux&lt;/a&gt; state), this won't present an issue.&lt;/p&gt;
&lt;h3&gt;Private constructor&lt;/h3&gt;
&lt;p&gt;Another way to get around our smart constructor is to create a subclass of &lt;code&gt;Ref&lt;/code&gt; that will nullify our validation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validRefString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;SneakyRefSubclass&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validRefString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Any parameter which requires a &lt;code&gt;Ref&lt;/code&gt; will also accept an instance of a subclass of &lt;code&gt;Ref&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Disaster!&lt;/span&gt;

&lt;span class="c1"&gt;// This runs without raising an error&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sneakyRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SneakyRefSubclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bad ref&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// And this compiles!&lt;/span&gt;
&lt;span class="nx"&gt;hb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sneakyRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To stop this from happening, we have to forbid subclassing of &lt;code&gt;Ref&lt;/code&gt;. At the time of writing, Typescript doesn't have a &lt;code&gt;final&lt;/code&gt; modifier, so you can't do this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// etc.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What you can do is make the constructor private, which prevents subclassing, and also stops code outside of the class from creating an instance using &lt;code&gt;new&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RefType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="ow"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="ne"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Invalid ref: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// This is how we create instances now&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now our &lt;code&gt;SneakyRefSubclass&lt;/code&gt; will no longer compile.&lt;/p&gt;
&lt;h3&gt;Complete example&lt;/h3&gt;
&lt;p&gt;Here's the complete example, with a bit of tidying up here and there.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// ref.ts&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^[A-Fa-f0-9]{64}$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RefType&lt;/span&gt;: &lt;span class="kt"&gt;unique&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RefType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="ow"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sha256regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="ne"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Invalid ref: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Only export the type, since clients of this module&lt;/span&gt;
&lt;span class="c1"&gt;// don&amp;#39;t need access to the runtime class&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// No need to reveal externally that we&amp;#39;re using a class,&lt;/span&gt;
&lt;span class="c1"&gt;// So export a factory function&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// hashbin-client.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;./ref&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;HashbinClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Getting blob: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Getting file: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Checking if ref exists: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;: &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Deleting: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createRef&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;./ref&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;HashbinClient&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;./hashbin-client&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;HashbinClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validRefString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;badRefString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;this is not a valid hashbin ref&amp;quot;&lt;/span&gt;

&lt;span class="nx"&gt;hb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validRefString&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Throws an error&lt;/span&gt;
&lt;span class="nx"&gt;hb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;badRefString&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h3&gt;Conclusion: Why care?&lt;/h3&gt;
&lt;p&gt;Ultimately, this pattern reduces the burden of validation by removing the dilemma of where and when to run the validation function. You run validation code when creating a value, and from that point onwards you can trust that you have a valid item. That alone is worth the price of having to wrap your primitive values.&lt;/p&gt;
&lt;p&gt;But the truth is, you should probably be wrapping your primitive values anyway. Conceptually, a string is rarely a string, but a name, an email address, a uuid etc.; an integer is rarely an integer - rather it's a temperature, a timestamp, a width, an age etc.&lt;/p&gt;
&lt;p&gt;NASA found this out to their peril in 1999.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Mars Climate Orbiter crashed and disintegrated in the Mars atmosphere because a component developed by Lockheed provided momentum measured in pound-force seconds, while another component developed by NASA expected momentum as Newton seconds. &lt;em&gt;-- &lt;a href="https://vladris.com/blog/2018/09/09/clean-code-types.html"&gt;Vlad Riscutia&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Who knows? Maybe this disaster could have been averted by function parameters which accepted &lt;code&gt;NewtonSeconds&lt;/code&gt; or &lt;code&gt;PoundForceSeconds&lt;/code&gt; rather than &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I also want to emphasize that smart constructors are not the only way to enforce complex constraints using the type system.&lt;/p&gt;
&lt;p&gt;Take the related example of validating a hex colour. You could use a smart constructor very similar to &lt;code&gt;Ref&lt;/code&gt;, or you could do something like the following (warning: Not the most efficient way to store hex colours!):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexDigit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;3&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;4&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;5&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;6&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;7&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;8&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;9&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;b&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;c&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;d&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;e&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;f&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexColor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HexDigit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexDigit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexDigit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexDigit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexDigit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexDigit&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;red&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexColor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;f&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;f&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;wont_compile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexColor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;f&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;f&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;@&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;wont_compile_either&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HexColor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;f&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;f&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;How far these approaches will take you, and under what circumstances, is a discussion for another day.&lt;/p&gt;
&lt;h3&gt;Further reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Articles&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fsharpforfunandprofit.com/posts/designing-with-types-single-case-dus/"&gt;Designing with types: Single case union types&lt;/a&gt; by &lt;a href="https://fsharpforfunandprofit.com/"&gt;Scott Wlaschin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/pragmatic-programmers/the-integrity-of-simple-v-alues-1fbbd2e7f4a8"&gt;The Integrity of Simple Values&lt;/a&gt; by &lt;a href="https://fsharpforfunandprofit.com/"&gt;Scott Wlaschin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vladris.com/blog/2018/09/09/clean-code-types.html"&gt;Clean Code: Types&lt;/a&gt; by &lt;a href="https://vladris.com/"&gt;Vlad Riscutia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Books&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/programming-with-types"&gt;Programming with Types&lt;/a&gt; by &lt;a href="https://vladris.com/"&gt;Vlad Riscutia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pragprog.com/titles/swdddf/domain-modeling-made-functional/"&gt;Domain Modelling Made Functional&lt;/a&gt; by &lt;a href="https://fsharpforfunandprofit.com/"&gt;Scott Wlaschin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</content><category term="Blog"></category><category term="typescript"></category><category term="types"></category></entry><entry><title>Retiring the "Grauniad News" twitterbot</title><link href="https://samfrances.co.uk/posts/2021/Apr/03/retiring-grauniad-news-twitterbot/" rel="alternate"></link><published>2021-04-03T16:00:00+01:00</published><updated>2021-04-03T16:00:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2021-04-03:/posts/2021/Apr/03/retiring-grauniad-news-twitterbot/</id><summary type="html">&lt;p&gt;I'm retiring the "Grauniad News" twitterbot&lt;/p&gt;</summary><content type="html">&lt;p&gt;After more than four years, I'm retiring the &lt;a href="https://twitter.com/grauniad_news"&gt;"Grauniad News"&lt;/a&gt; twitterbot. I'll be honest, I sort of forgot it was running! If you want to see misspelled Guardian headlines, you'll have to go to &lt;a href="https://twitter.com/guardian"&gt;@guardian&lt;/a&gt;.&lt;/p&gt;</content><category term="Blog"></category><category term="twitterbot"></category><category term="twitter"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2021/Apr/03/now-13/" rel="alternate"></link><published>2021-04-03T15:08:00+01:00</published><updated>2021-04-03T15:08:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2021-04-03:/posts/2021/Apr/03/now-13/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/"&gt;The Pragmatic Programmer, 20th Anniversary Edition&lt;/a&gt; by David Thomas and Andrew Hunt (finished)&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://www.informit.com/store/test-driven-development-by-example-9780321146533"&gt;Test Driven Development: By Example&lt;/a&gt; by Kent Beck (finished)&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mhprofessional.com/9780071743617-usa-algebra-demystified-second-edition-group"&gt;Algebra DeMYSTiFieD, Second Edition&lt;/a&gt; by Rhonda Huettenmueller&lt;ul&gt;
&lt;li&gt;Trying to slowly get my maths back to the level it once was, and beyond, with the ultimate goal of getting a solid grasp
  of linear algebra. Next after this will be &lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt;
  and then possibly &lt;a href="http://codingthematrix.com/"&gt;Coding the Matrix&lt;/a&gt; or &lt;a href="http://global.oup.com/booksites/content/9780199654444/"&gt;Linear Algebra: Step by Step&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskellbook.com/"&gt;Haskell programming from first principles&lt;/a&gt; by Christopher Allen and Julie Moronuki&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learnyousomeerlang.com/"&gt;Learn You Some Erlang for Great Good!&lt;/a&gt; by Fred Hebert&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.htdp.org/"&gt;How to Design Programs (2nd Edition)&lt;/a&gt; by Matthias Felleisen, Robert Bruce Findler, Matthew Flatt and Shriram Krishnamurthi&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/micromasters/software-development"&gt;UBCx: Software Development MicroMasters&lt;/a&gt; on &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2019/Aug/05/now-12/" rel="alternate"></link><published>2019-08-05T22:50:00+01:00</published><updated>2019-08-05T22:50:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2019-08-05:/posts/2019/Aug/05/now-12/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/kubernetes-in-action"&gt;Kubernetes in Action&lt;/a&gt; by Marko Lukša&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://www.manning.com/books/functional-programming-in-javascript"&gt;Functional Programming in JavaScript&lt;/a&gt; by Luis Atencio&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://www.manning.com/books/elixir-in-action-second-edition"&gt;Elixir in Action, Second Edition&lt;/a&gt; by Saša Jurić&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mhprofessional.com/9780071743617-usa-algebra-demystified-second-edition-group"&gt;Algebra DeMYSTiFieD, Second Edition&lt;/a&gt; by Rhonda Huettenmueller&lt;ul&gt;
&lt;li&gt;Trying to slowly get my maths back to the level it once was, and beyond, with the ultimate goal of getting a solid grasp
  of linear algebra. Next after this will be &lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt;
  and then possibly &lt;a href="http://codingthematrix.com/"&gt;Coding the Matrix&lt;/a&gt; or &lt;a href="http://global.oup.com/booksites/content/9780199654444/"&gt;Linear Algebra: Step by Step&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskellbook.com/"&gt;Haskell programming from first principles&lt;/a&gt; by Christopher Allen and Julie Moronuki&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learnyousomeerlang.com/"&gt;Learn You Some Erlang for Great Good!&lt;/a&gt; by Fred Hebert&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.htdp.org/"&gt;How to Design Programs (2nd Edition)&lt;/a&gt; by Matthias Felleisen, Robert Bruce Findler, Matthew Flatt and Shriram Krishnamurthi&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/micromasters/software-development"&gt;UBCx: Software Development MicroMasters&lt;/a&gt; on &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2019/Jan/16/now-11/" rel="alternate"></link><published>2019-01-16T22:50:00+00:00</published><updated>2019-01-16T22:50:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2019-01-16:/posts/2019/Jan/16/now-11/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learnyousomeerlang.com/"&gt;Learn You Some Erlang for Great Good!&lt;/a&gt; by Fred Hebert&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://www.poodr.com/"&gt;Practical Object-Oriented Design in Ruby&lt;/a&gt; by Sandi Metz&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.htdp.org/"&gt;How to Design Programs (2nd Edition)&lt;/a&gt; by Matthias Felleisen, Robert Bruce Findler, Matthew Flatt and Shriram Krishnamurthi&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mhprofessional.com/9780071743617-usa-algebra-demystified-second-edition-group"&gt;Algebra DeMYSTiFieD, Second Edition&lt;/a&gt; by Rhonda Huettenmueller&lt;ul&gt;
&lt;li&gt;Trying to slowly get my maths back to the level it once was, and beyond, with the ultimate goal of getting a solid grasp
  of linear algebra. Next after this will be &lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt;
  and then possibly &lt;a href="http://codingthematrix.com/"&gt;Coding the Matrix&lt;/a&gt; or &lt;a href="http://global.oup.com/booksites/content/9780199654444/"&gt;Linear Algebra: Step by Step&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskellbook.com/"&gt;Haskell programming from first principles&lt;/a&gt; by Christopher Allen and Julie Moronuki&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/micromasters/software-development"&gt;UBCx: Software Development MicroMasters&lt;/a&gt; on &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2018/Jul/07/now-10/" rel="alternate"></link><published>2018-07-07T17:11:00+01:00</published><updated>2018-07-07T17:11:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2018-07-07:/posts/2018/Jul/07/now-10/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://shop.oreilly.com/product/0636920030584.do"&gt;Introducing Elixir&lt;/a&gt; by Simon St. Laurent and J. Eisenberg&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://shop.oreilly.com/product/0636920032151.do"&gt;Javacript with Promises&lt;/a&gt; by Daniel Parker&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://www.poodr.com/"&gt;Practical Object-Oriented Design in Ruby&lt;/a&gt; by Sandi Metz&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://www.htdp.org/"&gt;How to Design Programs (2nd Edition)&lt;/a&gt; by Matthias Felleisen, Robert Bruce Findler, Matthew Flatt and Shriram Krishnamurthi&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="https://www.mhprofessional.com/9780071743617-usa-algebra-demystified-second-edition-group"&gt;Algebra DeMYSTiFieD, Second Edition&lt;/a&gt; by Rhonda Huettenmueller&lt;ul&gt;
&lt;li&gt;Trying to slowly get my maths back to the level it once was, and beyond, with the ultimate goal of getting a solid grasp
  of linear algebra. Next after this will be &lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt;
  and then possibly &lt;a href="http://codingthematrix.com/"&gt;Coding the Matrix&lt;/a&gt; or &lt;a href="http://global.oup.com/booksites/content/9780199654444/"&gt;Linear Algebra: Step by Step&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskellbook.com/"&gt;Haskell programming from first principles&lt;/a&gt; by Christopher Allen and Julie Moronuki&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/micromasters/software-development"&gt;UBCx: Software Development MicroMasters&lt;/a&gt; on &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>Using generators to build async agnostic API clients</title><link href="https://samfrances.co.uk/posts/2018/Jun/28/using-generators-to-build-async-agnostic-api-clients/" rel="alternate"></link><published>2018-06-28T20:02:00+01:00</published><updated>2018-06-28T20:02:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2018-06-28:/posts/2018/Jun/28/using-generators-to-build-async-agnostic-api-clients/</id><summary type="html">&lt;p&gt;A strategy for building API client libraries that work in async and sync contexts&lt;/p&gt;</summary><content type="html">&lt;p&gt;I'm a big fan of asyncio, couroutines and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; syntax in
Python 3.5+. However, they come with some well documented downsides. Not least
among these is the
&lt;a href="http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/"&gt;red/blue function problem&lt;/a&gt;.
Coroutines can call plain functions, but only a coroutine can await another
coroutine (unless you count running it by directly invoking the event loop).
Meanwhile, if a coroutine calls a plain function which triggers some sort of
blocking IO, the whole event loop is blocked.&lt;/p&gt;
&lt;p&gt;I have found the red/blue divide particularly annoying in the context of web
API clients. Take &lt;a href="https://boto3.readthedocs.io/"&gt;boto3&lt;/a&gt;, the popular client for
AWS's various APIs. This only works in a synchronous context (unless you don't
mind blocking the event loop for the duration of each request). Another project,
&lt;a href="https://github.com/aio-libs/aiobotocore"&gt;aiobotocore&lt;/a&gt;, provides some of these
capabilities in an async context, but doesn't support all services and operations.&lt;/p&gt;
&lt;p&gt;Yet, many of the things API clients do have nothing directly to do with IO,
async or otherwise. Preparing a HTTP request - determining what headers to
use, constructing a request body, calculating signatures and other
authentication details - is a purely functional, IO-free business.
The same goes for interpreting a HTTP response.&lt;/p&gt;
&lt;p&gt;A function or method in an API client is like a sandwich where constructing
the request and interpreting the response are the two pieces of bread, and sending
the request is the filling. We ought to be able to use the same bread for any
type of sandwich, without caring if the filling is ham, cheese, or something else.&lt;/p&gt;
&lt;p&gt;Ok, enough with the shaky metaphors. Time for some code.&lt;/p&gt;
&lt;p&gt;The strategy I have stumbled upon is to write the pure
request-construction / response-interpretation logic as a generator function,
which yields in the middle. The generator yields an object providing details of
the HTTP request it wishes to be sent, ceding control to "something else, I care not
what" which executes the actual web request, and sends an object representing the response
back into the generator. The generator then processes the response.&lt;/p&gt;
&lt;p&gt;Here's a toy API client for &lt;a href="http://www.icndb.com/"&gt;The Internet Chuck Norris Database&lt;/a&gt;.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;
&lt;span class="normal"&gt;40&lt;/span&gt;
&lt;span class="normal"&gt;41&lt;/span&gt;
&lt;span class="normal"&gt;42&lt;/span&gt;
&lt;span class="normal"&gt;43&lt;/span&gt;
&lt;span class="normal"&gt;44&lt;/span&gt;
&lt;span class="normal"&gt;45&lt;/span&gt;
&lt;span class="normal"&gt;46&lt;/span&gt;
&lt;span class="normal"&gt;47&lt;/span&gt;
&lt;span class="normal"&gt;48&lt;/span&gt;
&lt;span class="normal"&gt;49&lt;/span&gt;
&lt;span class="normal"&gt;50&lt;/span&gt;
&lt;span class="normal"&gt;51&lt;/span&gt;
&lt;span class="normal"&gt;52&lt;/span&gt;
&lt;span class="normal"&gt;53&lt;/span&gt;
&lt;span class="normal"&gt;54&lt;/span&gt;
&lt;span class="normal"&gt;55&lt;/span&gt;
&lt;span class="normal"&gt;56&lt;/span&gt;
&lt;span class="normal"&gt;57&lt;/span&gt;
&lt;span class="normal"&gt;58&lt;/span&gt;
&lt;span class="normal"&gt;59&lt;/span&gt;
&lt;span class="normal"&gt;60&lt;/span&gt;
&lt;span class="normal"&gt;61&lt;/span&gt;
&lt;span class="normal"&gt;62&lt;/span&gt;
&lt;span class="normal"&gt;63&lt;/span&gt;
&lt;span class="normal"&gt;64&lt;/span&gt;
&lt;span class="normal"&gt;65&lt;/span&gt;
&lt;span class="normal"&gt;66&lt;/span&gt;
&lt;span class="normal"&gt;67&lt;/span&gt;
&lt;span class="normal"&gt;68&lt;/span&gt;
&lt;span class="normal"&gt;69&lt;/span&gt;
&lt;span class="normal"&gt;70&lt;/span&gt;
&lt;span class="normal"&gt;71&lt;/span&gt;
&lt;span class="normal"&gt;72&lt;/span&gt;
&lt;span class="normal"&gt;73&lt;/span&gt;
&lt;span class="normal"&gt;74&lt;/span&gt;
&lt;span class="normal"&gt;75&lt;/span&gt;
&lt;span class="normal"&gt;76&lt;/span&gt;
&lt;span class="normal"&gt;77&lt;/span&gt;
&lt;span class="normal"&gt;78&lt;/span&gt;
&lt;span class="normal"&gt;79&lt;/span&gt;
&lt;span class="normal"&gt;80&lt;/span&gt;
&lt;span class="normal"&gt;81&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# agnostic_client.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NamedTuple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;aiohttp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClientSession&lt;/span&gt;

&lt;span class="n"&gt;URL_TEMPLATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;https://api.icndb.com/jokes/&lt;/span&gt;&lt;span class="si"&gt;{id}&lt;/span&gt;&lt;span class="s2"&gt;/&amp;quot;&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NamedTuple&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NamedTuple&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_joke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;URL_TEMPLATE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;JokeApiError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;API request failed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;NoSuchQuoteException&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;NoSuchJoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;success&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;JokeApiError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;API request failed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;joke&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JokeApiError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;StopIteration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;JokeApiError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;e&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_api_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;Return&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_api_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;Return&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JokeApiError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NoSuchJoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JokeApiError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;I still haven't decided if this is a good strategy overall, although it
certainly seems to achieve its immediate goal of allowing a large chunk of
code to be shared between sync and async clients. Comments welcome.&lt;/p&gt;
&lt;p&gt;¡Hasta pronto!&lt;/p&gt;</content><category term="Blog"></category><category term="python"></category><category term="asyncio"></category><category term="async"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2018/May/28/now-9/" rel="alternate"></link><published>2018-05-28T00:23:00+01:00</published><updated>2018-05-28T00:23:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2018-05-28:/posts/2018/May/28/now-9/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://pragprog.com/book/cdc-elixir/learn-functional-programming-with-elixir"&gt;Learn Functional Programming with Elixir&lt;/a&gt;
  by Ulisses Almeida&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://shop.oreilly.com/product/0636920030584.do"&gt;Introducing Elixir&lt;/a&gt; by Simon St. Laurent and J. Eisenberg&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://shop.oreilly.com/product/0636920032502.do"&gt;Lightweight Django&lt;/a&gt; by Julia Elman and Mark Lavin&lt;/del&gt; (finished)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskellbook.com/"&gt;Haskell programming from first principles&lt;/a&gt; by Christopher Allen and Julie Moronuki&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.headfirstlabs.com/books/hfalg/"&gt;Head First Algebra&lt;/a&gt; by Tracy Pilone &amp;amp; Dan Pilone&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/micromasters/software-development"&gt;UBCx: Software Development MicroMasters&lt;/a&gt; on &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2018/Mar/28/now-8/" rel="alternate"></link><published>2018-03-28T00:23:00+01:00</published><updated>2018-03-28T00:23:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2018-03-28:/posts/2018/Mar/28/now-8/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;#43; &lt;a href="https://pragprog.com/book/cdc-elixir/learn-functional-programming-with-elixir"&gt;Learn Functional Programming with Elixir&lt;/a&gt;
  by Ulisses Almeida&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://shop.oreilly.com/product/0636920030584.do"&gt;Introducing Elixir&lt;/a&gt; by Simon St. Laurent and J. Eisenberg&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://shop.oreilly.com/product/0636920032502.do"&gt;Lightweight Django&lt;/a&gt; by Julia Elman and Mark Lavin&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://learnyouahaskell.com/"&gt;Learn you a Haskell for Great Good&lt;/a&gt; by Miran Lipovaca&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://shop.oreilly.com/product/0636920046189.do"&gt;Concurrency in Go&lt;/a&gt; by Katherine Cox-Buday&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href=""&gt;Head First Ruby&lt;/a&gt; by Jay McGavren&lt;/del&gt; (finished)&lt;ul&gt;
&lt;li&gt;I read this to provide a foundation for "Practical Object-Oriented Design in Ruby" by Sandi Metz.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskellbook.com/"&gt;Haskell programming from first principles&lt;/a&gt; by Christopher Allen and Julie Moronuki&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.headfirstlabs.com/books/hfalg/"&gt;Head First Algebra&lt;/a&gt; by Tracy Pilone &amp;amp; Dan Pilone&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/micromasters/software-development"&gt;UBCx: Software Development MicroMasters&lt;/a&gt; on &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2017/Dec/11/now-7/" rel="alternate"></link><published>2017-12-11T00:32:00+00:00</published><updated>2017-12-11T00:32:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2017-12-11:/posts/2017/Dec/11/now-7/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://shop.oreilly.com/product/0636920046189.do"&gt;Concurrency in Go&lt;/a&gt; by Katherine Cox-Buday&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://www.manning.com/books/go-in-action"&gt;Go in Action&lt;/a&gt; by William Kennedy&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskellbook.com/"&gt;Haskell programming from first principles&lt;/a&gt; by Christopher Allen and Julie Moronuki&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.headfirstlabs.com/books/hfalg/"&gt;Head First Algebra&lt;/a&gt; by Tracy Pilone &amp;amp; Dan Pilone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/micromasters/software-development"&gt;UBCx: Software Development MicroMasters&lt;/a&gt; on &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2017/Nov/19/now-6/" rel="alternate"></link><published>2017-11-19T00:09:00+00:00</published><updated>2017-11-19T00:09:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2017-11-19:/posts/2017/Nov/19/now-6/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://shop.oreilly.com/product/0636920049579.do"&gt;Learning React&lt;/a&gt; by Alex Banks &amp;amp; Eve Porcello&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://leanpub.com/understandinges6"&gt;Understanding ECMAScript 6&lt;/a&gt; by Nicholas C. Zakas&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://shop.oreilly.com/product/0636920046516.do"&gt;Introducing Go&lt;/a&gt; by Caleb Doxsley&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/go-in-action"&gt;Go in Action&lt;/a&gt; by William Kennedy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskellbook.com/"&gt;Haskell programming from first principles&lt;/a&gt; by Christopher Allen and Julie Moronuki&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.headfirstlabs.com/books/hfalg/"&gt;Head First Algebra&lt;/a&gt; by Tracy Pilone &amp;amp; Dan Pilone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/micromasters/software-development"&gt;UBCx: Software Development MicroMasters&lt;/a&gt; on &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.safaribooksonline.com/library/view/test-driven-development/9781491919163/"&gt;Test-Driven Development&lt;/a&gt; on
  &lt;a href="https://www.safaribooksonline.com"&gt;Safari&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2017/Apr/24/now-5/" rel="alternate"></link><published>2017-04-24T08:32:00+01:00</published><updated>2017-04-24T08:32:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2017-04-24:/posts/2017/Apr/24/now-5/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Goldfinch_%28novel%29"&gt;The Goldfinch&lt;/a&gt; by Donna Tartt&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="https://leanpub.com/understandinges6"&gt;Understanding ECMAScript 6&lt;/a&gt; by Nicholas C. Zakas&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://haskellbook.com/"&gt;Haskell programming from first principles&lt;/a&gt; by Christopher Allen and Julie Moronuki&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.headfirstlabs.com/books/hfalg/"&gt;Head First Algebra&lt;/a&gt; by Tracy Pilone &amp;amp; Dan Pilone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://leanpub.com/understandinges6"&gt;Understanding ECMAScript 6&lt;/a&gt; by Nicholas C. Zakas&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/micromasters/software-development"&gt;UBCx: Software Development MicroMasters&lt;/a&gt; on &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://www.edx.org/xseries/how-code-systematic-program-design"&gt;UBCx: Systematic Program Design (XSeries)&lt;/a&gt; on
  &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.safaribooksonline.com/library/view/test-driven-development/9781491919163/"&gt;Test-Driven Development&lt;/a&gt; on
  &lt;a href="https://www.safaribooksonline.com"&gt;Safari&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I'm getting back into studying Spanish after a few months off.&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2017/Feb/14/now-4/" rel="alternate"></link><published>2017-02-14T22:25:00+00:00</published><updated>2017-02-14T22:25:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2017-02-14:/posts/2017/Feb/14/now-4/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://shop.oreilly.com/product/9780596517748.do"&gt;JavaScript: The Good Parts&lt;/a&gt; by Douglas Crockford&lt;/del&gt;
  (finished)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Goldfinch_%28novel%29"&gt;The Goldfinch&lt;/a&gt; by Donna Tartt&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://www.headfirstlabs.com/books/hfalg/"&gt;Head First Algebra&lt;/a&gt; by Tracy Pilone &amp;amp; Dan Pilone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/understandinges6"&gt;Understanding ECMAScript 6&lt;/a&gt; by Nicholas C. Zakas&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/xseries/how-code-systematic-program-design"&gt;UBCx: Systematic Program Design (XSeries)&lt;/a&gt; on
  &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.safaribooksonline.com/library/view/test-driven-development/9781491919163/"&gt;Test-Driven Development&lt;/a&gt; on
  &lt;a href="https://www.safaribooksonline.com"&gt;Safari&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I'm getting back into studying Spanish after a few months off.&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2017/Feb/11/now-3/" rel="alternate"></link><published>2017-02-11T16:53:00+00:00</published><updated>2017-02-11T16:53:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2017-02-11:/posts/2017/Feb/11/now-3/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://shop.oreilly.com/product/9780596517748.do"&gt;JavaScript: The Good Parts&lt;/a&gt; by Douglas Crockford&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Goldfinch_%28novel%29"&gt;The Goldfinch&lt;/a&gt; by Donna Tartt&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="https://www.amazon.co.uk/Foundation-Mathematics-K-Stroud/dp/0230579078/"&gt;Foundation Mathematics&lt;/a&gt; by K.A. Stroud&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://www.headfirstlabs.com/books/hfalg/"&gt;Head First Algebra&lt;/a&gt; by Tracy Pilone &amp;amp; Dan Pilone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/understandinges6"&gt;Understanding ECMAScript 6&lt;/a&gt; by Nicholas C. Zakas&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.librarything.com/catalog.php?view=samfrances&amp;amp;collection=-1&amp;amp;shelf=list&amp;amp;sort=dateread"&gt;So many books!!!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/xseries/how-code-systematic-program-design"&gt;UBCx: Systematic Program Design (XSeries)&lt;/a&gt; on
  &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.safaribooksonline.com/library/view/test-driven-development/9781491919163/"&gt;Test-Driven Development&lt;/a&gt; on
  &lt;a href="https://www.safaribooksonline.com"&gt;Safari&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I'm getting back into studying Spanish after a few months off.&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2017/Feb/02/now-2/" rel="alternate"></link><published>2017-02-02T00:26:00+00:00</published><updated>2017-02-02T00:26:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2017-02-02:/posts/2017/Feb/02/now-2/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://www.stevefenton.co.uk/publications/typescript-for-javascript-programmers/"&gt;TypeScript for Javascript
  Programmers&lt;/a&gt; by Steve
  Fenton&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://shop.oreilly.com/product/9780596517748.do"&gt;JavaScript: The Good Parts&lt;/a&gt; by Douglas Crockford&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Goldfinch_%28novel%29"&gt;The Goldfinch&lt;/a&gt; by Donna Tartt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/understandinges6"&gt;Understanding ECMAScript 6&lt;/a&gt; by Nicholas C. Zakas&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://www.apress.com/la/book/9781430258544"&gt;Foundations of Python Network Programming&lt;/a&gt; by Brandon Rhodes&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://zguide.zeromq.org/page:all"&gt;ZeroMQ&lt;/a&gt; by Pieter Hintjens&lt;/li&gt;
&lt;li&gt;So many books!!!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/xseries/how-code-systematic-program-design"&gt;UBCx: Systematic Program Design (XSeries)&lt;/a&gt; on
  &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.safaribooksonline.com/library/view/test-driven-development/9781491919163/"&gt;Test-Driven Development&lt;/a&gt; on
  &lt;a href="https://www.safaribooksonline.com"&gt;Safari&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I'm getting back into studying Spanish after a few months off.&lt;/li&gt;
&lt;li&gt;I have ordered a running machine! This way I can exercise in my pyjamas while watching Netflix. My plan is to make exercise so convenient that I can't find an excuse not to do it.&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2017/Jan/30/now-1/" rel="alternate"></link><published>2017-01-30T22:12:00+00:00</published><updated>2017-01-30T22:12:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2017-01-30:/posts/2017/Jan/30/now-1/</id><summary type="html">&lt;p&gt;What I'm doing now&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.stevefenton.co.uk/publications/typescript-for-javascript-programmers/"&gt;TypeScript for Javascript
  Programmers&lt;/a&gt; by Steve Fenton&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="http://shop.oreilly.com/product/9780596517748.do"&gt;JavaScript: The Good Parts&lt;/a&gt; by Douglas Crockford&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;a href="http://chimera.labs.oreilly.com/books/1234000001813/index.html"&gt;Apprenticeship Patterns: Guidance for the Aspiring Software
  Craftsman&lt;/a&gt; by David H. Hoover &amp;amp; Adewale Oshineye&lt;/del&gt; (finished)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Goldfinch_%28novel%29"&gt;The Goldfinch&lt;/a&gt; by Donna Tartt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/understandinges6"&gt;Understanding ECMAScript 6&lt;/a&gt; by Nicholas C. Zakas&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;&amp;#43; &lt;a href="https://www.nostarch.com/howlinuxworks2"&gt;How Linux Works&lt;/a&gt; by Brian Ward&lt;/li&gt;
&lt;li&gt;So many books!!!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/xseries/how-code-systematic-program-design"&gt;UBCx: Systematic Program Design (XSeries)&lt;/a&gt; on
  &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.safaribooksonline.com/library/view/test-driven-development/9781491919163/"&gt;Test-Driven Development&lt;/a&gt; on
  &lt;a href="https://www.safaribooksonline.com"&gt;Safari&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I'm getting back into studying Spanish after a few months off.&lt;/li&gt;
&lt;li&gt;I have ordered a running machine! This way I can exercise in my pyjamas while watching Netflix. My plan is to make exercise so convenient that I can't find an excuse not to do it.&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>What I'm doing now</title><link href="https://samfrances.co.uk/posts/2017/Jan/28/now-0/" rel="alternate"></link><published>2017-01-28T10:55:00+00:00</published><updated>2017-01-28T10:55:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2017-01-28:/posts/2017/Jan/28/now-0/</id><summary type="html"></summary><content type="html">&lt;p&gt;&lt;em&gt;(This is my &lt;a href="http://nownownow.com/about"&gt;now page&lt;/a&gt;. It tells you what I'm doing at the moment. It also reminds &lt;/em&gt;&lt;em&gt;me&lt;/em&gt;&lt;em&gt; what I have decided I want to be doing at the moment.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.stevefenton.co.uk/publications/typescript-for-javascript-programmers/"&gt;TypeScript for Javascript
  Programmers&lt;/a&gt; by Steve Fenton&lt;/li&gt;
&lt;li&gt;&lt;a href="http://chimera.labs.oreilly.com/books/1234000001813/index.html"&gt;Apprenticeship Patterns: Guidance for the Aspiring Software
  Craftsman&lt;/a&gt; by David H. Hoover &amp;amp; Adewale Oshineye&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Goldfinch_%28novel%29"&gt;The Goldfinch&lt;/a&gt; by Donna Tartt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking forward to reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.obeythetestinggoat.com/"&gt;Test-Driven Development with Python, 2nd Edition&lt;/a&gt; by Harry J.W. Percival&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; by Jeff Geerling&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/amazon-web-services-in-action"&gt;Amazon Web Services in Action&lt;/a&gt; by Michael Wittig &amp;amp;
  Andreas Wittig&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/javascriptallongesix"&gt;JavaScript Allongé, the "Six" Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/understandinges6"&gt;Understanding ECMAScript 6&lt;/a&gt; by Nicholas C. Zakas&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleedingedgepress.com/developing-a-redux-edge/"&gt;Developing a Redux Edge&lt;/a&gt; by Johannes Lumpe, Karl Purkhardt,
  Art Muller, Darío Cravero &amp;amp; Ezekiel Chentnik&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/docker-in-action"&gt;Docker in Action&lt;/a&gt; by Jeff Nickoloff&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/rabbitmq-in-depth"&gt;RabbitMQ in Depth&lt;/a&gt; by Gavin M. Roy&lt;/li&gt;
&lt;li&gt;So many books!!!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Studying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/xseries/how-code-systematic-program-design"&gt;UBCx: Systematic Program Design (XSeries)&lt;/a&gt; on
  &lt;a href="https://www.edx.org"&gt;Edx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.safaribooksonline.com/library/view/test-driven-development/9781491919163/"&gt;Test-Driven Development&lt;/a&gt; on
  &lt;a href="https://www.safaribooksonline.com"&gt;Safari&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of interesting stuff for &lt;a href="https://cydarmedical.com/"&gt;Cydar&lt;/a&gt;, involving things like Django Rest Framework, Celery, Ansible, and AWS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I'm getting back into studying Spanish after a few months off.&lt;/li&gt;
&lt;li&gt;I have ordered a running machine! This way I can exercise in my pyjamas while watching Netflix. My plan is to make exercise so convenient that I can't find an excuse not to do it.&lt;/li&gt;
&lt;/ul&gt;</content><category term="Now"></category></entry><entry><title>More twitterbot silliness</title><link href="https://samfrances.co.uk/posts/2017/Jan/23/more-twitterbot-silliness/" rel="alternate"></link><published>2017-01-23T23:41:00+00:00</published><updated>2017-01-23T23:41:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2017-01-23:/posts/2017/Jan/23/more-twitterbot-silliness/</id><summary type="html">&lt;p&gt;Not content to rest on my laurels after the roaring success that was &lt;a href="https://twitter.com/fight_guybrush"&gt;@fight_guybrush&lt;/a&gt;, I have written another twitter bot.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Not content to rest on my laurels after the roaring success that was &lt;a href="https://twitter.com/fight_guybrush"&gt;@fight_guybrush&lt;/a&gt;, I have written another twitter bot, &lt;a href="https://twitter.com/grauniad_news"&gt;@grauniad_news&lt;/a&gt;, an exact replica of the twitter feed for The Guardian newspaper, but with added spelling mistakes.&lt;/p&gt;
&lt;p&gt;(For any non-British readers, The Guardian is a UK newspaper with a reputation for spelling mistakes, among other things, giving rise to the nickname "The Grauniad".)&lt;/p&gt;
&lt;p&gt;This bot is a slightly more sophisticated project than the last one, which was just a python script triggered hourly by cron. The new bot is written in node.js, uses Twitter's streaming API to monitor and respond to tweets in real time, and is hosted on an Amazon Web Services t2.nano instance, launched using Ansible. I use the &lt;a href="https://github.com/ttezel/twit"&gt;Twit&lt;/a&gt; library to communicate with Twitter's API, and &lt;a href="https://github.com/foreverjs/forever"&gt;forever&lt;/a&gt; to ensure that the bot keeps running continuously.&lt;/p&gt;
&lt;p&gt;The method of generating badly spelled tweets is quite simple, but even so, it taught me some important lessons. The most important lesson: watch out for infinite loops in node. Early on, I found that the twitterbot would freeze after a seemingly random interval during which it worked normally. No errors were logged, and &lt;code&gt;forever list&lt;/code&gt; showed the program as still running.&lt;/p&gt;
&lt;p&gt;The problem turned out to be in my simple function for introducing misspellings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;misspellRandomWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Choose a first word, filtering out urls&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;iFirstWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iFirstWord&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iFirstWord&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;@&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;iFirstWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Choose second misspelled word, and make sure it isn&amp;#39;t the first or an URL&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;iSecondWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iSecondWord&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
            &lt;span class="nx"&gt;iSecondWord&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;iFirstWord&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
            &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iSecondWord&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;@&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;iSecondWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iFirstWord&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;swapRandomLetters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iFirstWord&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iSecondWord&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;swapRandomLetters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iSecondWord&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This worked most of the time, but would get into an infinite loop in certain circumstances - for example, if every word in the tweet starts with "@".&lt;/p&gt;
&lt;p&gt;I don't know if this is how a node.js expert would do it, but after resolving this bug, I started guarding my loops to catch any unforeseen conditions that would cause them to loop infinitely, and make sure that these were logged as errors.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;misspellRandomWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;nLoops&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Infinite loop error detection&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isSwappable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iFirstWord&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;iFirstWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Infinite loop detection&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;nLoops&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Infinite loop detected on sentence: &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you're interested, you can take a look at the code &lt;a href="https://github.com/samfrances/grauniad-node"&gt;on Github&lt;/a&gt;, or have a look at the bot &lt;a href="https://twitter.com/grauniad_news"&gt;on Twitter&lt;/a&gt;. Be warned, the bot is not sophisticated enough to know when a randomly misspelled headline might cause offence.&lt;/p&gt;</content><category term="Blog"></category><category term="javascript"></category><category term="node"></category><category term="aws"></category><category term="ansible"></category><category term="twitterbot"></category><category term="twitter"></category></entry><entry><title>A Monkey Island inspired twitterbot</title><link href="https://samfrances.co.uk/posts/2015/Aug/08/a-monkey-island-inspired-twitterbot/" rel="alternate"></link><published>2015-08-08T01:07:00+01:00</published><updated>2015-08-08T01:07:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2015-08-08:/posts/2015/Aug/08/a-monkey-island-inspired-twitterbot/</id><summary type="html">&lt;p&gt;Just for fun, I've made a rather silly twitterbot inspired by Monkey Island's 'insult sword-fighting'.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Just for fun, I've made a rather silly twitterbot inspired by Monkey Island's 'insult sword-fighting'. For example, tweet "&lt;a href="https://twitter.com/fight_guybrush"&gt;@fight_guybrush&lt;/a&gt; you fight like a dairy farmer" and it will reply "How appropriate! You fight like a cow!" within the hour. Other Monkey Island insults should work too. It uses the python tweepy library, which was a bit of a challenge as the documentation seems to be quite incomplete. I've used cron to schedule the bot script to run every hour. The code is Here: &lt;a href="https://github.com/samfrances/threepwood"&gt;https://github.com/samfrances/threepwood&lt;/a&gt;&lt;/p&gt;</content><category term="Blog"></category><category term="python"></category><category term="twitterbot"></category><category term="twitter"></category></entry><entry><title>Relaunching with Pelican</title><link href="https://samfrances.co.uk/posts/2015/Aug/06/relaunching-with-pelican/" rel="alternate"></link><published>2015-08-06T21:51:00+01:00</published><updated>2015-08-06T21:51:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2015-08-06:/posts/2015/Aug/06/relaunching-with-pelican/</id><summary type="html">&lt;p&gt;After realising that my django-powered site was running on a dangerously old version of django, I've decided to relaunch the site using pelican.&lt;/p&gt;</summary><content type="html">&lt;p&gt;After realising that my django-powered site was running on a dangerously old version of django, I've decided to relaunch the site using &lt;a href="http://docs.getpelican.com/en/3.6.2/index.html"&gt;pelican&lt;/a&gt;, a python static site generation framework. I reason that if it's good enough for django legend &lt;a href="http://www.pydanny.com/my-new-blog.html"&gt;Daniel Roy Greenfeld&lt;/a&gt; it's good enough for me.&lt;/p&gt;
&lt;p&gt;Most of the old posts aren't up yet, and I'm currently using a prepackaged theme, but I'm going to be working on adding the old content and writing my own pelican theme so that the site ends up looking like the old one. I'll also be adding disqus comments in the fullness of time, or working out a way of hosting my own comments.&lt;/p&gt;
&lt;p&gt;¡Hasta pronto!&lt;/p&gt;</content><category term="Blog"></category><category term="python"></category><category term="pelican"></category></entry><entry><title>MVC todo list demo</title><link href="https://samfrances.co.uk/posts/2014/Jan/29/mvc-todo-list-demo/" rel="alternate"></link><published>2014-01-29T00:00:00+00:00</published><updated>2014-01-29T00:00:00+00:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2014-01-29:/posts/2014/Jan/29/mvc-todo-list-demo/</id><summary type="html">&lt;p class="first last"&gt;Here's a simple javascript todo list application, which I created as an exercise in javascript MVC without a framework.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Here's a simple javascript todo list application, which I created as an exercise in javascript MVC without a framework.&lt;/p&gt;
&lt;p&gt;It is influenced by &lt;a class="reference external" href="http://alexatnet.com/articles/model-view-controller-mvc-javascript"&gt;this article&lt;/a&gt;. However, it differs by making each todo list item its own MVC triad.&lt;/p&gt;
&lt;p&gt;The source code is &lt;a class="reference external" href="https://github.com/samfrances/todo-mvc-demo"&gt;on github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I may explain the code in a later post.&lt;/p&gt;
&lt;iframe src="https://samfrances.co.uk/todo-mvc-demo/" style="border:none; width:550px; height:300px;"&gt;&lt;/iframe&gt;</content><category term="Blog"></category><category term="javascript"></category><category term="html5"></category><category term="tutorials"></category><category term="design-patterns"></category></entry><entry><title>A canvas drag and drop example</title><link href="https://samfrances.co.uk/posts/2012/Oct/08/canvas-drag-and-drop-example/" rel="alternate"></link><published>2012-10-08T00:00:00+01:00</published><updated>2012-10-08T00:00:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2012-10-08:/posts/2012/Oct/08/canvas-drag-and-drop-example/</id><summary type="html">&lt;p class="first last"&gt;I've recently unearthed an old piece of code which uses multiple layered HTML5 canvas elements to create a multi-layered drag and drop effect. The code is probably due some refinements, but I thought I'd post a link just for fun.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I've recently unearthed an old piece of code which uses multiple layered HTML5 canvas elements to create a multi-layered drag and drop effect. The code is probably due some refinements, but I thought I'd post a link just for fun.&lt;/p&gt;
&lt;p&gt;Drag and drop the characters and shapes (if you like):&lt;/p&gt;
&lt;iframe src="https://samfrances.github.io/canvas-dragndrop-demo/" style="border:none; width:550px; height:550px;"&gt;&lt;/iframe&gt;</content><category term="Blog"></category><category term="javascript"></category><category term="html5"></category><category term="canvas"></category><category term="tutorials"></category></entry><entry><title>Creating a blog with django, part 1: models</title><link href="https://samfrances.co.uk/posts/2012/Jul/03/creating-blog-django-part-1-models/" rel="alternate"></link><published>2012-07-03T23:43:00+01:00</published><updated>2012-07-03T23:43:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2012-07-03:/posts/2012/Jul/03/creating-blog-django-part-1-models/</id><summary type="html">&lt;p&gt;In this tutorial, I'll walk you through one possible way of creating a basic blog application using django 1.3's class-based generic views, built-in admin interface and syndication framework.&lt;/p&gt;</summary><content type="html">&lt;p&gt;In this tutorial, I'll walk you through one possible way of creating a basic blog application using django 1.3's class-based generic views, built-in admin interface and syndication framework.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note: this tutorial was written for django 1.3, and is therefore out of date.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I'll assume that you are familiar with some django fundamentals, specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creating a new project&lt;/li&gt;
&lt;li&gt;Creating a new app&lt;/li&gt;
&lt;li&gt;Setting up a database and configuring django's database settings&lt;/li&gt;
&lt;li&gt;The basics of django models, templates, views, urlconfs etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you aren't familiar with these things, then I suggest you work through the &lt;a href="https://docs.djangoproject.com/en/1.3/"&gt;'first steps' tutorial&lt;/a&gt; on the &lt;a href="https://www.djangoproject.com"&gt;django website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Start with an existing django project or create a new one, and make sure the database settings are up and running. We'll call the project directory &lt;code&gt;mysite&lt;/code&gt; for convenience. Create a new app called &lt;code&gt;blog&lt;/code&gt; and open &lt;code&gt;mysite/blog/models.py&lt;/code&gt;. Our models will include a Post class to represent blog posts and a Tag class to represent the different categories a post can be placed in:&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# models.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SlugField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique_for_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;publication_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;publication_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ManyToManyField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;Each &lt;code&gt;Tag&lt;/code&gt; instance has only one attribute, a &lt;code&gt;name&lt;/code&gt;, which is implemented as a &lt;code&gt;CharField&lt;/code&gt; with a unique value (&lt;code&gt;unique=True&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Post&lt;/code&gt; class is somewhat more interesting. It has a &lt;code&gt;title&lt;/code&gt; attribute, a main &lt;code&gt;body&lt;/code&gt; of text a &lt;code&gt;publication_date&lt;/code&gt; that defaults to the time at which the individual post was created.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;tags&lt;/code&gt; attribute implements a many-to-many relationship with &lt;code&gt;Tag&lt;/code&gt;; Each blog post can fall under many categories, and each category can include many blog posts.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;slug&lt;/code&gt; attribute is a &lt;code&gt;SlugField&lt;/code&gt;, which contains a 'slug' - a short, human-readable label for a blog post, used as part of its URL, and consisting only of letters, numbers, underscores and/or hyphens. Setting the &lt;code&gt;unique_for_date&lt;/code&gt; option to &lt;code&gt;'publication_date'&lt;/code&gt; ensures that no two blog posts can have both the same &lt;code&gt;publication_date&lt;/code&gt; and the same &lt;code&gt;slug&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, append &lt;code&gt;'mysite.blog'&lt;/code&gt; to &lt;code&gt;INSTALLED_APPS&lt;/code&gt; in &lt;code&gt;settings.py&lt;/code&gt;. Run &lt;code&gt;python manage.py validate&lt;/code&gt; to check that your models are valid, and create the database tables corresponding to your models by typing `python manage.py syncdb.&lt;/p&gt;
&lt;p&gt;Now you are ready for the next installment of this tutorial, which will intergrate our model with django's admin framework.&lt;/p&gt;</content><category term="Blog"></category><category term="python"></category><category term="django"></category><category term="tutorials"></category></entry><entry><title>Creating a blog with django, part 2: admin interface</title><link href="https://samfrances.co.uk/posts/2012/Jul/03/creating-blog-django-part-2-admin-interface/" rel="alternate"></link><published>2012-07-03T23:43:00+01:00</published><updated>2012-07-03T23:43:00+01:00</updated><author><name>Sam Frances</name></author><id>tag:samfrances.co.uk,2012-07-03:/posts/2012/Jul/03/creating-blog-django-part-2-admin-interface/</id><summary type="html">&lt;p class="first last"&gt;In part 1 of this series, we defined the models for our blog application. However, aside from &lt;cite&gt;python manage.py shell&lt;/cite&gt;, we don't yet have a way to create and edit blog posts or categories. For this, we need to enable django's admin interface.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In &lt;a class="reference external" href="https://samfrances.co.uk/posts/2012/Jul/03/creating-blog-django-part-1-models/"&gt;part2&lt;/a&gt; of this series, we defined the models for our blog application. However, aside from &lt;code&gt;python manage.py shell&lt;/code&gt;, we don't yet have a way to create and edit blog posts or categories. For this, we need to enable django's admin interface.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note: this tutorial was written for django 1.3, and is therefore out of date.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you haven't already done so, enable the admin interface by adding &lt;code&gt;django.contrib.admin&lt;/code&gt; to the &lt;code&gt;INSTALLED_APPS&lt;/code&gt; setting, and uncommenting the relevant lines in the main project urlconf, &lt;code&gt;mysite/urls.py&lt;/code&gt;.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf.urls.defaults&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;

&lt;span class="c1"&gt;# Uncomment the next two lines to enable the admin:&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autodiscover&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# Examples:&lt;/span&gt;
    &lt;span class="c1"&gt;# url(r&amp;#39;^$&amp;#39;, &amp;#39;mysite.views.home&amp;#39;, name=&amp;#39;home&amp;#39;),&lt;/span&gt;
    &lt;span class="c1"&gt;# url(r&amp;#39;^mysite/&amp;#39;, include(&amp;#39;mysite.foo.urls&amp;#39;)),&lt;/span&gt;

    &lt;span class="c1"&gt;# Uncomment the admin/doc line below to enable admin documentation:&lt;/span&gt;
    &lt;span class="c1"&gt;# url(r&amp;#39;^admin/doc/&amp;#39;, include(&amp;#39;django.contrib.admindocs.urls&amp;#39;)),&lt;/span&gt;

    &lt;span class="c1"&gt;# Uncomment the next line to enable the admin:&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^admin/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;Create a file called &lt;code&gt;admin.py&lt;/code&gt; in the directory containing the blog app.&lt;/p&gt;
&lt;p&gt;First, we need to import the admin module and the models which we wish to add to the admin interface.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# admin.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mysite.blog.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Tag&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;Next we register our models with the admin site.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;At this point if you start the test server and go to &lt;a class="reference external" href="http://127.0.0.1:8000/admin/"&gt;http://127.0.0.1:8000/admin/&lt;/a&gt; you will see that the Post and Tag models have been added.&lt;/p&gt;
&lt;img alt="Screenshot of admin interface" src="https://samfrances.co.uk/posts/2012/Jul/03/creating-blog-django-part-2-admin-interface/images/django-admin1.png" /&gt;
&lt;p&gt;However, if you create a couple of blog post and then try to view the list of posts, this is what you will see:&lt;/p&gt;
&lt;img alt="Screenshot of admin interface" src="https://samfrances.co.uk/posts/2012/Jul/03/creating-blog-django-part-2-admin-interface/images/django-admin2.png" /&gt;
&lt;img alt="Screenshot of admin interface" src="https://samfrances.co.uk/posts/2012/Jul/03/creating-blog-django-part-2-admin-interface/images/django-admin3.png" /&gt;
&lt;p&gt;In order to rectify this, we need to add a &lt;code&gt;__unicode__&lt;/code&gt; method to each of our models.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# models.py&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, we want the admin interface to generate the slug field automatically from the title field. To do this, we add the following lines to the admin.py file.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prepopulated_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;slug&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,)}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;We then add 'PostAdmin' as the second argument to the call which registers Post with the admin interface.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;9&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PostAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;Now you will see that the slug field is automatically filled in when you enter a title for your blog post. Here's the complete code for reference:&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# admin.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mysite.blog.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Tag&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prepopulated_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;slug&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,)}&lt;/span&gt;

&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PostAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# models.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SlugField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique_for_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;publication_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;publication_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ManyToManyField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__unicode__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;That concludes this installment. Next time we'll get to the real meat of the project - using Django's generic class-based views.&lt;/p&gt;
</content><category term="Blog"></category><category term="python"></category><category term="django"></category><category term="tutorials"></category></entry></feed>