<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
        <channel>
                <title>Nicholas Kariniemi</title>
                <description>Source code for blog</description>                
                <link>http://blog.ndk.io</link>
                <atom:link href="http://blog.ndk.io/feed.xml" rel="self" type="application/rss+xml" />
                
                        <item>
                                <title>Talk: JavaScript and Self Deception</title>
                                <description>&lt;!-- _includes/base.html --&gt;

&lt;p&gt;JavaScript and Self-Deception: Why TypeScript is not enough&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube-nocookie.com/embed/IvPBMEYxP-Y&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;JavaScript is often derided for callback hell and for quirks around equality, number handling, type coercions and the “this” keyword. This is largely a straw man argument. Developers work around these shortcomings through conventions, tools, and libraries and problems caused by these kinds of quirks come up infrequently. This talk instead aims to explore the best ways that I know how to write JavaScript- using good conventions, using functional libraries, using types, using promises and immutable data- and motivate PureScript as a way of pushing the limits and bringing development in the JavaScript ecosystem to the next level.&lt;/p&gt;

&lt;p&gt;Presented at the &lt;a href=&quot;https://www.reaktorbreakpoint.com/&quot;&gt;Reaktor Breakpoint 2018 conference&lt;/a&gt; on May 24, 2018.&lt;/p&gt;
</description>
                                <pubDate>Thu, 24 May 2018 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/javascript-and-self-deception.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/javascript-and-self-deception.html</guid>
                        </item>
                
                        <item>
                                <title>Fear, trust and PureScript</title>
                                <description>&lt;!-- _includes/base.html --&gt;

&lt;p&gt;In my previous post I argue that JavaScript is by default a low trust environment for programming, that ideas that build trust like optional typing, functional transformations, and immutability have severe trade-offs in JavaScript, and that choosing one of these ideas often means rejecting another. Despite that, JavaScript is a better environment than most popular languages for building trust, and the broader argument applies to most popular languages, typed or not.&lt;/p&gt;

&lt;p&gt;In PureScript you get types, immutability, and functional programming “for free”– the trade-offs aren’t as steep. This is largely true of a few other languages as well, but let’s see what our discussion of fear and trust from the previous post looks like in PureScript from the same two perspectives: understanding the shape of data and changing data.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.reaktor.com/blog/fear-trust-and-purescript/&quot;&gt;Read the full post on the Reaktor blog.&lt;/a&gt;&lt;/p&gt;
</description>
                                <pubDate>Thu, 08 Mar 2018 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/fear-trust-purescript.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/fear-trust-purescript.html</guid>
                        </item>
                
                        <item>
                                <title>Fear, trust and JavaScript</title>
                                <description>&lt;!-- _includes/base.html --&gt;

&lt;p&gt;As developers, we want to reduce fear of our code failing and increase trust in our code working well. Many developers working with JavaScript borrow useful ideas from functional programming and strongly-typed programming languages to reduce fear by transferring trust from developers to tools and the code. Ideas like optional types, functional transformations, and immutability can all help to write better JavaScript. When pulling these ideas together in JavaScript, however, they come with severe trade-offs, work together poorly, and ultimately fail in the goal of effectively transferring trust from developers to code and tools.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.reaktor.com/blog/fear-trust-and-javascript/&quot;&gt;Read the full post on the Reaktor blog.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=18314628&quot;&gt;Hacker News (2018-10-27)&lt;/a&gt;&lt;/p&gt;
</description>
                                <pubDate>Sun, 04 Mar 2018 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/fear-trust-javascript.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/fear-trust-javascript.html</guid>
                        </item>
                
                        <item>
                                <title>Refactoring 30000 lines of JS with types</title>
                                <description>&lt;!-- _includes/base.html --&gt;

&lt;p&gt;30000 lines of client-side JavaScript. No tests. Two difficult TV deployment platforms with poor tooling. Strong dependencies on poorly documented external APIs. The task: add support for a third TV platform to the two supported platforms and switch to a new backend with a different API. How can we do this without breaking things?&lt;/p&gt;

&lt;p&gt;One approach is to add tests incrementally- end to end tests to avoid breaking functionality and unit tests to aid with refactoring. But this was mostly user interface code on TV platforms with poor and varied tooling. Automated user interface testing on each platform would be challenging if possible at all. Manual testing on each platform was time consuming due to slow and inconsistent deployment tools.&lt;/p&gt;

&lt;p&gt;Some parts of the application could be tested and this was a part of how we made this change. But the more interesting idea we tried was to refactor with types.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.reaktor.com/blog/refactoring-30000-lines-js-types/&quot;&gt;Read the full post on the Reaktor blog.&lt;/a&gt;&lt;/p&gt;
</description>
                                <pubDate>Tue, 24 Jan 2017 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/refactoring-with-types.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/refactoring-with-types.html</guid>
                        </item>
                
                        <item>
                                <title>Wrapping JavaScript for PureScript</title>
                                <description>&lt;!-- _includes/base.html --&gt;

&lt;p&gt;How do I wrap a JavaScript API to use from PureScript? I had a chance to think about this while wrapping the &lt;a href=&quot;https://screeps.com&quot;&gt;Screeps game&lt;/a&gt; &lt;a href=&quot;http://support.screeps.com/hc/en-us/articles/203084991-API-Reference&quot;&gt;API&lt;/a&gt; so I could &lt;a href=&quot;https://github.com/nicholaskariniemi/purescript-screeps&quot;&gt;play Screeps using PureScript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 800px;&quot; src=&quot;/img/screeps_room.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Screeps is an MMO strategy game for programmers. You play by writing a JavaScript AI to control your minions. Everyone’s code runs 24/7 across hundreds of rooms in the same world.&lt;/p&gt;

&lt;p&gt;PureScript is a small strongly typed language that compiles to JavaScript.&lt;/p&gt;

&lt;p&gt;As a target for PureScript, the Screeps JavaScript API has some challenges. It makes heavy use of prototypes, a class hierarchy, and implicit effects. There’s some implicit mutable state. Performance is important, because the game caps the amount of CPU and memory you can use. But I gave a shot at wrapping it and I’m pretty happy with the result.&lt;/p&gt;

&lt;h2 id=&quot;how-do-i&quot;&gt;How do I…&lt;/h2&gt;
&lt;hr /&gt;

&lt;p&gt;As a relative newcomer to PureScript and strongly typed functional programming languages, I ran into a number of puzzles in wrapping the Screeps API. Here is what I ran into and how I ended up solving it.&lt;/p&gt;

&lt;h3 id=&quot;how-do-i-call-a-javascript-function-from-purescript&quot;&gt;How do I call a JavaScript function from PureScript?&lt;/h3&gt;

&lt;p&gt;Write a &lt;code class=&quot;highlighter-rouge&quot;&gt;foreign import&lt;/code&gt; type declaration for how you want to call the function and a corresponding JavaScript function that actually calls the function (and handles currying). Borrowing an example from the &lt;a href=&quot;https://github.com/purescript/purescript/wiki/FFI-tips&quot;&gt;PureScript Wiki&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;PureScript:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;joinPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilePath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilePath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilePath&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;JavaScript:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;joinPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'path'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In practice, though, writing these curried JavaScript functions is tedious and error-prone so you’ll want to use helper functions. The &lt;a href=&quot;https://github.com/purescript/purescript-functions&quot;&gt;purescript-functions&lt;/a&gt; library provides some basic wrappers to allow you to do this instead:&lt;/p&gt;

&lt;p&gt;PureScript:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;joinPathImpl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Fn2&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilePath&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilePath&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilePath&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;joinPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilePath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilePath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FilePath&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;joinPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runFn2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;joinPathImpl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;JavaScript:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;joinPathImpl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'path'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-do-i-wrap-functions-that-use-this&quot;&gt;How do I wrap functions that use &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt;?&lt;/h3&gt;

&lt;p&gt;The Screeps API is makes heavy use of object and singleton methods. For example, to make a creep attack a target you call&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;creep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;attack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;PureScript doesn’t have classes or the concept of &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt;. I represent this in PureScript as a function that takes in the object as the first argument. Calling the method on the object is handled with a little FFI helper function.&lt;/p&gt;

&lt;p&gt;PureScript:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;getActiveBodyparts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Creep&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BodyPartType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;getActiveBodyparts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisFn1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;getActiveBodyparts&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;runThisFn1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;JavaScript:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;runThisFn1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-do-i-wrap-constant-values&quot;&gt;How do I wrap constant values?&lt;/h3&gt;

&lt;p&gt;The Screeps API has a large number of constants. I wanted to expose these to PureScript in a type-safe way without much runtime overhead. The solution I used was to wrap constants in &lt;code class=&quot;highlighter-rouge&quot;&gt;newtypes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;PureScript:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;newtype&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ReturnCode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ReturnCode&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ReturnCode&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;err_not_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ReturnCode&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;err_no_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ReturnCode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;JavaScript:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err_not_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ERR_NOT_OWNER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err_no_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ERR_NO_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For those unfamiliar with newtypes, it provides a type alias that at runtime works the same as the underlying type. In theory this gives us the benefits of both worlds: compile-time type safety and run-time performance. If we never expose the constructor for the newtype, the only way for a user to obtain values e.g. of type &lt;code class=&quot;highlighter-rouge&quot;&gt;ReturnCode&lt;/code&gt; is through the ways we provide. This allows the use of only the constant values that we have defined.&lt;/p&gt;

&lt;p&gt;The type could also be defined with a &lt;code class=&quot;highlighter-rouge&quot;&gt;foreign import&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ReturnCode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This might be nicer for some cases, but makes it harder to write, say, type class implementations because you can’t rely on generics.&lt;/p&gt;

&lt;h3 id=&quot;how-do-i-wrap-a-class-hierarchy&quot;&gt;How do I wrap a class hierarchy?&lt;/h3&gt;

&lt;p&gt;Screeps has a class hierarchy a few classes deep:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;RoomObject
-&amp;gt; Creep
-&amp;gt; Structure
  -&amp;gt; OwnedStructure
    -&amp;gt; StructureContainer
    -&amp;gt; StructureSpawn
    -&amp;gt; ...etc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;PureScript doesn’t have classes, prototypes, or inheritance. In particular, wrapping this in PureScript involves at least two challenges:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;How can functions be defined once to work with any objects of a given class?&lt;/li&gt;
  &lt;li&gt;How can appropriately-typed sub-class instances be obtained when needed?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The solution to 2) is discussed elsewhere. My initial solution for 1) used a &lt;em&gt;type class&lt;/em&gt; hierarchy like the following:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IRoomObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;IRoomObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IStructure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;IStructure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IOwnedStructure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iRoomObjectRoomObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IRoomObject&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomObject&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Structure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iRoomObjectStructure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IRoomObject&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Structure&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iStructureStructure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;IStructure&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Structure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Generic functions can then be written that require any member of a certain type class:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;room&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;IRoomObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Room&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;room&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafeField&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;room&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This was less than ideal to use, though. Type classes aren’t very composable, they say, and in particular I was having difficulty defining ADTs that used type class instances. Type classes and ADTs don’t seem to mix well. You can’t, for instance, put a type class constraint on an ADT.&lt;/p&gt;

&lt;p&gt;I ended up borrowing a trick from &lt;a href=&quot;https://wiki.haskell.org/wikiupload/6/65/Wxhaskell.pdf&quot;&gt;wxHaskell&lt;/a&gt; using &lt;em&gt;phantom types&lt;/em&gt;. Instead of using type classes, I defined the class hierarchy using ADTs:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RawOwnedStructure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RawRoomObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RawStructure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RawContainer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RawRoomObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Structure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;RawStructure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;OwnedStructure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Structure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;RawOwnedStructure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Container&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Structure&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RawContainer&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;OwnedStructure&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RawController&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The types the user uses are defined as type synonyms on the left, e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;RoomObject&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Container&lt;/code&gt;. Parent classes contain a phantom type &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; which carries the class hierarchy information. On the right side we have the “raw” types which are not used directly by users but are seen in type errors.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;room&lt;/code&gt; function from above ends up like this:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;room&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Room&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;room&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafeField&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;room&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is simpler–no type class constraint– and works more nicely with ADTs and other constructs. How it works was a little tricky to me at first. Suppose we pass in a &lt;code class=&quot;highlighter-rouge&quot;&gt;Container&lt;/code&gt; to our &lt;code class=&quot;highlighter-rouge&quot;&gt;room&lt;/code&gt; function, which expects a &lt;code class=&quot;highlighter-rouge&quot;&gt;RoomObject a&lt;/code&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;Container&lt;/code&gt; is a type synonym. If we expand out the type synonyms it ends up looking as follows:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  Container
= Structure RawContainer
= RoomObject (RawStructure RawContainer)
= RawRoomObject (RawStructure RawContainer)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The actual type defined by &lt;code class=&quot;highlighter-rouge&quot;&gt;Container&lt;/code&gt; is&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;RawRoomObject (RawStructure RawContainer)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how this contains the class hierarchy. So a function taking in &lt;code class=&quot;highlighter-rouge&quot;&gt;RoomObject a&lt;/code&gt; will accept a &lt;code class=&quot;highlighter-rouge&quot;&gt;Container&lt;/code&gt; because, looking at the expansion above, &lt;code class=&quot;highlighter-rouge&quot;&gt;Container&lt;/code&gt; is equivalent to&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;RoomObject (RawStructure RawContainer) = RoomObject a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pretty neat. Also, all of these types are defined only at compile-time and disappear at run time. In the end the JS code just passes in objects like it always does, but with strong compile-time guarantees about correct objects being passed within our code.&lt;/p&gt;

&lt;h3 id=&quot;how-do-i-parse-function-return-values-into-the-correct-types&quot;&gt;How do I parse function return values into the correct types?&lt;/h3&gt;

&lt;p&gt;The Screeps API has a number of functions that take in a constant specifying a type and return an object of that type. For example, &lt;code class=&quot;highlighter-rouge&quot;&gt;room.find(FIND_CREEPS)&lt;/code&gt; finds all creeps in a room, &lt;code class=&quot;highlighter-rouge&quot;&gt;room.find(FIND_FLAGS)&lt;/code&gt; finds all flags, and so forth.&lt;/p&gt;

&lt;p&gt;In JavaScript this is not a problem, because you either assume the correct type or verify the type otherwise e.g. from a “type” field. For PureScript we would like to have this function return the appropriate type e.g. Creep or Flag.&lt;/p&gt;

&lt;p&gt;Phantom types to the rescue. It turns out that we can tag our &lt;code class=&quot;highlighter-rouge&quot;&gt;FIND_*&lt;/code&gt; constants with a phantom type and use this type tag to return an object of the correct type.&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;newtype&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindType&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;find_creeps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindType&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Creep&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;find_flags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindType&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Flag&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;find_construction_sites&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindType&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ConstructionSite&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;find_my_spawns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindType&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Spawn&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Room&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisFn1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;find&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Some constants are not associated with the most specific type, though. For example &lt;code class=&quot;highlighter-rouge&quot;&gt;room.find(FIND_STRUCTURES)&lt;/code&gt; returns all structures in a room, which may be one of any number of more specific structure types. We can’t know at compile time what types it is returning. In these cases I just return &lt;code class=&quot;highlighter-rouge&quot;&gt;Structure Unit&lt;/code&gt; and then provide functions for safe casting to more specific sub-types.&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;toContainer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Structure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Container&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;toContainer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafeCast&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;structure_container&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;unsafeCast&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;StructureType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Structure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;unsafeCast&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;struc&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;structureType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;struc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Just&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafeCoerce&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;struc&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This checks the type of the object from the &lt;code class=&quot;highlighter-rouge&quot;&gt;structureType&lt;/code&gt; field, which contains the string name of the type, and then uses &lt;code class=&quot;highlighter-rouge&quot;&gt;unsafeCoerce&lt;/code&gt; to force it to the correct type. This works similarly to TypeScript’s &lt;a href=&quot;https://basarat.gitbooks.io/typescript/content/docs/types/typeGuard.html&quot;&gt;user defined type guards&lt;/a&gt;. I’m not entirely happy with this solution but it works.&lt;/p&gt;

&lt;h3 id=&quot;how-do-i-handle-functions-that-return-null-or-undefined&quot;&gt;How do I handle functions that return null or undefined?&lt;/h3&gt;

&lt;p&gt;In JavaScript anything can be null or undefined and there’s no way of knowing. In PureScript nullable values are represented explicitly using &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; types. We can parse values to &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; values using a little FFI helper that just checks for null or undefined and returns the appropriate &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;PureScript:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;findClosestByPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomPosition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;findClosestByPath&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toMaybe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisFn1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;findClosestByPath&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findType&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;toMaybe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NullOrUndefined&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;toMaybe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runFn3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toMaybeImpl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Just&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;toMaybeImpl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Fn3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;NullOrUndefined&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;JavaScript:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toMaybeImpl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nothing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;just&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nothing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;just&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-do-i-handle-functions-that-throw-exceptions&quot;&gt;How do I handle functions that throw exceptions?&lt;/h3&gt;

&lt;p&gt;JavaScript functions can throw exceptions willy nilly. In PureScript it’s more common to use an &lt;code class=&quot;highlighter-rouge&quot;&gt;Either&lt;/code&gt; type, for instance, to return either the value we want or an error. So we catch the exception and return an &lt;code class=&quot;highlighter-rouge&quot;&gt;Either&lt;/code&gt; value:&lt;/p&gt;

&lt;p&gt;PureScript:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;findClosestByPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomPosition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Either&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;findClosestByPath&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errorToEither&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;toMaybe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisFn1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;findClosestByPath&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrapContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  
&lt;span class=&quot;n&quot;&gt;errorToEither&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Either&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;errorToEither&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errorToEitherImpl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Right&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;errorToEitherImpl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Either&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Either&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;Either&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;JavaScript:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;errorToEitherImpl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This admittedly gets a bit ugly in some cases such as the above example, where it turns out the Screeps API will either throw an exception, or return a value which is sometimes null or undefined. But that’s how the API is, and we keep our type guarantees.&lt;/p&gt;

&lt;p&gt;It turns out we can make this easier by using functions from &lt;a href=&quot;https://github.com/purescript/purescript-exceptions&quot;&gt;purescript-exceptions&lt;/a&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;try&lt;/code&gt; takes an effectful function that throws an exception and turns it into a function that catches exceptions and returns either the exception or the value you wanted. As we’ve handled all of the side effects, we can make it a pure (non-effectful) function again with &lt;code class=&quot;highlighter-rouge&quot;&gt;runPure&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;findClosestByPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomPosition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FindContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Either&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;findClosestByPath&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runPure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;closestByPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;kr&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;closestByPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toMaybe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisEffFn1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;findClosestByPath&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrapContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-do-i-pass-in-arguments-to-overloaded-functions&quot;&gt;How do I pass in arguments to overloaded functions?&lt;/h3&gt;

&lt;p&gt;For example, a number of Screeps functions take in either x and y coordinates or a RoomPosition (containing x and y coordinates) or a RoomObject (which exists at a certain x and y position).&lt;/p&gt;

&lt;p&gt;PureScript doesn’t have function overloading, but you can handle it fairly neatly using ADTs as in the following:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TargetPosition&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;TargetPt&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;TargetObj&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;RoomObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;TargetPos&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomPosition&lt;/span&gt;
  
&lt;span class=&quot;n&quot;&gt;getDirectionTo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomPosition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TargetPosition&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Direction&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;getDirectionTo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TargetPt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisFn2&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;getDirectionTo&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;getDirectionTo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TargetPos&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherPos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisFn1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;getDirectionTo&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherPos&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;getDirectionTo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TargetObj&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisFn1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;getDirectionTo&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, this requires the caller to wrap the argument in the appropriate type constructor e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;getDirectionTo pos (TargetObj obj)&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;how-do-i-pass-in-a-large-number-of-optional-parameters&quot;&gt;How do I pass in a large number of optional parameters?&lt;/h3&gt;

&lt;p&gt;Some Screeps functions take in a JavaScript object which contains a number of optional fields. In JavaScript this works neatly because you just pass in an object with the fields you want. In PureScript it’s not as clean because optional fields have to be represented explicitly e.g. as &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; values.&lt;/p&gt;

&lt;p&gt;One seemingly common pattern for handling this in Haskell is to provide a default options object that has all of the options with their defaults. Users can then modify the object to include the options that they want with a relatively short syntax.&lt;/p&gt;

&lt;p&gt;The tweak I made in my library is to make all of the options &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; types and to only include the &lt;code class=&quot;highlighter-rouge&quot;&gt;Just&lt;/code&gt; values in the final options object. The reason for this is that the way to indicate that an option is not used in the Screeps API is to exclude it. Including a default value, even &lt;code class=&quot;highlighter-rouge&quot;&gt;undefined&lt;/code&gt;, may not be the same semantically.&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;moveTo'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Creep&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TargetPosition&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MoveOptions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Eff&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MEMORY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ReturnCode&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;moveTo'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creep&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TargetPt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisEffFn3&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;moveTo&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creep&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectMaybes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MoveOptions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PathOptions&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reusePath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serializeMemory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Boolean&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;noPathFinding&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PathOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignoreCreeps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Boolean&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignoreDestructibleStructures&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Boolean&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignoreRoads&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Boolean&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignore&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;avoid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxOps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heuristicWeight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Number&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serialize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Boolean&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxRooms&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;moveOpts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MoveOptions&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;moveOpts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignoreCreeps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignoreDestructibleStructures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignoreRoads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;avoid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxOps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heuristicWeight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serialize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxRooms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reusePath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serializeMemory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;noPathFinding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nothing&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;selectMaybes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JsObject&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;selectMaybes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafePartial&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selectMaybesImpl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isJust&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fromJust&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;selectMaybesImpl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Maybe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JsObject&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JsObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;JavaScript:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selectMaybesImpl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isJust&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fromJust&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;newObj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isJust&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])){&lt;/span&gt;
                    &lt;span class=&quot;nx&quot;&gt;newObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fromJust&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;newObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-do-i-store-memory-in-a-type-safe-way&quot;&gt;How do I store memory in a type-safe way?&lt;/h3&gt;

&lt;p&gt;Screeps allow you to store 2 MB of your own memory. The default way this is handled is that you store objects on the &lt;code class=&quot;highlighter-rouge&quot;&gt;Memory&lt;/code&gt; object that are serializable to JSON. When your code is loaded on each tick, the memory gets deserialized. This deserialization also counts toward your CPU usage.&lt;/p&gt;

&lt;p&gt;You can also handle memory at a lower level using &lt;code class=&quot;highlighter-rouge&quot;&gt;RawMemory&lt;/code&gt;, which allows you to write your own serialization/deserialization in place of the default &lt;code class=&quot;highlighter-rouge&quot;&gt;JSON.stringify&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;JSON.parse&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In PureScript we want all of our loaded data to be typed, of course, so we need easy ways to store and load typed data.&lt;/p&gt;

&lt;p&gt;The approach I took is to use &lt;a href=&quot;https://github.com/purescript-contrib/purescript-argonaut&quot;&gt;purescript-argonaut&lt;/a&gt; to allow you to store any data for which you can write &lt;code class=&quot;highlighter-rouge&quot;&gt;DecodeJson&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;EncodeJson&lt;/code&gt; instances. You can use generics to avoid writing most of the boilerplate and you end up with something like the following to do encoding and decoding:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;derive&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genericCreepState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Generic&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CreepState&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decodeJsonCreepState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DecodeJson&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CreepState&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;where&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;decodeJson&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gDecodeJson&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encodeJsonCreepState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;EncodeJson&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CreepState&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;where&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;encodeJson&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gEncodeJson&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One interesting API quirk I ran into is that it turns out some functions implicitly modify the memory. This is not necessarily directly represented in the Screeps API, but in my PureScript wrapper those functions are tagged with the &lt;code class=&quot;highlighter-rouge&quot;&gt;MEMORY&lt;/code&gt; effect to warn of this. This brings me to the next item.&lt;/p&gt;

&lt;h3 id=&quot;how-do-i-handle-effectful-code&quot;&gt;How do I handle effectful code?&lt;/h3&gt;

&lt;p&gt;Screeps has a number of commands that have implicit side effects. When you give a creep a command, the command is implicitly executed at the end of the game tick. Some functions implicitly or explicitly modify stored memory. Others are time-dependent.&lt;/p&gt;

&lt;p&gt;In PureScript implicit side effects are made explicit to make the code easier to think about and refactor. The classic Haskell example is that if a function can launch nukes as a side effect, we would like to know about it.&lt;/p&gt;

&lt;p&gt;Well, Screeps has a function for launching nukes: &lt;code class=&quot;highlighter-rouge&quot;&gt;launchNuke&lt;/code&gt;. We save ourselves from accidentally launching nukes by tagging it with an effect:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;launchNuke&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Nuker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RoomPosition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Eff&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CMD&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ReturnCode&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;launchNuke&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runThisEffFn1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;launchNuke&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Returning an effect forces all callers to recognize the fact that this function has side effects. It makes it clearer what functions actually do and makes it easier to separate the simpler side-effect free functions from the more difficult side-effectful functions.&lt;/p&gt;

&lt;p&gt;In the end I defined four effects:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cd&quot;&gt;-- | Execute a Screeps command before the next tick as a side effect e.g. to move a creep.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CMD&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;

&lt;span class=&quot;cd&quot;&gt;-- | Get or set mutable memory&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MEMORY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;

&lt;span class=&quot;cd&quot;&gt;-- | Global scope is cleared periodically, so values depending on global variables&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;-- | like Game and Memory need to be fetched dynamically. This effect enforces this.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TICK&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;

&lt;span class=&quot;cd&quot;&gt;-- | Time-dependent functions have different output depending on when they are called.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foreign&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TIME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-do-i-get-performance&quot;&gt;How do I get performance?&lt;/h3&gt;

&lt;p&gt;Code performance in Screeps is important. You are allowed a certain CPU/processing cap and if you exceed the cap your code is killed. The cap rises according to your level.&lt;/p&gt;

&lt;p&gt;PureScript doesn’t have a runtime and compilation mostly includes only the bits of libraries that you use. Overall performance using this PureScript library is good enough and I suspect depends more on the user’s code than the library itself. Anecdotally my AI code performs close to its CPU cap (e.g. 40/40 CPU used for 2 rooms) where others seem to be able to perform much under the CPU cap (e.g. 10-20 CPU for 2 rooms). This is probably because the code is mostly a pure function with side effects at the edges. More performant code might spend more time in the Eff monad’s &lt;code class=&quot;highlighter-rouge&quot;&gt;do&lt;/code&gt; notation. But it’s not something I’ve looked into much yet.&lt;/p&gt;

&lt;p&gt;There are a few things I do in the library and in my code to try to eke out a little more performance:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use type aliases or newtype wrapping to only use types at compile time.&lt;/li&gt;
  &lt;li&gt;Use pattern-matching where possible in place of e.g. chained Maybe values. In Haskell I suspect &lt;code class=&quot;highlighter-rouge&quot;&gt;(Just a) &amp;lt;|&amp;gt; somethingElse&lt;/code&gt; wouldn’t execute &lt;code class=&quot;highlighter-rouge&quot;&gt;somethingElse&lt;/code&gt;, but in PureScript you need to handle the laziness yourself. Pattern-matching maps directly to performant nested if statements.&lt;/li&gt;
  &lt;li&gt;Avoid inlining functions.&lt;/li&gt;
  &lt;li&gt;Use lazily-evaluated values via &lt;a href=&quot;https://github.com/purescript/purescript-lazy&quot;&gt;purescript-lazy&lt;/a&gt; where necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;Writing Screeps code in PureScript is fun. Tick-based gameplay maps nicely to modeling your AI as a pure function from memory and game state to new memory and commands to execute, with side effects executed only at the edges. Type-safety saves you from breaking everything in late night refactoring or frantic live-coding updates to save your base. Though to be fair it didn’t quite save my base from annihilation. Twice.&lt;/p&gt;

&lt;p&gt;Wrapping JavaScript in PureScript requires more thought than writing TypeScript or Flow definitions for JavaScript. To do a proper job of it you have to &lt;em&gt;grok&lt;/em&gt; the code you are wrapping. But it also allows you to create better compile-time guarantees about how your code works, which translates to more reliable and refactorable code.&lt;/p&gt;

&lt;p&gt;Compared to compile-to-js languages like ClojureScript or GHCJS, it’s likely easier to get performant PureScript code because PureScript doesn’t have a runtime. The low-level tools like mutability and JavaScript hacks are also there to be used if necessary.&lt;/p&gt;

&lt;p&gt;The Screeps PureScript library is &lt;a href=&quot;https://github.com/nicholaskariniemi/purescript-screeps&quot;&gt;here&lt;/a&gt;. Go forth and screep, my friend.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;
Edit 2016-9-13: Added comments about purescript-exceptions and defining constants with &lt;code class=&quot;highlighter-rouge&quot;&gt;foreign import data&lt;/code&gt;.
&lt;/small&gt;&lt;/p&gt;
</description>
                                <pubDate>Mon, 05 Sep 2016 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/purescript-ffi.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/purescript-ffi.html</guid>
                        </item>
                
                        <item>
                                <title>Why CSP matters II: How do I know sync works?</title>
                                <description>&lt;!-- _includes/base.html --&gt;

&lt;p&gt;How does one know if their synchronization algorithm works? I have a real-time synced grocery list app. In the &lt;a href=&quot;/why-csp-matters1.html&quot;&gt;last post&lt;/a&gt; I attempted to scrape by on synchronization by implementing differential sync based on an intelligent-sounding paper. In the end my implementation had subtle bugs and was difficult to debug. I hoped to find a better approach to the problem using the CSP language.&lt;/p&gt;

&lt;p&gt;One challenge is just to make the problem understandable. If the CSP language can represent the problem clearly and succinctly, it already makes it easier to manipulate the algorithm, discover problem cases, and implement it properly. But can CSP actually prove that synchronization works? And perhaps more importantly, is it worth the effort?&lt;/p&gt;

&lt;h2 id=&quot;csp-and-cspm&quot;&gt;CSP and CSP&lt;sub&gt;M&lt;/sub&gt;&lt;/h2&gt;

&lt;p&gt;CSP has a long and rich history in research and there are doubtless countless uses and resources of which I haven’t the foggiest. The books I found that I liked were Roscoe’s &lt;a href=&quot;http://www.cs.ox.ac.uk/bill.roscoe/publications/68b.pdf&quot;&gt;“The Theory and Practice of Concurrency”&lt;/a&gt; and Steve Schneider’s &lt;a href=&quot;http://www.computing.surrey.ac.uk/personal/st/S.Schneider/books/CRTS.pdf&quot;&gt;“Concurrent and Real Time Systems: the CSP approach”&lt;/a&gt;, in addition to &lt;a href=&quot;http://www.usingcsp.com/cspbook.pdf&quot;&gt;The CSP Book&lt;/a&gt; from Hoare himself. But the important thing for me came from Roscoe’s book, which introduced me to the CSP&lt;sub&gt;M&lt;/sub&gt; language.&lt;/p&gt;

&lt;p&gt;CSP is a mathematical language for representing processes that talk to each other. It allows you to model problems and then make assertions about those models. Modeling can be useful, as it helps you think about the problem, and assertions are grand. If you can make it work. Eventually. But to me this felt difficult to apply and not very well scalable to more complicated problems.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto 20px auto; width: 500px; max-width: 100%&quot; src=&quot;./img/up_down_equation.png&quot; /&gt;
&lt;img style=&quot;display: block; margin: 0 auto; width: 150px; max-width: 100%&quot; src=&quot;./img/up_down_diagram.png&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;b&gt;What CSP looks like&lt;/b&gt;&lt;/center&gt;

&lt;p&gt;But CSP&lt;sub&gt;M&lt;/sub&gt;, as it turns out, is just a programming language. It’s a modern programming language, inspired by languages like Miranda and Haskell. But don’t be too worried if Haskell doesn’t inspire. It is a much stripped down and focused version of this.&lt;/p&gt;

&lt;p&gt;So instead of writing intricate mathematical algorithms, we can just write programs and debug and test them. Better still, the CSP&lt;sub&gt;M&lt;/sub&gt; “IDE”, the &lt;a href=&quot;https://www.cs.ox.ac.uk/projects/fdr/&quot;&gt;FDR3 CSP refinement checker&lt;/a&gt;, comes with a REPL. You can write algorithms like you write code, load it into the REPL, explore it visually, and tweak it and see if it still compiles and passes assertions. Let’s try it out.&lt;/p&gt;

&lt;h2 id=&quot;a-simple-algorithm-syncing-broccolis-with-cspm&quot;&gt;A simple algorithm: syncing broccolis with CSP&lt;sub&gt;M&lt;/sub&gt;&lt;/h2&gt;

&lt;p&gt;The goal is to keep grocery list items in sync between different clients, connected to a central database via one or more servers. Here is what the algorithm looks like, using broccoli:&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto 20px auto; width: 500px; max-width: 100%&quot; src=&quot;./img/sync_through_db.png&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;b&gt;Broccoli algorithm&lt;/b&gt;&lt;/center&gt;

&lt;p&gt;The rest of this section walks through representing the broccoli algorithm using CSP&lt;sub&gt;M&lt;/sub&gt;. You can follow along by &lt;a href=&quot;https://www.cs.ox.ac.uk/projects/fdr/&quot;&gt;downloading FDR3&lt;/a&gt; and &lt;a href=&quot;https://github.com/hoodunit/grub/blob/master/spec/sync.csp&quot;&gt;my complete CSP&lt;sub&gt;M&lt;/sub&gt; program&lt;/a&gt; and opening the program using FDR3 (“&lt;code class=&quot;highlighter-rouge&quot;&gt;fdr3 sync.csp&lt;/code&gt;”). Useful FDR3 commands to start out: &lt;code class=&quot;highlighter-rouge&quot;&gt;:help&lt;/code&gt; to see what commands are available and &lt;code class=&quot;highlighter-rouge&quot;&gt;:reload&lt;/code&gt; to reload the current file after changes have been made.&lt;/p&gt;

&lt;h3 id=&quot;client-sync-process&quot;&gt;Client sync process&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NUM_CLIENTS = 2
NUM_DB_STATES = 5
CLIENTS = {0..NUM_CLIENTS-1}
TIMES = {0..NUM_DB_STATES-1}

channel up, down, render:CLIENTS.TIMES

CLIENT(i, t) = 
   up!i!t -&amp;gt; CLIENT'(i, t)
[] CLIENT'(i, t)

CLIENT'(i, t) = 
   down!i?server_t
   -&amp;gt; render!i!server_t
   -&amp;gt; CLIENT(i, server_t)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the client, I define two different processes which essentially behave as one process: CLIENT and CLIENT’. The CLIENT process either uploads a diff to the server when the user performs an action or behaves just like the CLIENT’ process. The CLIENT’ process takes a diff from the server, renders it, and then behaves like the CLIENT process. The &lt;em&gt;t&lt;/em&gt; variable represents the client’s state and is incremented each time data is saved to the database. The client process has three events it can engage in: &lt;code class=&quot;highlighter-rouge&quot;&gt;up&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;down&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;render&lt;/code&gt;. Each event has a client number associated with it to identify the client and a state representing the database “time”.&lt;/p&gt;

&lt;p&gt;How do we know if we implemented the process correctly? You can interactively explore the states of the process using &lt;code class=&quot;highlighter-rouge&quot;&gt;:probe CLIENT(0, 0)&lt;/code&gt;, which gives you a dialog like this:&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto 20px auto; max-width: 100%&quot; src=&quot;./img/client_probe_commands.png&quot; /&gt;
&lt;img style=&quot;display: block; margin: 0 auto 20px auto; max-width: 100%&quot; src=&quot;./img/client_probe.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The probe dialog shows the possible actions our process can engage in at any given state. It’s easy to run through the diagram, try out different scenarios, and assure oneself that the process works how it was supposed to.&lt;/p&gt;

&lt;h3 id=&quot;server-sync-process&quot;&gt;Server sync process&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SERVER(i, client_t) = 
   up!i?server_t
   -&amp;gt; save!i
   -&amp;gt; saved!i?new_server_t
   -&amp;gt; down!i!new_server_t
   -&amp;gt; SERVER(i, new_server_t)
[] report_queue?j:diff(CLIENTS,{i})?new_server_t
   -&amp;gt; if new_server_t == client_t
      then SERVER(i, client_t)
      else down!i!client_t!new_server_t
        -&amp;gt; SERVER(i, new_server_t)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The server sync process does two things. First, it takes in input from the client via the &lt;em&gt;up&lt;/em&gt; event, saves it to the database, and sends any remaining changes back down to the client. Second, it reads in changes from other clients via (our representation of) the &lt;a href=&quot;http://docs.datomic.com/transactions.html#sec-5-3&quot;&gt;Datomic report queue&lt;/a&gt; and sends them down to the client, if they haven’t already been handled.&lt;/p&gt;

&lt;h3 id=&quot;database-process&quot;&gt;Database process&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DB(t) =
   save?i
   -&amp;gt; saved!i!next_t(t)
   -&amp;gt; DB(next_t(t))

REPORTQUEUE(i) =
   saved?j:diff(CLIENTS,{i})?t
   -&amp;gt; REPORTQUEUE'(i, j, t)
REPORTQUEUE'(i, j, t) =
   saved?j':diff(CLIENTS,{i})?new_t
   -&amp;gt; REPORTQUEUE'(i, j', new_t)
[] report_queue!j!t -&amp;gt; REPORTQUEUE(i)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The database process DB is very simple. It takes a new state to save from the server sync process &lt;em&gt;i&lt;/em&gt; and saves it, returning the id &lt;em&gt;t&lt;/em&gt; representing the database after being saved. The report queue process REPORTQUEUE takes these saved events and puts them in a sliding buffer of size 1 for other server sync processes to consume. This is how changes are passed from one client to another. It is a sliding buffer of size 1 because we only care about the latest state of the database. If we don’t have time to process an event in between it doesn’t matter - we just diff with the latest state we have available and send that down to the client.&lt;/p&gt;

&lt;h3 id=&quot;process-wiring&quot;&gt;Process wiring&lt;/h3&gt;

&lt;p&gt;Finally, everything is wired together in the end.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SERVER_WITH_REPORTS(i, t0) = (SERVER(i, t0) [|{| report_queue |}|] REPORTQUEUE(i))
CONN(i, t0) = CLIENT(i, t0) [|{| up.i, down.i |}|] SERVER_WITH_REPORTS(i, t0)
CONNS(t0) = [| productions(saved) |] i:CLIENTS @ CONN(i, t0)
SYSTEM = DB(0) [|{| save, saved |}|] CONNS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All processes start with an initial state &lt;code class=&quot;highlighter-rouge&quot;&gt;t=0&lt;/code&gt;. The CLIENT process communicates with the SERVER process with the &lt;em&gt;up&lt;/em&gt; and &lt;em&gt;down&lt;/em&gt; events. The server process in turn communicates with the REPORTQUEUE process via the &lt;em&gt;report_queue&lt;/em&gt; event. Each client/server connection pair, represented by the CONN process, receives &lt;em&gt;saved&lt;/em&gt; events synchronously from the database. Finally, all of the connections communicate with the database via the &lt;em&gt;save&lt;/em&gt; and &lt;em&gt;saved&lt;/em&gt; events.&lt;/p&gt;

&lt;h3 id=&quot;putting-it-all-together&quot;&gt;Putting it all together&lt;/h3&gt;

&lt;p&gt;As I left out a few details, here is the full example:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NUM_CLIENTS = 2
NUM_DB_STATES = 10
CLIENTS = {0..NUM_CLIENTS-1}
TIMES = {0..NUM_DB_STATES-1}

channel up, down, render, saved, report_queue:CLIENTS.TIMES
channel save:CLIENTS

next_t(t) = (t + 1) % NUM_DB_STATES

CLIENT(i, t) = 
   up!i!t -&amp;gt; CLIENT'(i, t)
[] CLIENT'(i, t)

CLIENT'(i, t) = 
   down!i?server_t
   -&amp;gt; render!i!server_t
   -&amp;gt; CLIENT(i, server_t)

SERVER(i, client_t) = 
   up!i?server_t
   -&amp;gt; save!i
   -&amp;gt; saved!i?new_server_t
   -&amp;gt; down!i!new_server_t
   -&amp;gt; SERVER(i, new_server_t)
[] report_queue?j:diff(CLIENTS,{i})?new_server_t
   -&amp;gt; if new_server_t == client_t
      then SERVER(i, client_t)
      else down!i!new_server_t
        -&amp;gt; SERVER(i, new_server_t)

DB(t) =
   save?i
   -&amp;gt; saved!i!next_t(t)
   -&amp;gt; DB(next_t(t))

REPORTQUEUE(i) =
   saved?j:diff(CLIENTS,{i})?t
   -&amp;gt; REPORTQUEUE'(i, j, t)
REPORTQUEUE'(i, j, t) =
   saved?j':diff(CLIENTS,{i})?new_t
   -&amp;gt; REPORTQUEUE'(i, j', new_t)
[] report_queue!j!t -&amp;gt; REPORTQUEUE(i)

SERVER_WITH_REPORTS(i, t0) = (SERVER(i, t0) [|{| report_queue |}|] REPORTQUEUE(i))
CONN(i, t0) = CLIENT(i, t0) [|{| up.i, down.i |}|] SERVER_WITH_REPORTS(i, t0)
CONNS(t0) = [| productions(saved) |] i:CLIENTS @ CONN(i, t0)
SYSTEM = DB(0) [|{| save, saved |}|] CONNS(0)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;center&gt;&lt;b&gt;Sync algorithm in CSP&lt;/b&gt;&lt;/center&gt;

&lt;p&gt;So now we have a model representing our implementation. This is not the simplest model we could have come up with, nor is it the most complex. There are a lot of details that we left out. Client-server connections are not actually synchronous, even though we represent them as so. There is a certain amount of buffering in the system that is not modeled. We consider the diffing and patching algorithm only in very vague terms, passing around a single variable &lt;em&gt;t&lt;/em&gt; to represent the database state. We don’t represent the possibility of lost packets or of the client disconnecting. We don’t represent users making changes as a separate event, instead tying it in directly to the moment the changes are sent to the server via the &lt;em&gt;up&lt;/em&gt; event.&lt;/p&gt;

&lt;p&gt;This is a simplified representation of the problem but already a useful one. Our algorithm is concise and unambiguous. We can visualize the algorithm and iteratively exploring the states of the algorithm using FDR3’s &lt;code class=&quot;highlighter-rouge&quot;&gt;:probe SYSTEM&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto 20px auto; max-width: 100%&quot; src=&quot;./img/system_probe.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But beyond that, this allows us to prove things about our algorithm.&lt;/p&gt;

&lt;h2 id=&quot;proving-synchronization-works-sync-one-broccoli&quot;&gt;Proving synchronization works: Sync one broccoli&lt;/h2&gt;

&lt;p&gt;What would we like to say about our algorithm using this model? Well, we want to prove that syncing “works”. What does it mean for syncing to “work”? Let’s start simple. Syncing works if I enter a broccoli on my phone and, at some point later, my wife sees broccoli on her phone. No other users or vegetables are involved.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto 20px auto; width: 400px; max-width: 100%&quot; src=&quot;./img/sync_one.png&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;b&gt;Syncing a single broccoli&lt;/b&gt;&lt;/center&gt;

&lt;p&gt;How can we verify this using our CSP&lt;sub&gt;M&lt;/sub&gt; model?&lt;/p&gt;

&lt;h3 id=&quot;1-allow-only-one-change-from-one-client&quot;&gt;1) Allow only one change from one client&lt;/h3&gt;

&lt;p&gt;First, we want to restrict our implementation to allow only a single change from a single user. We do this by defining a processes that perform according to this specification and then forcing our implementation to synchronize with these processes on the relevant events.&lt;/p&gt;

&lt;p&gt;Our first process restricts the implementation to only a single change:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MaxInputs(0) = SKIP
MaxInputs(n) = up?i?t -&amp;gt; MaxInputs(n-1)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The MaxInputs process performs &lt;em&gt;n&lt;/em&gt; changes from any client &lt;em&gt;i&lt;/em&gt; and then stops. If we set &lt;code class=&quot;highlighter-rouge&quot;&gt;n=1&lt;/code&gt; then it only allows one change. The question mark in &lt;code class=&quot;highlighter-rouge&quot;&gt;up?i?t&lt;/code&gt; indicates that we are taking in &lt;em&gt;i&lt;/em&gt; and &lt;em&gt;t&lt;/em&gt; as inputs, which in this case we ignore. The SKIP event represents successful termination of the process.&lt;/p&gt;

&lt;p&gt;A second process specifies that the input must come from a specific client:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;OnlyClient(i) = up!i?t -&amp;gt; OnlyClient(i)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The OnlyClient process takes in inputs infinitely, but only from client &lt;em&gt;i&lt;/em&gt;. The important syntactical difference here is in &lt;code class=&quot;highlighter-rouge&quot;&gt;up!i?t&lt;/code&gt;. The exclamation point indicates that we are forcing &lt;em&gt;i&lt;/em&gt; to be the specified client &lt;em&gt;i&lt;/em&gt; i.e. only the one client is allowed to make changes.&lt;/p&gt;

&lt;p&gt;To force our algorithm to behave according to these constraints, we tell it to synchronize with our constraint processes on the &lt;code class=&quot;highlighter-rouge&quot;&gt;up&lt;/code&gt; event:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;OneInputFromClientZero = (OnlyClient(0) [|{| up |}|] MaxInputs(1)) [|{| up |}|] SYSTEM
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The system can only engage in &lt;code class=&quot;highlighter-rouge&quot;&gt;up&lt;/code&gt; events when both of our other processes are ready to engage in &lt;code class=&quot;highlighter-rouge&quot;&gt;up&lt;/code&gt; events. Thus the system is only allowed to take in a single input from client 0 and then cannot take any more inputs. Our constraints only affect the inputs, so it can still freely do whatever else that it wants, like upload to the server or save changes to the database. To verify that our constrained process works properly you can explore it with &lt;code class=&quot;highlighter-rouge&quot;&gt;:probe OneInputFromClientZero&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;2-assert-that-the-change-makes-it-to-the-other-client&quot;&gt;2) Assert that the change makes it to the other client&lt;/h3&gt;

&lt;p&gt;Now, we want to show that a change on client 0 will make it to client 1. What does this look like from the outside i.e. what sequence of events would we expect to see if this works? We would expect to see an input/upload event from client 0 followed by a render event from client 1: &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;up.0.0, render.1.1&amp;gt;&lt;/code&gt;. In between a bunch of other events happen that we don’t care about right now.&lt;/p&gt;

&lt;p&gt;Here is a process that does that:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SyncOneInput = up.0.0 -&amp;gt; render.1.1 -&amp;gt; STOP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The SyncOneInput process is our &lt;em&gt;specification&lt;/em&gt;. This is how we hope our system will behave. We hope it will take input from client 0, render the state on client 1, and stop.&lt;/p&gt;

&lt;p&gt;Finally, we prove that our system fulfills this specification by asserting that it does:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;assert SyncOneInput [FD= OneInputFromClientZero 
  \diff(Events, union(productions(up.0), {render.1.1}))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What we assert here is that our constrained implementation OneInputFromClientZero &lt;em&gt;must&lt;/em&gt; behave according to our specification SyncOneInput. If our specification takes in one input and sends it up to the server, our implementation must take in one input and send it up to the server. If our specification then, at some point after this, renders a new state on the other client, our implementation must also at some point render a new state on the other client.&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;We can run the assertion through FDR3 and see that it works. Syncing one broccoli works. This is like a simple end to end test for our algorithm. But let’s make it more interesting.&lt;/p&gt;

&lt;h2 id=&quot;proving-synchronization-works-sync-many-vegetables&quot;&gt;Proving synchronization works: Sync many vegetables&lt;/h2&gt;

&lt;p&gt;This app isn’t very useful if it only sends one broccoli from one person to another. What if I throw in tomatoes and my wife throws in milk and our friends who are visiting want zucchini and butternut squash?&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto 20px auto; width: 700px; max-width: 100%&quot; src=&quot;./img/sync_many2.png&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;b&gt;Syncing many vegetables from many people&lt;/b&gt;&lt;/center&gt;

&lt;p&gt;Instead of only allowing one client to make a single change, let’s allow any client to make any number of changes, up to maximum number of changes. They stop adding things at some point. Then we verify that each client properly renders the final changes. This is essentially a definition for eventual consistency.&lt;/p&gt;

&lt;p&gt;First, we need to allow a larger number of inputs &lt;em&gt;n&lt;/em&gt; instead of just one input. We can use our MaxInputs process from above:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MaxInputSystem(n) = SYSTEM [|{| up |}|] MaxInputs(n)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we need to define what we actually want to see in our specification. If the system is restricted to &lt;em&gt;n&lt;/em&gt; inputs, what we would like to see after all of the inputs have been entered is for each of the connected clients to render the final state. For example, if 9 inputs are allowed, we allow 9 inputs from arbitrary clients and then expect to see all of the clients to render the state after 9 changes i.e. engage in the &lt;em&gt;render.i.9&lt;/em&gt; event. Conceptually this is still a very simple specification, though the syntax gets more involved:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sequences({}) = {&amp;lt;&amp;gt;}
sequences(a) = {&amp;lt;z&amp;gt;^z' | z &amp;lt;- a, z' &amp;lt;- sequences(diff(a, {z}))}

renderAll(sequence, t) = ; i:sequence @ render!i.t -&amp;gt; SKIP

SyncAll(n) = |~| i:CLIENTS @ up!i!0 -&amp;gt; SyncAll'(n, n-1)
SyncAll'(n, 0) = |~| renderSeq:sequences(CLIENTS) @ renderAll(renderSeq, n); STOP
SyncAll'(n, m) = |~| i:CLIENTS, t:TIMES @ up!i!t -&amp;gt; SyncAll'(n, m-1)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Without going into the syntax, this allows input nondeterministically in any order from any client and then requires them all to render the final state in nondeterministic order.&lt;/p&gt;

&lt;p&gt;The final assertion can be run with FDR3 and it passes. We can even change the number of clients from two to four and see that it still passes.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;assert SyncAll(9) [FD= MaxInputSystem(9)
  \diff(Events, union(productions(up), {render.i.9 | i &amp;lt;- CLIENTS}))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What have we proved here? A similar end-to-end test might work as follows. We pull up four browsers. We randomly make &lt;em&gt;n&lt;/em&gt; changes on a random window, stop for some period, and then verify that all of the browsers end up in the same state. This is a useful test. It shows that the system, including the parts outside of the algorithm, works in this specific situation. It means you probably didn’t screw up anything majorly.&lt;/p&gt;

&lt;p&gt;But ultimately that test is also very limited. It randomly tests one single possible timing of events out of a huge space of possible timings of events. The exact behavior of the system, and whether it works or not, may depend on very specific timings. The test only tests one of these timings. The next time it runs it tests a different timing of events. Even if it fails, we have no good way of repeating the test. We just know that our system is “broken”. Somehow.&lt;/p&gt;

&lt;p&gt;Our CSP assertion is much more powerful than an end-to-end test. Instead of testing one specific situation, it tests every single combination of timings that can occur with our system and says that in &lt;em&gt;all&lt;/em&gt; cases our system will end up in sync. If the assertion passes, we know that the algorithm should work for any timing of events. If the assertion fails, it provides a single clear example of a sequence of events that causes the assertion to fail.&lt;/p&gt;

&lt;h2 id=&quot;conclusion-does-my-sync-algorithm-work&quot;&gt;Conclusion: Does my sync algorithm work?&lt;/h2&gt;

&lt;p&gt;Our “eventual consistency” assertion says that given &lt;em&gt;n&lt;/em&gt; connected clients, if we allow them to arbitrarily make &lt;em&gt;k&lt;/em&gt; changes, eventually all clients will render those changes and stop changing. In this case I tested it with 4 clients and 9 changes, but this can be arbitrarily adjusted to any number of clients or changes as long as computing power suffices. On my computer two clients take a moment to verify, three take 36 seconds, and four take over twenty minutes. The parameters can be adjusted until you’re sure that the algorithm actually does work.&lt;/p&gt;

&lt;p&gt;Note some of the things that this assertion does &lt;em&gt;not&lt;/em&gt; say. It does not make any claims about timing. In theory synchronization could take a huge (finite) number of steps. Nor does not make any claims about the diffing/patching algorithm itself. We just assume it works.&lt;/p&gt;

&lt;p&gt;The second, important part of verifying that the sync algorithm works is to make sure that the model actually represents our app in the important ways. It doesn’t help to write one model and implement another. It’s also easy to overlook real world aspects of the system like buffering or disconnections. Some of these could be handled simply enough outside of the model, as long as our model assumptions hold. Others might need to be included into the model.&lt;/p&gt;

&lt;p&gt;Because the Clojure core.async library is based on CSP, it is a straightforward process to translate our model into Clojure. My final sync algorithm as it currently stands looks as follows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client sync process&lt;/strong&gt; (&lt;a href=&quot;https://github.com/hoodunit/grub/blob/b31489b2b88f4d9fdc3c245e1f4c1fcc32ab3880/src/cljc/grub/client_sync.cljc#L11&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;

&lt;div class=&quot;small-code&quot;&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sync-client!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initial-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-server&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state-buffer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-syncs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initial-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initial-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;awaiting-ack?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;awaiting-ack?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-syncs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-syncs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;a/alts!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when-not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nil?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;condp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-syncs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Full sync, new ui state tag:&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state-buffer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-ui-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;state/state=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-ui-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Changes, current ui state tag:&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-ui-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-server&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;event/diff-msg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-ui-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-ui-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:shadow-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; Our state is based on what they think it's based on&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; Update server state we are based on&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;diff/patch-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                              &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; Apply changes directly to UI&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-client-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;swap!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diff/patch-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Applied diff, new ui tag:&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-client-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Applied diff, new server tag:&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-server-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                          &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; If there are any diffs to reconcile, they will come back through input buffer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-client-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

                        &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; State mismatch, do full sync&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-server&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;event/full-sync-request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; Need to make sure we are in sync, send diff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Reconnected, sending diff&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-server&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;event/diff-msg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bug: Received a sync event on an unknown channel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start-sync!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-server&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-ui-states&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-syncs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state-buffer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;a/sliding-buffer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;a/pipe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-ui-states&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-server&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;event/full-sync-request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-sync-event&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-syncs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initial-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:full-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-sync-event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initial-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sync-client!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initial-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-server&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state-buffer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-syncs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ui-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Server sync process&lt;/strong&gt; (&lt;a href=&quot;https://github.com/hoodunit/grub/blob/b31489b2b88f4d9fdc3c245e1f4c1fcc32ab3880/src/clj/grub/server_sync.clj#L24&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;

&lt;div class=&quot;small-code&quot;&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure&quot; data-lang=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start-sync!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list-name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-client&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-sync-reqs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rand-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;awaiting-state?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;awaiting-state?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-sync-reqs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-sync-reqs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;a/alts!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when-not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nil?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;condp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shadow-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-shadow-db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/as-of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shadow-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-shadow-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db/get-current-db-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-shadow-db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug-print&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Got diff from client: &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shadow-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; -&amp;gt; &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db/patch-state!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list-name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/basis-t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db/get-current-db-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-after&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-shadow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;diff/patch-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-shadow-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return-diff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;event/diff-msg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-shadow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug-print&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Send diff to client : &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; -&amp;gt; &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-client&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return-diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

                &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full-sync-reqs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current-db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/basis-t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current-db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db/get-current-db-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current-db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug-print&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Full sync client to : &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-client&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;event/full-sync&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

                &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report-queue&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx-report&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-db-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db-after&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx-report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/basis-t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-db-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; Already up to date, do nothing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug-print&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Got report &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; but client already up-to-date at &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

                    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; Changes, send them down&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db/get-current-db-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-db-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/as-of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db-conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db/get-current-db-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug-print&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Got report, send diff to client: &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; -&amp;gt; &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;&amp;gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to-client&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;event/diff-msg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client-state&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;recur&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Throwable.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bug: Received an event on unknown channel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
 
&lt;/div&gt;

&lt;p&gt;I think it works. I have a few niggling doubts about the interaction between buffering and the diffing algorithm. But if it doesn’t work, I have a solid way to make it better. It might not work because the implementation doesn’t match the model, in which case I update one or the other. Or it might be that the model itself is flawed. If the model is flawed, I should be able to write an assertion that fails and then change the model until it does not fail. All of these changes build on each other. The model can be iteratively improved without the need to start over from the beginning.&lt;/p&gt;

&lt;p&gt;Try it out &lt;a href=&quot;https://grub.ndk.io&quot;&gt;here&lt;/a&gt;. Pull up two browsers on the same URL and see if they sync. Use it for your family grocery run. If it doesn’t work, let me know. Or better yet, show how it doesn’t work based on the CSP algorithm, or by showing how the implementation doesn’t match the algorithm.&lt;/p&gt;

&lt;h2 id=&quot;why-csp-matters&quot;&gt;Why CSP matters&lt;/h2&gt;

&lt;p&gt;The point here isn’t that my CSP representation is a perfect model of my app and that my app must work in all situations because the model works. The point is that this CSP approach makes writing complicated concurrent or distributed systems easier. It is a relatively lightweight model checking approach that falls more in the category of advanced type systems than formal verification. We don’t need to manually manipulate math to see that our algorithm works. We just write a program in a funny (but modern) language, write tests in the form of assertions, and have our model checker brute force it to see if our tests pass or fail.&lt;/p&gt;

&lt;p&gt;Porting the resulting CSP model to Clojure is relatively straightforward because Clojure’s core.async library is based on CSP. It would be even more straightforward for a CSP implementation in a language closer to CSP&lt;sub&gt;M&lt;/sub&gt; like Haskell’s &lt;a href=&quot;https://www.cs.kent.ac.uk/projects/ofa/chp/&quot;&gt;CHP&lt;/a&gt;. The matching of models means that CSP can be used to solve complex problems using core.async in a way that would be more difficult using another approach like functional reactive programming or actors. It’s not that you couldn’t do it, but impedance mismatch makes CSP modeling less useful. Translation between languages may take more work and it’s more likely that the program doesn’t actually match the model.&lt;/p&gt;

&lt;p&gt;This is not a silver bullet. It’s easy for models to grow large enough to make them time consuming to check, for instance. The practicality of this approach for my day-to-day problems is an open question. But the promise is tantalizing. Once I had sufficient understanding of how CSP and CSP&lt;sub&gt;M&lt;/sub&gt; work, writing a model and a specification was relatively straightforward. It &lt;em&gt;feels&lt;/em&gt; more approachable and practical than writing distributed algorithms in CSP by hand, without sacrificing the power of the approach. With specifications that don’t care about most of the implementation details, algorithms can be refactored without fear of breaking basic premises. Instead of relying on an ad hoc implementation of an algorithm that someone on the Internet wrote, trusting that they knew what they were doing because they used fancy words and worked at Google, we can modify and verify the algorithm ourselves. Or we can throw it out and write our own algorithm entirely.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This&lt;/em&gt; is why CSP matters.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post is duplicated on both my blog and the &lt;a href=&quot;http://reaktor.com/blog/why-csp-matters-ii-how-do-i-know-sync-works&quot;&gt;Reaktor blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h3&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;What we are actually specifying is much more precise than my hand-waving explanation. We are saying that our specification process &lt;em&gt;failures/divergences refines&lt;/em&gt; our implementation process. For further detail I direct you to the &lt;a href=&quot;http://www.cs.ox.ac.uk/bill.roscoe/publications/68b.pdf&quot;&gt;Roscoe book&lt;/a&gt;, which provides precise mathematical definitions. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
                                <pubDate>Thu, 25 Feb 2016 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/why-csp-matters2.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/why-csp-matters2.html</guid>
                        </item>
                
                        <item>
                                <title>Why CSP matters I: Keeping things in sync</title>
                                <description>&lt;!-- _includes/base.html --&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/clojure/core.async&quot;&gt;core.async&lt;/a&gt; library is a well known library in the Clojure community for managing asynchronous programming. It is based on CSP or Communicating Sequential Processes, originally introduced by &lt;a href=&quot;http://spinroot.com/courses/summer/Papers/hoare_1978.pdf&quot;&gt;Tony Hoare in a 1978 paper&lt;/a&gt;. The fact that core.async is based in CSP is oft-mentioned in core.async introductions. But why should we care?&lt;/p&gt;

&lt;p&gt;CSP as a mathematical modeling language has a long and rich history in academic research. But as a developer trying to Get Things Done, does CSP matter? Once a library written in CSP exists, is there any reason for users to care about the underlying CSP concepts? And are there practical reasons that a library based on the CSP language is useful in ways that, say, a functional reactive programming or actors library is not?&lt;/p&gt;

&lt;p&gt;Rich Hickey, in the &lt;a href=&quot;http://clojure.com/blog/2013/06/28/clojure-core-async-channels.html&quot;&gt;blog post introducing core.async&lt;/a&gt;, hints that there are. He talks about building upon the work of CSP and its derivatives and states that “CSP proper is amenable to certain kinds of automated correctness analysis”. Edsger Dijkstra, in the introduction to &lt;a href=&quot;http://www.usingcsp.com/cspbook.pdf&quot;&gt;Tony Hoare’s CSP book&lt;/a&gt;, describes the CSP approach in glowing terms as showing the way to “what computing science could– or even should–be”.&lt;/p&gt;

&lt;p&gt;Tickled by the possibility of upgrading my mental tools, I looked through &lt;a href=&quot;http://www.usingcsp.com/cspbook.pdf&quot;&gt;Hoare’s CSP book&lt;/a&gt;. The book is well written. It presents a mathematical language for describing communicating processes, along with low level examples of how different forms of communication could be modeled.&lt;/p&gt;

&lt;p&gt;Reading through the book, I felt as though there was some profound understanding to be had in the CSP language, but the understanding itself eluded me. The examples were very low level. It seemed like a fine theoretical abstraction that was too difficult and time consuming to apply to practical problems. I put the book back on my mental shelf, forgot about the promise of CSP, and went to the store to solve a more practical problem: getting groceries.&lt;/p&gt;

&lt;h2 id=&quot;a-practical-problem-keeping-groceries-in-sync&quot;&gt;A practical problem: Keeping groceries in sync&lt;/h2&gt;

&lt;p&gt;Getting groceries sucks. Everyone has to do it, thinking about food takes mental energy, and the process is error prone and sometimes stressful. Most people fall back on simple strategies to make grocery shopping work. They get the things they always get, or they painstakingly write out and maintain paper grocery lists.&lt;/p&gt;

&lt;p&gt;There are other solutions in this problem space, but my idea was to write a real-time synced grocery list web app. You add items to the list from recipes and everyone sees them immediately. You can split up in the grocery store with your buddies or wife and check off items as you find them. Everyone sees the whole time what is left to get. You all get out of the store quicker.&lt;/p&gt;

&lt;p&gt;How do I keep these items in sync? That’s simple. Every time something changes, we send the change to everyone else. As every software developer knows&lt;a href=&quot;https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing&quot;&gt;*&lt;/a&gt;, the network is fast and reliable and available so the changes will make it through immediately and everyone will stay in sync.&lt;/p&gt;

&lt;p&gt;This poor man’s solution actually worked remarkably well. Until it didn’t. My child would press something on my phone while I was weighing broccoli and I would lose my Internet connection and end up getting twice as many tomatoes as I needed, because my wife had picked them up while I was disconnected. The blue cheese would disappear from the list under mysterious circumstances. Mistakes were made. The dinner table suffered. Test users (my wife and two year old) didn’t appreciate my explanations about the complexities and prohibitive costs of proper synchronization algorithms for a small unfunded product development team.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto; width: 500px; max-width: 100%&quot; src=&quot;./img/sync_duplicate_item_color.png&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;b&gt;Sync issues: duplicate items&lt;/b&gt;&lt;/center&gt;

&lt;p&gt;&lt;strong&gt;Worst of all is the uncertainty.&lt;/strong&gt; You’re never quite sure if everything is still on the list, or if items have been mysteriously “lost”, or if items that you check off have already been checked off by someone else. This is clearly not a proper solution for a software developer (nor a software developer’s wife).&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto; width: 500px; max-width: 100%&quot; src=&quot;./img/sync_missing_item.png&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;b&gt;Sync issues: missing items&lt;/b&gt;&lt;/center&gt;

&lt;p&gt;I hunted around for better solutions to my sync problems. I could make everything synchronous, but that would be slow and only work online. &lt;a href=&quot;https://en.wikipedia.org/wiki/Operational_transformation&quot;&gt;Operational transformations&lt;/a&gt; developed for text-based collaboration sounded promising but probably more complex than necessary. Finally I settled on differential synchronization, described in a &lt;a href=&quot;https://neil.fraser.name/writing/sync/&quot;&gt;2009 paper by Neil Frasier of Google&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;learning-by-implementing-papers-differential-sync&quot;&gt;Learning by implementing papers: Differential sync&lt;/h2&gt;

&lt;p&gt;Differential sync had a number of aspects that felt like a good fit for my use case. Everyone can edit all the time without blocking others. Local states naturally converge in an “eventually consistent” sort of manner. Finally and perhaps most importantly, someone had written a &lt;a href=&quot;https://neil.fraser.name/writing/sync/&quot;&gt;coherent paper&lt;/a&gt; that explained how to implement it.&lt;/p&gt;

&lt;h3 id=&quot;how-differential-sync-works&quot;&gt;How differential sync works&lt;/h3&gt;

&lt;p&gt;The basic approach in the client-server setup works as follows. Client and server start out in the same state. Both client and server maintain their own state and a “shadow” state representing the last synced state from the other party. When changes are made on a client, a diff is computed between the new state and the shadow state. The diff is tagged with the client and server versions that it is based on and sent off to the server. The server verifies the version numbers, patches the changes onto its own state and its shadow client state, and then compares the two. If there are differences, i.e. someone else made changes on the server in the meantime, the two new states are diffed and the diff is sent back to the client. Repeat ad infinitum. Go ahead and &lt;a href=&quot;https://neil.fraser.name/writing/sync/&quot;&gt;read the paper&lt;/a&gt;, it’s actually quite well written.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto; width: 500px; max-width: 100%&quot; src=&quot;./img/diff_sync.gif&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;b&gt;Differential synchronization guaranteed delivery method&lt;/b&gt; (&lt;a href=&quot;https://neil.fraser.name/writing/sync&quot;&gt;source&lt;/a&gt;)&lt;/center&gt;

&lt;p&gt;This is a simple idea on the face of it, but as always, the devil is in the implementation details.&lt;/p&gt;

&lt;h3 id=&quot;how-my-implementation-works&quot;&gt;How my implementation works&lt;/h3&gt;

&lt;p&gt;I implemented this as a &lt;strong&gt;Clojure&lt;/strong&gt; and &lt;strong&gt;ClojureScript&lt;/strong&gt; web app. You can try it out &lt;a href=&quot;https://grub.ndk.io&quot;&gt;here&lt;/a&gt; &lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and read the source &lt;a href=&quot;https://github.com/hoodunit/grub&quot;&gt;here&lt;/a&gt;. The front end uses &lt;a href=&quot;https://github.com/omcljs/om&quot;&gt;Om&lt;/a&gt; for the UI and is connected via WebSocket to a Clojure backend, which persists changes to a &lt;a href=&quot;http://www.datomic.com&quot;&gt;Datomic&lt;/a&gt; database. Synchronization is handled by a client-side core.async process (go block) communicating with a server-side core.async process. To update the UI when the state changes, it passes the new state to a Clojure atom and through Om/React magic the UI is re-rendered efficiently. To capture all UI state changes, it uses a nifty feature of Om that allows one to observe all UI data changes. We pass these via a core.async channel to our our client synchronization process.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;display: block; margin: 0 auto; width: 600px; max-width: 100%&quot; src=&quot;./img/grub_architecture.png&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;b&gt;App architecture&lt;/b&gt;&lt;/center&gt;

&lt;p&gt;The diffing and patching algorithm works as follows. Both client and server start out in the same state containing a list of our grocery items, which I refer to as “grubs”:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:grubs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id1&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2 cans cherry tomatoes&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id2&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cream&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id3&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4 T. red pesto&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id4&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1 yellow onion&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id5&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2 T. brown sugar&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id6&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1 garlic bulb&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id7&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cottage cheese&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When one client makes some changes&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:grubs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id1&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2 cans cherry tomatoes&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; removed &quot;cream&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id3&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4 T. red pesto&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id4&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1 yellow onion&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id5&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2 T. brown sugar&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id6&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2 garlic bulbs&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; edited&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id7&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cottage cheese&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id8&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;milk&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; added&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;we compute the diff of these changes and send it on to the server:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:grubs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id1&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id3&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id6&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2 garlic bulbs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id8&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;milk&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:completed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The diff tells which items were added or modified under the “:+” map and the IDs of items that were removed under the “:-“ set. To update the server state, we then run through the diff and merge in the changes to the server state. Merges happen on a “last edit wins” basis.&lt;/p&gt;

&lt;h3 id=&quot;how-clojure-makes-this-nice&quot;&gt;How Clojure makes this nice&lt;/h3&gt;

&lt;p&gt;I want to stop for a moment to point out how elegantly the whole process works using Clojure. Diffing and patching is handled in a straightforward and relatively efficient manner using Clojure’s immutable data structures. UI updates are just a &lt;code class=&quot;highlighter-rouge&quot;&gt;render&lt;/code&gt; call with the latest state. By using core.async for communication between our sync processes, our sync processes don’t actually care that the communication is going over the network. They take in messages and send out messages and all that matters is the type and timing of messages. This also makes it straightforward to test. We connect the processes directly to each other, spoof messages as necessary, and verify that the processes shoot out the correct messages.&lt;/p&gt;

&lt;p&gt;Finally, storing the data in a &lt;a href=&quot;http://www.datomic.com&quot;&gt;Datomic&lt;/a&gt; database, which I had not used before, has been pleasant and has interesting properties. Datomic is an immutable data store. It works similarly to Clojure’s data structures in preserving the history of changes while using structure sharing for performance.&lt;/p&gt;

&lt;p&gt;Having the entire history of changes instantly makes the grocery app much more valuable. It allows me to do things like look over my entire history of grocery shopping and see how often I cook burgers in a month. The app could suggest items to add to grocery lists based on items I have previously purchased. It could make recommendations based on users who have similar tastes.&lt;/p&gt;

&lt;p&gt;This together with the fact that changes are synced immediately also has other interesting implications both from technical and privacy perspectives. You can derive a lot of personal information from this kind of data. You could analyze the timing of when items are completed to organize the grocery items by store section. You could find out when people shop for groceries, when they think about groceries, what they eat, what time zone they live in, how their tastes have changed, or how many times they change their mind about broccoli and end up getting a premade meal instead. Immutable data unlocks compelling possibilities but can also have serious privacy implications.&lt;/p&gt;

&lt;p&gt;Another interesting property of using Datomic as a data store is from the synchronization angle. In a normal synchronization setting you would have to store a certain amount of history and sync clients from this history. If the client state is older than the history, then you would have to do a full sync, losing all changes from the client. With Datomic we have the entire history of changes. We can do a sync from any historical state of the database. In practice you would probably want to do a full sync for older client states anyway, but the idea is compelling.&lt;/p&gt;

&lt;p&gt;If I had to implement this project in JavaScript, the complexity may have killed me well before this point. One way to do it would be to basically reinvent Clojure in JavaScript: use &lt;a href=&quot;https://facebook.github.io/immutable-js&quot;&gt;ImmutableJS&lt;/a&gt; or &lt;a href=&quot;http://swannodette.github.io/mori&quot;&gt;Mori&lt;/a&gt; for immutable data structures, use &lt;a href=&quot;https://baconjs.github.io&quot;&gt;BaconJS&lt;/a&gt; streams to communicate events, separate React rendering and app data and use a single event loop in order to render and capture changes, and use &lt;a href=&quot;http://martinfowler.com/eaaDev/EventSourcing.html&quot;&gt;event sourcing&lt;/a&gt; or the like to keep a history of database events. This would be a much more difficult project to tackle. Using Clojure makes it possible to tackle complex problems that would otherwise be too much trouble.&lt;/p&gt;

&lt;h3 id=&quot;why-sync-was-still-difficult&quot;&gt;Why sync was still difficult&lt;/h3&gt;

&lt;p&gt;Even using Clojure, I ran into my own challenges with implementing the differential sync algorithm. The paper sometimes lacked detail. My use case was not identical to the paper’s use cases. My own architectural decisions did not always agree with the paper’s approach. As my implementation strayed from the strait and narrow path drawn by the paper, it became more and more uncertain whether the implementation worked.&lt;/p&gt;

&lt;p&gt;In particular, I ran into the following challenges:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The paper assumes a traditional polling-based HTTP request model.&lt;/strong&gt; The paper’s algorithm only allows one packet in flight at a time and otherwise seems to imply a traditional HTTP request based model. I had wanted to use WebSockets in order to receive changes from other clients as quickly as possible. It is unclear whether these two thoughts are compatible.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The paper assumes the server will keep the shadow state for a client even if the client is not connected.&lt;/strong&gt; The paper does not describe the process of initialization, but assumes the client and server are in a “connected” state throughout (although packets may be lost). This means the server must maintain a representation of the client state even when the client is disconnected. This feels like an unreasonable restriction.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Code complexity.&lt;/strong&gt; This synchronization requires careful coordination between sync processes and the UI and database. Refactoring core.async processes can also be more difficult than normal refactoring because go threads are implicit and used through macros (which have different scoping rules).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My implementation seemed to work, but after all was said and done I was left with a nagging feeling of uncertainty. The uncertainty that had plagued me with my initial ad hoc approach to synchronization was creeping back. Grabbing pepperoni slices from the grocery store freezer I was plagued by the thought that I was forgetting something. I checked and double-checked that blue cheese had made it onto the list. I asked my wife to show me her own list at times to see if my list looked the same. The lists did match. Except sometimes, inexplicably, they didn’t.&lt;/p&gt;

&lt;h3 id=&quot;how-can-i-know-that-my-sync-algorithm-works&quot;&gt;How can I know that my sync algorithm works?&lt;/h3&gt;

&lt;p&gt;I tried different tactics to overcome this uncertainty. One of the basic problems is that the problem was too large to easily think about. Error cases occurred in very distinct scenarios, and it was too difficult to know if changing one aspect of the algorithm would affect a completely different scenario.&lt;/p&gt;

&lt;p&gt;I tried finding ways to represent the problem in a more conceptualizable manner. I drew diagrams and wrote out possible synchronization scenarios on paper. I thought about using &lt;a href=&quot;http://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/resources/statecharts.pdf&quot;&gt;statecharts&lt;/a&gt; or other diagramming languages to represent the problem more clearly.&lt;/p&gt;

&lt;p&gt;I wrote test cases to verify that different aspects of the synchronization algorithm worked correctly and to verify that the big picture worked if you hammered on it. But testing concurrent code is fundamentally broken. You can write test cases. You can pull up many browsers and have them all hammer on the app and randomly disconnect and connect and see that the browsers end up in the same state. This helps you feel more confident, but ultimately it only tests a small part of the problem space. Concurrent errors may depend on very specific timings of events. How do you know if synchronization works in &lt;em&gt;all&lt;/em&gt; cases?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short I had failed.&lt;/strong&gt; I didn’t feel more confident about my implementation of differential sync than I had felt about the ad hoc approach. I blamed Neil Frasier for not thinking his algorithm through completely. I blamed my implementation, which had grown in complexity and was difficult and time consuming to verify. I blamed core.async, which had failed me in my moment of need. But the thought nagged me that there had to be a better way to handle this problem. I needed to be able to write my own algorithm. I needed to upgrade my mental tools.&lt;/p&gt;

&lt;p&gt;And I remembered the promise of CSP.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;http://blog.ndk.io/why-csp-matters2.html&quot;&gt;Continued in part II&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post is duplicated on both my blog and the &lt;a href=&quot;http://reaktor.com/blog/why-csp-matters-i-keeping-things-in-sync&quot;&gt;Reaktor blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h3&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Go ahead and &lt;a href=&quot;https://grub.ndk.io&quot;&gt;use the app&lt;/a&gt; if you like. I try not to break things but this is a hobby project so there are no guarantees. Different URLs correspond to different lists, which are synced independently. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
                                <pubDate>Tue, 23 Feb 2016 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/why-csp-matters1.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/why-csp-matters1.html</guid>
                        </item>
                
                        <item>
                                <title>PureScript on Android</title>
                                <description>&lt;p&gt;I’ve been playing around with PureScript on Android via React Native.&lt;/p&gt;

&lt;p&gt;Here’s what a TodoMVC-style app looks like:&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;width: 300px; margin: 0 auto; display: block;&quot; src=&quot;/img/todomvc_screenshot.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And here’s what the guts of the code look like:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eff&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Render&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppState&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eff&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;AppState&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;readState&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;container&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;todos&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;newTodoContainer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;textInput&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;newTodo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                   &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newTodo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;kt&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;placeholder&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;What needs to be done?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;kt&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onChangeText&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newTodo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformState&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updateNewTodo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTodo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                   &lt;span class=&quot;kt&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onSubmitEditing&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformState&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;addTodo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;listView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;todoList&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderRow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;todoRow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderSeparator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;todoSeparator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderHeader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;separator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bottomBar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;filters&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;filterButton&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
           &lt;span class=&quot;n&quot;&gt;filterButton&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;filterButton&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Completed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;clearCompleted&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
              &lt;span class=&quot;kt&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onPress&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformState&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clearCompleted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; 
             &lt;span class=&quot;s&quot;&gt;&quot;Clear completed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
        
&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Running app&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;registerComponent&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;PureScriptSampleApp&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;component&lt;/span&gt;
  &lt;span class=&quot;kr&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;component&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createClass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialState&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dataSource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listViewDataSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialTodos&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;initialState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updateDataSource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nextId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                                 &lt;span class=&quot;n&quot;&gt;newTodo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                                 &lt;span class=&quot;n&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialTodos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                                 &lt;span class=&quot;n&quot;&gt;dataSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                                                 &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;All&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check out the &lt;a href=&quot;https://github.com/hoodunit/purescript-react-native-todomvc&quot;&gt;example source code&lt;/a&gt; or the &lt;a href=&quot;https://github.com/hoodunit/purescript-react-native&quot;&gt;React Native wrapper code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This wrapper code builds on Phil Freeman’s &lt;a href=&quot;https://github.com/purescript-contrib/purescript-react&quot;&gt;purescript-react&lt;/a&gt; low-level wrapper for React, adding support for various elements used in Android. It is a work in progress. Functional afficionados will raise their eyebrows. Android gurus will throw their hands up in disgust. But it is PureScript. It is Android. And it is awesome.&lt;/p&gt;
</description>
                                <pubDate>Tue, 13 Oct 2015 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/purescript-on-android.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/purescript-on-android.html</guid>
                        </item>
                
                        <item>
                                <title>The state of Clojure on Android</title>
                                <description>&lt;p&gt;&lt;em&gt;Note that this post was written in April of 2015, based on experiments run on relatively current Android devices of the time. The situation may be different now.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Clojure on Android suffers from the slow startup times of the Clojure runtime. The Lean Clojure compiler projects promise fast startup times and performance at the cost of dynamism and complexity. Does it work?&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 600px;&quot; src=&quot;/img/clojure_plus_android_dark.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;How do you know if anything works? You test it. You set up some experiment that you think models the problem. You make your change, run the experiment with and without the change, and see what happens. You draw conclusions and quibble about whether you tested what you thought you tested and whether the results mean anything.&lt;/p&gt;

&lt;p&gt;Here’s what I benchmarked and what I think it means. But first a bit of background.&lt;/p&gt;

&lt;h2 id=&quot;what-is-lean-clojure&quot;&gt;What is Lean Clojure?&lt;/h2&gt;

&lt;p&gt;The idea of Lean Clojure is to remove some of the dynamism of Clojure for performance. Over the summer of 2014 two projects were developed related to this. Alexander Yakushev developed the &lt;a href=&quot;http://clojure-android.info/skummet/&quot;&gt;Skummet lean Clojure compiler&lt;/a&gt; based on the standard Clojure compiler. Reid McKenzie worked on the &lt;a href=&quot;http://www.arrdem.com/2014/08/06/of_oxen,_carts_and_ordering/&quot;&gt;Oxcart compiler&lt;/a&gt; based on the Clojure in Clojure compiler. The Oxcart compiler compiles a rather limited subset of Clojure so it is not presented here.&lt;/p&gt;

&lt;p&gt;The Skummet compiler works something like the following.&lt;/p&gt;

&lt;p&gt;Clojure namespaces set up a dynamic mapping between symbols, vars, and functions. This mapping looks basically like this:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 600px;&quot; src=&quot;/img/namespace_vars.svg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At run time Clojure calls functions like this, in decompiled JVM bytecode:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;RT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clojure.core&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cons&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getRawRoot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Skummet changes this by dropping out the middle men, the symbols and vars, to get something like this:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 600px;&quot; src=&quot;/img/namespace_vars_lean.svg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Invoking functions gets a lot simpler:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;clojure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;$cons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Skummet does other things related to metadata and workarounds to preserve dynamism when necessary, but I believe this is the meat of it.&lt;/p&gt;

&lt;p&gt;It’s fairly obvious why this would be expected to improve Clojure execution speed. Clojure is mostly functions and this simplifies almost every function execution. This should speed things up by reducing overhead and making it easier for the virtual machine to optimize by inlining and doing whatever other black magic virtual machines do.&lt;/p&gt;

&lt;p&gt;It’s perhaps less immediately obvious why this is expected to improve startup times. When the Clojure runtime loads a program, it loads all of the namespaces that are used in the program right at the start. When a namespace is loaded, it sets up the mapping from symbols to vars to functions. It also sets metadata on those vars. This isn’t a complicated process, but you need to create your var objects, create your metadata objects, and assign them to the appropriate places. It takes some time. Skummet cuts out a lot of this work, and so should reduce startup times.&lt;/p&gt;

&lt;p&gt;That’s the theory. But does it work?&lt;/p&gt;

&lt;h2 id=&quot;does-lean-clojure-work&quot;&gt;Does Lean Clojure work?&lt;/h2&gt;

&lt;p&gt;I took five benchmarks from the Computer Language Benchmarks Game,  two little benchmarks of my own, and ran the benchmarks on the Nexus 5 and Nexus 7 on both Dalvik and ART. Here are the results:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 800px;&quot; src=&quot;/img/benchmarks1.png&quot; /&gt;
&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 800px;&quot; src=&quot;/img/benchmarks2.png&quot; /&gt;
&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 800px;&quot; src=&quot;/img/benchmarks3.png&quot; /&gt;
&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 800px;&quot; src=&quot;/img/benchmarks4.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The same benchmarks presented with only startup times by test environment:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 800px;&quot; src=&quot;/img/benchmark_startup_times.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Each benchmark opens an Android activity and performs some task. The &lt;em&gt;hello&lt;/em&gt; benchmark just prints “Hello world”. The &lt;em&gt;dependencies&lt;/em&gt; benchmark does something trivial with two library dependencies, &lt;a href=&quot;https://github.com/ReactiveX/RxClojure&quot;&gt;RxClojure&lt;/a&gt; (or &lt;a href=&quot;https://github.com/ReactiveX/RxJava&quot;&gt;RxJava&lt;/a&gt;) and &lt;a href=&quot;https://github.com/cognitect/transit-clj&quot;&gt;Transit&lt;/a&gt;. The others execute algorithms specified in the &lt;a href=&quot;http://benchmarksgame.alioth.debian.org/&quot;&gt;Computer Language Benchmarks Game&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The programs are written in Java and Clojure and compiled using the Java, Clojure, and Skummet compilers. Each benchmark is executed thirty times and the results are averaged.&lt;/p&gt;

&lt;p&gt;What might we conclude from this?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clojure on Android apps start slowly (2+ seconds minimum)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, duh, you’re thinking. The benchmarks give a bit of the scope of the problem, though. On Android Dalvik, Clojure apps take a minimum of nearly two seconds to start on the Nexus 5 phone and 2.5 seconds on the Nexus 7 tablet. The Nexus 5 is a relatively new phone and probably faster than most phones on the market, so the general case for performance is likely to be worse.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;dependencies&lt;/em&gt; benchmark performs a trivial task with two library dependencies and has startup times exceeding 2.5 and 3.5 seconds for the same device setups. This suggests a fairly fast scaling up of startup times. Actual apps would likely have more dependencies and code and take significantly longer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ART helps (1.5+ seconds minimum)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The latest version of Android, Lollipop, uses the new ART virtual machine to execute apps in place of Dalvik. This improved startup times in these benchmarks by about about 20-30% on average.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lean Clojure helps even more (0.7+ seconds minimum)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lean Clojure cuts Clojure on Android startup times in half across the board, dropping them from around 1.5 to 2.5 seconds to around 0.7 to 1 seconds. Run time performance seems to be on par with standard Clojure, though these benchmarks are poor tests of performance.&lt;/p&gt;

&lt;h2 id=&quot;what-does-this-mean&quot;&gt;What does this mean?&lt;/h2&gt;

&lt;p&gt;Lean Clojure works. Skummet cuts Clojure on Android startup times in half in these benchmarks. But it’s not good enough.&lt;/p&gt;

&lt;p&gt;A half a second is about the minimum amount of time needed to execute Clojure code on Android on Skummet. Times for actual programs are likely to be significantly higher. This might not be a problem for apps that are loaded once and run for a long period, but for many types of development this is just too long.&lt;/p&gt;

&lt;p&gt;If the lean Clojure project were continued, it seems likely it would bring this down to an acceptable range. Dependency shaking and inlining functions could make a large difference. Tools like ProGuard could make additional improvements. There are also other possible directions for Clojure on Android like ClojureScript plus third party frameworks such as Titanium or the upcoming Facebook React Native.&lt;/p&gt;

&lt;p&gt;But Android is still waiting for it’s Swift.&lt;/p&gt;

&lt;h2 id=&quot;more-details&quot;&gt;More details&lt;/h2&gt;

&lt;p&gt;The rather verbose version of this post is in my &lt;a href=&quot;https://ndk.io/clojure_on_android.pdf&quot;&gt;thesis&lt;/a&gt;.&lt;/p&gt;
</description>
                                <pubDate>Thu, 23 Apr 2015 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/state-of-coa.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/state-of-coa.html</guid>
                        </item>
                
                        <item>
                                <title>Solving Clojure Boot Time</title>
                                <description>&lt;p&gt;Clojure programs start slowly because they load the &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; namespace before doing anything useful. Loading the &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; namespace loads the Java class files for all of the functions in &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; and sets up Vars to point to new class instances corresponding to each function.&lt;/p&gt;

&lt;p&gt;This is slow. Simple desktop Clojure programs start about 35x more slowly than their Java counterparts. Clojure Android apps start as little as 6x more slowly than their Java counterparts, but the base start time is much higher so the problem is worse.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 200px; width: 100%; max-width: 434px;&quot; src=&quot;/img/clojure_vs_java_speed.svg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 500px; width: 100%; max-width: 720px;&quot; src=&quot;/img/hello_world_all_nocore.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the very best case a Clojure on Android app currently starts in about 1.7 seconds. More normal cases likely take at least 3-5 seconds. Jakob Nielsen in his book &lt;a href=&quot;http://www.nngroup.com/books/usability-engineering/&quot;&gt;Usability Engineering&lt;/a&gt; suggests &lt;a href=&quot;http://www.nngroup.com/articles/response-times-3-important-limits/&quot;&gt;a few rules of thumb&lt;/a&gt; for UI response time. A user perceives as much as 0.1 seconds as instantaneous. Delays of more than 0.2 seconds are noticeable and delays of more than one second should have some indication to the user that something is happening. Ten seconds or more and you will lose your user’s attention completely.&lt;/p&gt;

&lt;p&gt;Currently Clojure on Android apps fall in the “noticeably slow” range. This is also true for command line tools like Leiningen, even on reasonably fast computers. Reducing startup delay to 0.5 seconds on Android would get us into the range of noticeable delay not requiring a loading screen. This means we’re looking for about a &lt;strong&gt;4-6x improvement&lt;/strong&gt; in boot times.&lt;/p&gt;

&lt;p&gt;This amount of speedup will probably require dramatic changes to the Clojure runtime. About half of the boot time on the desktop is due to loading the Java classes themselves. This suggests that the Clojure bootstrap process is just doing too much. We need to do dramatically less work during the Clojure runtime boot process.&lt;/p&gt;

&lt;p&gt;So here are a few ideas for how to improve Clojure start time. These are mostly suggested by other people and I have added my own thoughts regarding each of them. I break them out broadly into three categories: &lt;strong&gt;do less work&lt;/strong&gt;, &lt;strong&gt;do it faster&lt;/strong&gt;, or &lt;strong&gt;do it later&lt;/strong&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;do-less-work&quot;&gt;Do Less Work&lt;/h2&gt;

&lt;p&gt;What work are we doing in the bootstrap process that is unnecessary? How can we eliminate this work to speed up our bootstrap process?&lt;/p&gt;

&lt;h3 id=&quot;1-stripped-application-specific-runtime&quot;&gt;1. Stripped, application-specific runtime&lt;/h3&gt;

&lt;p&gt;Most of the work of bootstrapping Clojure programs relates to setting up dynamic variables for Clojure functions. Many of these functions will never be used, but dynamic features like runtime evaluation and compilation require access to all functions in the Clojure language.&lt;/p&gt;

&lt;p&gt;But what if we decide that we don’t need runtime evaluation or compilation, at least for production? Then there’s no sense in keeping around all of these extra functions that are never used. This idea is to generate a specific stripped production runtime for the application being developed. Instead of including the normal Clojure runtime/compiler with your app, the app would use this new stripped runtime. The runtime would differ from the normal runtime in the following ways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;All core functions that are not used are stripped out from the Clojure runtime and therefore not loaded in core initialization.&lt;/li&gt;
  &lt;li&gt;Runtime compilation and evaluation functions are removed.&lt;/li&gt;
  &lt;li&gt;Unused metadata is also removed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a fairly easy idea to test. Take a few Clojure programs, see what &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; functions they rely on, strip the remaining functions from &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; source, recompile the Clojure runtime, and run the program using the new runtime.&lt;/p&gt;

&lt;p&gt;Using this approach I was able to cut the run time for a Hello World program in half. This could possibly be reduced even further. This is a great improvement, but it is essentially the best case scenario. This best case scenario is not good enough. Most programs will use a much larger fraction of &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; and will see much smaller speedups.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;do-it-faster&quot;&gt;Do It Faster&lt;/h2&gt;

&lt;p&gt;How can we do the same bootstrap work faster? In what ways can we optimize this process?&lt;/p&gt;

&lt;h3 id=&quot;2-optimized-runtime-loading&quot;&gt;2. Optimized runtime loading&lt;/h3&gt;

&lt;p&gt;This idea I vagely label “optimize runtime loading”. Currently the Clojure runtime (by which I mostly mean the &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; namespace) is compiled in almost the exact same way as any other Clojure code. It is also run in the same way as any other namespace. While this is elegant from a language development perspective, is there some way we could optimize &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; loading so that it works faster?&lt;/p&gt;

&lt;p&gt;Every time a Clojure program is run it does the exact same work in setting up a Namespace object for &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt;. Is there some way we can do this work once, save the result to a single binary file, and then load the file at one time? I don’t know. It seems like there could be a way but I don’t know what it is.&lt;/p&gt;

&lt;p&gt;Otherwise maybe there are shortcuts we could take for compiling &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; that would not work in the general case for Clojure code. I don’t know what those shortcuts might be, but I think they exist.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;do-it-later&quot;&gt;Do It Later&lt;/h2&gt;

&lt;p&gt;Do we need to do all of this work on boot? Why can’t we defer setup work until we actually need it?&lt;/p&gt;

&lt;h3 id=&quot;3-modularized-clojurecore&quot;&gt;3. Modularized clojure.core&lt;/h3&gt;

&lt;p&gt;This idea is, on the face of it, quite simple. Break &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; into smaller namespaces. Overall Clojure 1.5.1 has 591 publicly available functions or variables via &lt;code class=&quot;highlighter-rouge&quot;&gt;ns-publics&lt;/code&gt;. Is this a lot? I don’t know. Direct comparisons with non-functional languages is a bit difficult. Scala and Java would appear to have fewer classes and methods in their core libraries and Ruby appears to have more functions, but I cannot say for sure.&lt;/p&gt;

&lt;p&gt;The Clojure core namespace does provide a lot of functionality: bitwise operations, regular expressions, type inspection, concurrency operations, multifunctions, Java interop, operations for the core data structures, structs, transients, namespaces, vars, hierarchies, protocols, metadata, compilation and evaluation. Probably a number of these could be broken into separate namespaces.&lt;/p&gt;

&lt;p&gt;But here’s the challenge. Here’s my chart of the function interdependencies in &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 549px;&quot; src=&quot;/img/clojure_core_deps.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Each of those nodes is a function in &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt;. The lines indicate which other functions in &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; the function relies on.&lt;/p&gt;

&lt;p&gt;Now tell me how that breaks down cleanly into separate namespaces.&lt;/p&gt;

&lt;p&gt;There’s one cluster on the bottom left that relies solely on &lt;code class=&quot;highlighter-rouge&quot;&gt;defn&lt;/code&gt;. That could maybe be pulled out. On the other hand, I believe those functions work mostly by deferring to &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.lang.RT&lt;/code&gt;, so pulling them out might not help much.&lt;/p&gt;

&lt;p&gt;If there are too many interdependencies in &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt;, then breaking it down further would make loading time worse rather than better. If a function depends on several functions in other namespaces then loading multiple namespaces takes longer than loading one.&lt;/p&gt;

&lt;h3 id=&quot;4-lazy-initialization-of-clojure-functions&quot;&gt;4. Lazy initialization of Clojure functions&lt;/h3&gt;

&lt;p&gt;This is an idea &lt;a href=&quot;http://gal.dolber.com/post/78110050703/reduce-startup&quot;&gt;presented by galdolber&lt;/a&gt;, who has worked on a Clojure compiler for Objective C. Here’s my understanding of how this idea might work.&lt;/p&gt;

&lt;p&gt;Clojure core bootstrapping sets up a Var to point to every function in the &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; namespace. In Java bytecode the setup work looks like this:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;const__cons&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clojure.core&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cons&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AFn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;const__consMeta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metaForCons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;const__cons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setMeta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;const__consMeta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;const__cons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bindRoot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The end result looks kind of like this:&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;dynamicBinding&quot;&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 500px;&quot; src=&quot;/img/Var.svg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Namespace object for &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; is set up with a mapping of Symbols to Vars corresponding to functions. Each Var in turn points to its own metadata and an instance of the Java class file which implements the Var functionality.&lt;/p&gt;

&lt;p&gt;When you call a function, it is done like this (in pseudo-Java bytecode):&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// (cons args)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;RT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clojure.core&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cons&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getRawRoot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This first fetches the &lt;code class=&quot;highlighter-rouge&quot;&gt;core&lt;/code&gt; namespace and then fetches the Var in the namespace corresponding to &lt;code class=&quot;highlighter-rouge&quot;&gt;cons&lt;/code&gt;. From the Var we can then grab the current value with &lt;code class=&quot;highlighter-rouge&quot;&gt;getRawRoot&lt;/code&gt; and invoke it.&lt;/p&gt;

&lt;p&gt;But why do we need to set up everything beforehand when a namespace is loaded? Why can’t we just load and set up functions as we need them? galdolber’s &lt;a href=&quot;http://gal.dolber.com/post/78110050703/reduce-startup&quot;&gt;idea&lt;/a&gt; would move the Vars into their corresponding class instances and initialize them only when they are first used. This also sounds fairly similar to &lt;a href=&quot;http://blog.ndk.io/clojure-bootstrapping.html&quot;&gt;Laurent petit’s comment&lt;/a&gt; on my other post. I picture the result something like this:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;blog-img&quot; style=&quot;min-width: 300px; width: 100%; max-width: 500px;&quot; src=&quot;/img/Var in class.svg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We keep Clojure’s dynamic binding features, as we still use mutable Namespaces and Vars, while pushing the time to load and initialize functions to the first time they are used.&lt;/p&gt;

&lt;p&gt;This could be a good idea. It could also cause hiccups in performance. Right now all loading penalties are paid cleanly when a namespace is loaded. Under this idea if you use a function that has dependencies across &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.core&lt;/code&gt; you may pay the loading time penalty at an awkward point.&lt;/p&gt;

&lt;p&gt;This could also be combined with the stripped runtime idea. We throw out unneeded functions and load the functions we do need lazily. It could work. But is it enough?&lt;/p&gt;

&lt;p&gt;In the Objective C case galdolber quotes a speedup from 2.3 seconds to 900 milliseconds. This is about a 2.6x speedup. Stripping our runtime could give a little more. A similar speedup in Android would drop our fastest startup times from about 2 seconds to 770 milliseconds. The average case could be twice that. This is good, but based on those numbers it’s not good enough. Prove me wrong, galdolber.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;do-less-work-faster-and-later&quot;&gt;Do Less Work, Faster, and Later&lt;/h2&gt;

&lt;p&gt;The problem with many of the above ideas is that they don’t seem to go far enough. We need dramatic speed improvements. This probably requires dramatic changes to the way Clojure works.&lt;/p&gt;

&lt;h3 id=&quot;5-lean-jvm-runtime&quot;&gt;5. Lean JVM runtime&lt;/h3&gt;

&lt;p&gt;This idea makes the most dramatic changes but also has the most compelling potential. This is also what Daniel Solano Gómez has been &lt;a href=&quot;http://dev.clojure.org/display/community/Project+Ideas#ProjectIdeas-LeanJVMRuntime&quot;&gt;proposing&lt;/a&gt; all along and echoes some of what &lt;a href=&quot;https://twitter.com/mikera&quot;&gt;mikera&lt;/a&gt; has been &lt;a href=&quot;http://blog.ndk.io/clojure-bootstrapping.html&quot;&gt;saying&lt;/a&gt;. It has taken me several months to understand what is going on well enough to appreciate this idea.&lt;/p&gt;

&lt;h3 id=&quot;clojure-dynamism&quot;&gt;Clojure dynamism&lt;/h3&gt;

&lt;p&gt;Clojure is a dynamic language. Two key features that Clojure supports as a dynamic language are dynamic binding of vars and namespaces and dynamic incremental compilation and evaluation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic binding&lt;/strong&gt; is in some ways a surprising feature for a language that touts its immutability. All variables defined with &lt;code class=&quot;highlighter-rouge&quot;&gt;def&lt;/code&gt; and functions defined with &lt;code class=&quot;highlighter-rouge&quot;&gt;defn&lt;/code&gt; are implemented using mutable Vars. The Vars are stored within mutable Namespaces. Either can be changed at any time. This means that every function call requires at least two levels of indirection: first get the Namespace, then the Var, then the function itself, and finally call the function. Refer to the &lt;a href=&quot;#dynamicBinding&quot;&gt;diagram&lt;/a&gt; further up.&lt;/p&gt;

&lt;p&gt;The second important feature is &lt;strong&gt;dynamic incremental compilation and evaluation&lt;/strong&gt;. Clojure is a very flexible language. Almost anything that can be done at compile time can also be done at run time. It includes built in evaluation &lt;a href=&quot;#refInterpretation&quot;&gt;&lt;sup&gt;[1]&lt;/sup&gt;&lt;/a&gt; and compilation. Among other things, this enables the power of the Read Eval Print Loop (REPL). Combined with dynamic binding of namespaces and namespace variables, this gives a programmer a substantial amount of power to change their program while it is running.&lt;/p&gt;

&lt;p&gt;In development, both of these features are very useful. Dynamic binding allows you to redefine functions for testing and development and gives more tools to work with external libraries that you do not control. Runtime evaluation and compilation combined with dynamic binding enable the short feedback loops of a powerful REPL development environment.&lt;/p&gt;

&lt;p&gt;But how important are these features in production? Dynamic compilation seems to be used hardly at all in production. Use of dynamic binding in production is usually discouraged. In the case of Android development, dynamic recompilation is not even possible in the same way. When using a Clojure library from a Java environment it seems unlikely these features would be useful. In both of these last cases fast startup time and runtime performance seem more important.&lt;/p&gt;

&lt;h3 id=&quot;the-idea&quot;&gt;The Idea&lt;/h3&gt;

&lt;p&gt;The premises of a lean JVM runtime are the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The program is Ahead of Time (AOT) compiled.&lt;/li&gt;
  &lt;li&gt;After compilation the program does not need to be redefined.&lt;/li&gt;
  &lt;li&gt;Run time performance is the main priority.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on these premises, the following changes would be made to the Clojure compiler and runtime:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Dynamic binding is removed. Namespaces compile to classes. Namespace variables and functions compile to static fields or methods. The Var is probably removed entirely.&lt;/li&gt;
  &lt;li&gt;Dynamic compilation and evaluation are removed. Functions that rely on runtime compilation and evaluation such as &lt;code class=&quot;highlighter-rouge&quot;&gt;eval&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;compile&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;load&lt;/code&gt;, and so forth are eliminated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;how-will-this-improve-clojure-startup-times&quot;&gt;How will this improve Clojure startup times?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;When variables compile to static methods and fields there are fewer classes to load and they can be loaded lazily. This means there is a lot less to load at boot and boot times should be dramatically shorter.&lt;/li&gt;
  &lt;li&gt;Immutable functions and namespace variables can be directly referenced. This removes the two levels of indirection from fetching mutable namespaces and fetching mutable vars. For boot time or when loading classes, this means there is a lot less work to do.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;how-else-could-this-improve-clojure-performance&quot;&gt;How else could this improve Clojure performance?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Removing Var and Namespace indirection should significantly improve runtime performance.&lt;/li&gt;
  &lt;li&gt;Static compilation enables dead code elimination. The size of packaged Clojure apps could be reduced significantly.&lt;/li&gt;
  &lt;li&gt;Static compilation enables other compiler optimizations that are not possible under a dynamic, “nothing is certain” environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;but-is-it-still-clojure&quot;&gt;But is it still &lt;em&gt;Clojure&lt;/em&gt;?&lt;/h3&gt;

&lt;p&gt;In theory the default Clojure compiler and runtime could be used for development. The same REPL development story would work, tests could redefine variables, and any other dynamic features would be available.&lt;/p&gt;

&lt;p&gt;At run time, however, it would be a different story. A large laundry list of features would be dropped from the Clojure runtime. The lean runtime would be fast but lack the elegant symmetry and power of near equivalent run time and compile time features. It could also limit some of the interop with existing libraries which rely on dynamic rebinding of variables.&lt;/p&gt;

&lt;p&gt;In many ways this would be similar to ClojureScript. ClojureScript has a &lt;a href=&quot;https://github.com/clojure/clojurescript/wiki/Differences-from-Clojure&quot;&gt;subset&lt;/a&gt; of Clojure functionality. Among other differences, it lacks runtime evaluation and compilation, vars, and much of the runtime access to variables. This lean runtime would provide a similar subset of Clojure functionality, while taking it further in some respects.&lt;/p&gt;

&lt;p&gt;It also looks like Common Lisp has corresponding features that allow the developer to specify what is compiled for speed and what is interpreted for flexibility. Some helpful Common Lisp guru could tell if these features are relevant. Scala would be another source for inspiration, as it generates static code much different than Clojure’s code.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;The Clojure bootstrapping process needs to do much less work for Clojure programs to start in a reasonable time for library or Android applications. I have presented here five different ideas for achieving faster start times in Clojure:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Stripped, application-specific runtime&lt;/li&gt;
  &lt;li&gt;Optimized runtime loading&lt;/li&gt;
  &lt;li&gt;Modularized clojure.core&lt;/li&gt;
  &lt;li&gt;Lazy initialization of Clojure functions&lt;/li&gt;
  &lt;li&gt;Lean JVM runtime&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The only idea that I feel goes far enough to achieve reasonable performance on Android is the lean JVM runtime idea. It is probably the most difficult of these ideas to implement but it is the idea I will try to move forward with.&lt;/p&gt;

&lt;p&gt;There are lots of unanswered questions here. The implementation details of the lean JVM runtime are still vague. What do you think? Am I missing important details? Am I in completely over my head? Are there other good ideas out there? What would you like to see?&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;small&gt;
&lt;a name=&quot;refInterpretation&quot;&gt;[1]&lt;/a&gt; Clojure is cited as a purely compiled language in a number of places. But then why does &lt;a href=&quot;https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L7098&quot;&gt;this&lt;/a&gt; look so much like interpretation? Is Java bytecode involved in some way that I can’t see right now? &lt;strong&gt;Update:&lt;/strong&gt; as per Alex Miller’s comment, yes, it is actually compiled.
&lt;/small&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;small&gt;&lt;/small&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Edit 2014-03-19: removed all references to interpretation as per Alex Miller’s correction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&amp;lt;/small&amp;gt;&lt;/p&gt;

</description>
                                <pubDate>Wed, 19 Mar 2014 00:00:00 +0000</pubDate>
                                <link>http://blog.ndk.io/solving-clojure-boot-time.html</link>
                                <guid isPermaLink="true">http://blog.ndk.io/solving-clojure-boot-time.html</guid>
                        </item>
                
        </channel>
</rss>
