tag:blogger.com,1999:blog-328113562024-03-05T07:29:20.683-08:00ProgrammingTimothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comBlogger57125tag:blogger.com,1999:blog-32811356.post-14036681058846166422023-10-13T21:42:00.002-07:002023-10-14T11:07:37.236-07:00Clojure REPL tip: Setting up a retry key binding<p><span face="-apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"" style="background-color: white; color: #24292e; font-size: 16px;">One thing that I love about Clojure is that the Read-Eval-Print-Loop (REPL) enables me to redefine functions on the fly, without restarting anything. This makes trying things, making changes, and trying them again very fluid. Trying stuff, changing it, and trying it again pretty much sums up how I write code.</span></p><blockquote style="background-color: white; border-left: 0.25em solid rgb(223, 226, 229); box-sizing: border-box; color: #6a737d; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin: 0px 0px 16px; padding: 0px 1em;"><p style="box-sizing: border-box; margin-bottom: 16px; margin-top: 0px;">If at first you don’t succeed, dust yourself off and try again.</p><p style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px;">– <cite style="box-sizing: border-box;">Aaliyah</cite></p></blockquote><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">Today I’m going to share with you my favorite “try it again” tip, which is to bind keys to <em style="box-sizing: border-box;">try something</em> and <em style="box-sizing: border-box;">try it again</em>.</p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;"><br /></p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/fY8KgXf2Pjs" width="320" youtube-src-id="fY8KgXf2Pjs"></iframe></div><br /><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;"><br /></p><h2 id="repl-usage" style="background-color: white; border-bottom: 1px solid rgb(234, 236, 239); box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;">REPL usage<a aria-label="Anchor" class="anchorjs-link" data-anchorjs-icon="" href="https://timothypratley.github.io/claykind/docs/try-again#repl-usage" style="-webkit-font-smoothing: antialiased; background-color: transparent; box-sizing: border-box; color: #0366d6; font-family: anchorjs-icons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 1em; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; opacity: 0; padding-left: 0.375em; text-decoration-line: none;"></a></h2><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">Let’s recap basic REPL usage. I can type stuff into the REPL input window and evaluate some code. Generally I don’t do that though. I’m not usually <em style="box-sizing: border-box;">in</em> the REPL, I’m <em style="box-sizing: border-box;">in</em> my source code sending forms to the REPL. To achieve that requires some essential key bindings.</p><table style="background-color: white; border-collapse: collapse; border-spacing: 0px; color: #24292e; display: block; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px; overflow: auto; width: 980px;"><thead style="box-sizing: border-box;"><tr style="border-top: 1px solid rgb(198, 203, 209); box-sizing: border-box;"><th style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px; text-align: right;">Binding</th><th style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px;">Action</th></tr></thead><tbody style="box-sizing: border-box;"><tr style="border-top: 1px solid rgb(198, 203, 209); box-sizing: border-box;"><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px; text-align: right;"><code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">alt-t</code></td><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px;">Evaluate the expression before the cursor</td></tr><tr style="background-color: #f6f8fa; border-top: 1px solid rgb(198, 203, 209); box-sizing: border-box;"><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px; text-align: right;"><code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">cmd-t</code></td><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px;">Evaluate top-level expression at the cursor</td></tr><tr style="border-top: 1px solid rgb(198, 203, 209); box-sizing: border-box;"><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px; text-align: right;"><code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">ctrl-c</code></td><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px;">Run test under cursor</td></tr><tr style="background-color: #f6f8fa; border-top: 1px solid rgb(198, 203, 209); box-sizing: border-box;"><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px; text-align: right;"><code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">shift-ctrl-c</code></td><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px;">Run all tests in namespace</td></tr><tr style="border-top: 1px solid rgb(198, 203, 209); box-sizing: border-box;"><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px; text-align: right;"><code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">ctrl-c</code></td><td style="border: 1px solid rgb(223, 226, 229); box-sizing: border-box; padding: 6px 13px;">Re-run last test action</td></tr></tbody></table><h2 id="the-problem-" style="background-color: white; border-bottom: 1px solid rgb(234, 236, 239); box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;">The Problem 😢<a aria-label="Anchor" class="anchorjs-link" data-anchorjs-icon="" href="https://timothypratley.github.io/claykind/docs/try-again#the-problem-" style="-webkit-font-smoothing: antialiased; background-color: transparent; box-sizing: border-box; color: #0366d6; font-family: anchorjs-icons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 1em; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; opacity: 0; padding-left: 0.375em; text-decoration-line: none;"></a></h2><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">A common scenario I find myself in is that I set up a little bit of code to try something. Then I make some changes, and need to retry the same snippet or test. In the meantime I have navigated away and I need to either go back to the form I am interested in, or navigate to the REPL and use history to try it again. Both of these strategies work, but there is a better way!</p><h2 id="the-solution-" style="background-color: white; border-bottom: 1px solid rgb(234, 236, 239); box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;">The Solution 💡<a aria-label="Anchor" class="anchorjs-link" data-anchorjs-icon="" href="https://timothypratley.github.io/claykind/docs/try-again#the-solution-" style="-webkit-font-smoothing: antialiased; background-color: transparent; box-sizing: border-box; color: #0366d6; font-family: anchorjs-icons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 1em; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; opacity: 0; padding-left: 0.375em; text-decoration-line: none;"></a></h2><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">The idea is to create a REPL command that creates a function in the <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">user</code> namespace. Then we can make a keybinding like “send form before caret to REPL”, and another keybinding to execute the saved function. That way even if we send other forms to the REPL, we can conveniently retry the snippet we are focused on.</p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">Here is how I set the “try” command up: <img alt="img" src="https://timothypratley.github.io/claykind/docs/try-form-before-caret.png" style="border-style: none; box-sizing: content-box; max-width: 100%;" /></p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">I use Cursive Clojure (IntelliJ). If you use a different editor, you can find instructions for creating key bound commands in the <a href="https://scicloj.github.io/clay/#setup" style="background-color: transparent; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Clay setup documentation</a>.</p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;"><code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">~form-before-caret</code> gets replaced with code.</p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">So if my cursor is at the end of an expression (cursor shown as <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">_</code>):</p><div class="language-clojure highlighter-rouge" style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="p" style="box-sizing: border-box;">(</span><span class="nb" style="box-sizing: border-box; color: #0086b3;">+</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="mi" style="box-sizing: border-box; color: #009999;">1</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="mi" style="box-sizing: border-box; color: #009999;">2</span><span class="p" style="box-sizing: border-box;">)</span><span class="n" style="box-sizing: border-box;">_</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">My editor will send the following code to the REPL:</p><div class="language-clojure highlighter-rouge" style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="p" style="box-sizing: border-box;">(</span><span class="nf" style="box-sizing: border-box; color: #990000; font-weight: bold;">do</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">(</span><span class="nf" style="box-sizing: border-box; color: #990000; font-weight: bold;">intern</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="ss" style="box-sizing: border-box; color: #990073;">'user</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="ss" style="box-sizing: border-box; color: #990073;">'retry</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">(</span><span class="k" style="box-sizing: border-box; color: black; font-weight: bold;">fn</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="n" style="box-sizing: border-box;">retry</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">[]</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="p" style="box-sizing: border-box;">(</span><span class="nb" style="box-sizing: border-box; color: #0086b3;">+</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="mi" style="box-sizing: border-box; color: #009999;">1</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="mi" style="box-sizing: border-box; color: #009999;">2</span><span class="p" style="box-sizing: border-box;">)))</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="p" style="box-sizing: border-box;">(</span><span class="nf" style="box-sizing: border-box; color: #990000; font-weight: bold;">user/retry</span><span class="p" style="box-sizing: border-box;">))</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">This creates the <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">retry</code> var in the <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">user</code> namespace (<code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">user/retry</code>), and assigns it to a function that will run the code I am interested in.</p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">It is worth considering a few questions about namespaces at this point. <em style="box-sizing: border-box;">Which namespace will the command execute in?</em> <em style="box-sizing: border-box;">How can <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">user/retry</code> “see” variables in the currently edited namespace?</em></p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">The command will execute in the namespace of the file I am editing, because I configured the command to do so through Cursive. The function is created in the current namespace, because <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">(fn retry [] (+ 1 2))</code> is eagerly evaluated as an argument to <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">intern</code>. Because the function is created in the current namespace, <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">x</code> is resolved correctly. <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">user/retry</code> can be invoked from any namespace by the fully qualified name of the var <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">user/retry</code>.</p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">Here is how I set the “retry” command which simply invokes <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">(user/retry)</code>: <img alt="img" src="https://timothypratley.github.io/claykind/docs/retry-form.png" style="border-style: none; box-sizing: content-box; max-width: 100%;" /></p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">And here are the keybindings for the commands I use: <img alt="img" src="https://timothypratley.github.io/claykind/docs/retry-keybindings.png" style="border-style: none; box-sizing: content-box; max-width: 100%;" /></p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">I’ve bound <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">alt-z</code> to <span style="box-sizing: border-box; font-weight: 600;">“try form before caret”</span> and <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">ctrl-z</code> to <span style="box-sizing: border-box; font-weight: 600;">“retry”</span>.</p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">Now let’s take it for a spin. Here is some code I’ve been working on; I’m drawing a heart shape.</p><div class="language-clojure highlighter-rouge" style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="p" style="box-sizing: border-box;">(</span><span class="k" style="box-sizing: border-box; color: black; font-weight: bold;">def</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="n" style="box-sizing: border-box;">heart-path</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="s" style="box-sizing: border-box; color: #dd1144;">"M 12.0 7.2 C 10.5 5.6 8.1 5.2 6.3 6.7 C 4.5 8.1 4.2 10.6 5.7 12.4 L 12.0 18.3 L 18.3 12.4 C 19.7 10.6 19.5 8.1 17.7 6.7 C 15.8 5.2 13.4 5.6 12.0 7.2 Z"</span><span class="p" style="box-sizing: border-box;">)</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div><blockquote style="background-color: white; border-left: 0.25em solid rgb(223, 226, 229); box-sizing: border-box; color: #6a737d; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin: 0px 0px 16px; padding: 0px 1em;"><div class="language-clojure highlighter-rouge" style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="s" style="box-sizing: border-box; color: #dd1144;">"#'blog.try-again/heart-path"</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div></blockquote><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">This shape comes from <a href="https://www.nan.fyi/svg-paths" style="background-color: transparent; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">svg-paths</a> by <a href="https://twitter.com/nandafyi" style="background-color: transparent; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">@nandafyi</a>.</p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">I’ll visualize it in an SVG image:</p><div class="language-clojure highlighter-rouge" style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="p" style="box-sizing: border-box;">(</span><span class="k" style="box-sizing: border-box; color: black; font-weight: bold;">defn</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="n" style="box-sizing: border-box;">svg</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">[</span><span class="o" style="box-sizing: border-box; color: black; font-weight: bold;">&</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="n" style="box-sizing: border-box;">body</span><span class="p" style="box-sizing: border-box;">]</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="p" style="box-sizing: border-box;">(</span><span class="nb" style="box-sizing: border-box; color: #0086b3;">into</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="o" style="box-sizing: border-box; color: black; font-weight: bold;">^</span><span class="p" style="box-sizing: border-box;">{</span><span class="no" style="box-sizing: border-box; color: teal;">:kindly/kind</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="no" style="box-sizing: border-box; color: teal;">:kind/hiccup</span><span class="p" style="box-sizing: border-box;">}</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="p" style="box-sizing: border-box;">[</span><span class="no" style="box-sizing: border-box; color: teal;">:svg</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">{</span><span class="no" style="box-sizing: border-box; color: teal;">:width</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="mi" style="box-sizing: border-box; color: #009999;">256</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="no" style="box-sizing: border-box; color: teal;">:height</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="mi" style="box-sizing: border-box; color: #009999;">256</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="no" style="box-sizing: border-box; color: teal;">:viewBox</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">[</span><span class="mi" style="box-sizing: border-box; color: #009999;">0</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="mi" style="box-sizing: border-box; color: #009999;">0</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="mi" style="box-sizing: border-box; color: #009999;">24</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="mi" style="box-sizing: border-box; color: #009999;">24</span><span class="p" style="box-sizing: border-box;">]</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="no" style="box-sizing: border-box; color: teal;">:xmlns</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="s" style="box-sizing: border-box; color: #dd1144;">"http://www.w3.org/2000/svg"</span><span class="p" style="box-sizing: border-box;">}]</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="n" style="box-sizing: border-box;">body</span><span class="p" style="box-sizing: border-box;">))</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div><blockquote style="background-color: white; border-left: 0.25em solid rgb(223, 226, 229); box-sizing: border-box; color: #6a737d; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin: 0px 0px 16px; padding: 0px 1em;"><div class="language-clojure highlighter-rouge" style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="s" style="box-sizing: border-box; color: #dd1144;">"#'blog.try-again/svg"</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div></blockquote><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">The heart path goes into an SVG element:</p><div class="language-clojure highlighter-rouge" style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="p" style="box-sizing: border-box;">(</span><span class="k" style="box-sizing: border-box; color: black; font-weight: bold;">defn</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="n" style="box-sizing: border-box;">heart</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">[]</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="p" style="box-sizing: border-box;">[</span><span class="no" style="box-sizing: border-box; color: teal;">:path</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">{</span><span class="no" style="box-sizing: border-box; color: teal;">:fill</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="s" style="box-sizing: border-box; color: #dd1144;">"green"</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="no" style="box-sizing: border-box; color: teal;">:d</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="n" style="box-sizing: border-box;">heart-path</span><span class="p" style="box-sizing: border-box;">}])</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div><blockquote style="background-color: white; border-left: 0.25em solid rgb(223, 226, 229); box-sizing: border-box; color: #6a737d; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin: 0px 0px 16px; padding: 0px 1em;"><div class="language-clojure highlighter-rouge" style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="s" style="box-sizing: border-box; color: #dd1144;">"#'blog.try-again/heart"</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div></blockquote><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">And to see what it looks like I’ll make a <a href="https://www.youtube.com/watch?v=Qx0-pViyIDU&t=1229s" style="background-color: transparent; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">“rich comment block”</a>:</p><div class="language-clojure highlighter-rouge" style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="p" style="box-sizing: border-box;">(</span><span class="nb" style="box-sizing: border-box; color: #0086b3;">comment</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="p" style="box-sizing: border-box;">(</span><span class="nf" style="box-sizing: border-box; color: #990000; font-weight: bold;">svg</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">(</span><span class="nf" style="box-sizing: border-box; color: #990000; font-weight: bold;">heart</span><span class="p" style="box-sizing: border-box;">)))</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">The comment block allows me to conveniently send the expression <code class="language-plaintext highlighter-rouge" style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">(svg (heart))</code> to the REPL, and of course we’ll use my new <span style="box-sizing: border-box; font-weight: 600;">“try form before caret”</span> on it.</p><div class="language-clojure highlighter-rouge" style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="p" style="box-sizing: border-box;">(</span><span class="nf" style="box-sizing: border-box; color: #990000; font-weight: bold;">svg</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">(</span><span class="nf" style="box-sizing: border-box; color: #990000; font-weight: bold;">heart</span><span class="p" style="box-sizing: border-box;">))</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div><svg height="256" style="background-color: white; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;" viewbox="0 0 24 24" width="256" xmlns="http://www.w3.org/2000/svg"><path d="M 12.0 7.2 C 10.5 5.6 8.1 5.2 6.3 6.7 C 4.5 8.1 4.2 10.6 5.7 12.4 L 12.0 18.3 L 18.3 12.4 C 19.7 10.6 19.5 8.1 17.7 6.7 C 15.8 5.2 13.4 5.6 12.0 7.2 Z" fill="green"></path></svg><span face="-apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"" style="background-color: white; color: #24292e; font-size: 16px;"></span><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">Now I can navigate throughout my code base, making changes and fixes, and seeing the effect is just one keystroke away with <span style="box-sizing: border-box; font-weight: 600;">“retry”</span>:</p><div class="language-clojure highlighter-rouge" style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="p" style="box-sizing: border-box;">(</span><span class="k" style="box-sizing: border-box; color: black; font-weight: bold;">defn</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="n" style="box-sizing: border-box;">heart</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">[]</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="p" style="box-sizing: border-box;">[</span><span class="no" style="box-sizing: border-box; color: teal;">:path</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">{</span><span class="no" style="box-sizing: border-box; color: teal;">:fill</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="s" style="box-sizing: border-box; color: #dd1144;">"red"</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span><span class="no" style="box-sizing: border-box; color: teal;">:d</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="n" style="box-sizing: border-box;">heart-path</span><span class="p" style="box-sizing: border-box;">}])</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div><blockquote style="background-color: white; border-left: 0.25em solid rgb(223, 226, 229); box-sizing: border-box; color: #6a737d; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin: 0px 0px 16px; padding: 0px 1em;"><div class="language-clojure highlighter-rouge" style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="s" style="box-sizing: border-box; color: #dd1144;">"#'blog.try-again/heart"</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div></blockquote><div class="language-clojure highlighter-rouge" style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;"><div class="highlight" style="background-color: #f8f8f8; box-sizing: border-box; margin-bottom: 16px;"><pre class="highlight" style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 0px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;"><span class="p" style="box-sizing: border-box;">(</span><span class="nf" style="box-sizing: border-box; color: #990000; font-weight: bold;">svg</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;"> </span><span class="p" style="box-sizing: border-box;">(</span><span class="nf" style="box-sizing: border-box; color: #990000; font-weight: bold;">heart</span><span class="p" style="box-sizing: border-box;">))</span><span class="w" style="box-sizing: border-box; color: #bbbbbb;">
</span></code></pre></div></div><svg height="256" style="background-color: white; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px;" viewbox="0 0 24 24" width="256" xmlns="http://www.w3.org/2000/svg"><path d="M 12.0 7.2 C 10.5 5.6 8.1 5.2 6.3 6.7 C 4.5 8.1 4.2 10.6 5.7 12.4 L 12.0 18.3 L 18.3 12.4 C 19.7 10.6 19.5 8.1 17.7 6.7 C 15.8 5.2 13.4 5.6 12.0 7.2 Z" fill="red"></path></svg><span face="-apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"" style="background-color: white; color: #24292e; font-size: 16px;"></span><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">Setting up the command to sync all changes before executing makes it easy to test changes as I go, often I can rely on syncing instead of sending updates to the REPL individually.</p><p style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; margin-bottom: 16px; margin-top: 0px;">Special thanks to <a href="https://twitter.com/chrishouser" style="background-color: transparent; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">@chrishouser</a> and <a href="https://hachyderm.io/@IPmonger">@IPmonger@hachyderm.io</a> for dedicating a session of <a href="https://chouser.us/lisp2022/" style="background-color: transparent; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">LiSP reading group</a> to improving my workflow with try/retry.</p>Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-39728252711265159872023-05-03T01:58:00.000-07:002023-05-03T01:58:24.542-07:00The Hallway Track: Clojure/Conj 2023 in Durham<div style="text-align: left;"><span style="font-weight: normal;">These are my notes and impressions from the hallway conversations during Clojure/Conj 2023 conference. I'm sharing them in the hope of capturing some of the atmosphere and provide a peek into what people in the Clojure community are up to and what companies are using Clojure.</span></div><div style="text-align: left;"><span style="font-weight: normal;"><br /></span></div><h2 style="text-align: left;">General observations</h2><div><span id="docs-internal-guid-94d179e9-7fff-2fb6-9588-40faf94ce7dd"><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px; text-align: left;"><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Database announcements:</span></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li><a href="https://blog.datomic.com/2023/04/datomic-is-free.html" style="font-family: Arial; font-size: 11pt; text-decoration-line: none; white-space: pre;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Datomic is free now</span></a></li><li><a href="https://www.xtdb.com/v2" style="font-family: Arial; font-size: 11pt; text-decoration-line: none; white-space: pre;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">XTDB v2</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> and Cloud</span></li><li><a href="https://flur.ee/" style="font-family: Arial; font-size: 11pt; text-decoration-line: none; white-space: pre;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Fluree</span></a></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">BI and reporting for Datomic</span></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">And many rumors about new options arriving soon </span></li></ul><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Several non-JVM dialects of Clojure are appearing:</span></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li><a href="https://github.com/Tensegritics/ClojureDart" style="font-family: Arial; font-size: 11pt; text-decoration-line: none; white-space: pre;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">ClojureDart</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> (Dart)</span></li><li><a href="https://jank-lang.org/" style="font-family: Arial; font-size: 11pt; text-decoration-line: none; white-space: pre;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Jank</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> (C++)</span></li><li><a href="https://github.com/kalai-transpiler/kalai" style="font-family: Arial; font-size: 11pt; text-decoration-line: none; white-space: pre;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Kalai</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> (Rust)</span></li><li><a href="https://github.com/pilisp/pilisp" style="font-family: Arial; font-size: 11pt; text-decoration-line: none; white-space: pre;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">PiLisp</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> (Dart)</span></li></ul><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Attendance:</span></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Everyone was excited to be able to mingle and chat.</span></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Mostly returning Conj’rs.</span></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Lots of people, my guess is more than 500.</span></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Rich’s keynote was packed to capacity.</span></li></ul><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Hiring</span></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">There was a conspicuous absence of “we are hiring” pitches. Attendees and sponsors were recruiting customers and clients rather than employees this year.</span></li></ul><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Swag</span></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li><a href="https://www.metosin.fi/en/" style="font-family: Arial; font-size: 11pt; text-decoration-line: none; white-space: pre;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Metosin</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> had an awesome black “programmer” t-shirt, bag, and chocolate!</span></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">The Conj trucker hat is cool</span></li></ul><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Themes</span></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Appreciation of community the opportunity to congregate</span></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">High adoption of Malli and Reitit</span></li></ul><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Talks</span></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">All of the talks were excellent, </span>I highly recommend watching them on <a href="https://www.youtube.com/@ClojureTV/videos" style="font-family: Arial; font-size: 11pt; text-decoration-line: none; white-space: pre;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">ClojureTV</span></a></li></ul><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Travel</span></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Passing over the magnificent snow-capped mountains of America is an inspiring thing to experience.</span></li></ul></ul><div><span><br /></span></div></span><h2 style="text-align: left;"><span>Conversations</span></h2><div><br /><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px; text-align: left;"><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Toby (</span><a href="https://www.shortcut.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Shortcut</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">House construction</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Hiking destinations</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Dr Wolf chess app</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Wishes there was a mid-priced support tier for Datomic; w</span><span style="font-size: 11pt; white-space: pre-wrap;">ill the free tier be less responsive now?</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Jon</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Thinking about how to mine his own data</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Mark (</span><a href="https://www.metabase.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Metabase</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The importance of asking “what’s the problem?”</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Francis (</span><a href="https://www.shortcut.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Shortcut</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">We decanted our Datomic DB</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Constant time lookup was crucial to decanting</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Reading the TX log, running transformations</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Throwing stuff away: attributes we don’t use, entities like VCS data (we don’t need to replicate git)</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Large strings</span></p></li></ul><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Precompute the changes we need in a database</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">SQLite is fast but not fast enough for billions of entities. We used an on-disk hash table. Perfect hash, immutable</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">We now have less work per TX, more storage runway, and 20% speedups</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Backups are 7x faster</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Don’t use full text index</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Thinking about how to optimize blobs and working on the GraphQL API</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Dan (</span><a href="https://www.metabase.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Metabase</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Great to see a fellow member of the LiSP reading group</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Paul (</span><a href="https://sovasage.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">sovaSage</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Solving sleep apnea</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">It would have been nice to have more time to discuss Rich’s keynote</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Daniel (</span><a href="https://www.shortcut.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Shortcut</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Made a Clojure dialect </span><a href="https://github.com/pilisp/pilisp" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">PiLisp</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> </span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Pipe can be used for concatenative style</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Dart is a good target, it’s fast</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Evan (</span><a href="https://www.shortcut.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Shortcut</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Concerned about the rise of the unnecessary use of Kerbenutes</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Denise (</span><a href="https://www.viasat.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Viasat</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Starting a new project Java -> Clojure rewrite</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Challenges of managing teams, and the value of hearing when people feel that it’s working</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Launching rockets; launch delays are costly</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Suggested a templates gallery of diagrams for <a href="https://Hummi.app">Hummi.app</a> would help new users figure out what they can make. Editable examples.</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Aaron (</span><a href="https://www.viasat.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Viasat</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Theory of constraints, </span><a href="https://flyinglogic.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Flying logic</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> is awesome for diagrams, but expensive</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Causal reasoning is interesting and applicable to medical/ethical questions</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">George (</span><a href="https://www.cognitect.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Cognitect</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">/</span><a href="https://nubank.com.br/en/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">NuBank</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Love the Clojure community</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Learning new things</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Tight knit</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Everyone is interested in what other people are doing</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Less competition.</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Approachability</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">More personal and intimate that big tech conferences</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Contactable</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Niche in numbers, but still able to have </span><a href="https://www.clojuriststogether.org/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Clojurists together</span></a></p></li></ul><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Embracing change, things change, we evolve</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">I like Onyx. I used it for Climate data analysis</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Jeremy Taylor (</span><a href="https://www.juxt.pro/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Juxt</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">I’ve dedicated my life to building the cathedral of a DB </span><a href="https://www.xtdb.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">XTDB</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">, and it’s really good</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Arni (</span><a href="https://gaiwan.co/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Gaiwan</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">New project: </span><a href="https://github.com/lambdaisland/ornament" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Ornament</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> for styling. Particularly helpful when debugging to be able to connect named classes to your code.</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">BI and reporting for Datomic incoming</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Dave (</span><a href="https://kevel.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Kevel</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Created a music language </span><a href="https://alda.io/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Alda</span></a></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Interested in diagrams, mostly using <a href="https://plantuml.com/">PlantUML</a>, knows the → longer arrow trick.</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Finn (</span><a href="https://www.juxt.pro/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Juxt</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Working on </span><a href="https://www.xtdb.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">XTDB</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> Cloud</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Josh (</span><a href="https://roamresearch.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">RoamResearch</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Interested in seeing a demo of </span><a href="https://hummi.app/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Hummi.app</span></a></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Connor (</span><a href="https://roamresearch.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">RoamResearch</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">It’s all about interface: data entry widgets and connections</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">We are doing everything all at once, experimenting on ideas to make the future tool for thought.</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Ivan (</span><a href="https://www.shortcut.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Shortcut</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Data driven test scenario evaluator that creates and binds entities</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Eli (</span><a href="https://www.shortcut.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Shortcut</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Strategy in the bird game is to gather eggs</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Nathan</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Mining PDF data</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">SQL</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Lambda Island</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Institutional research, students, reports, dashboards, classes need</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Detective ability to explore</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Nil, it’s always nil</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Boolean edge cases are mysterious</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Ravi (</span><a href="https://nilenso.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Nilenso</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Healthcare, public record</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Deepa (</span><a href="https://nilenso.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Nilenso</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Solving hypertension. An ignored health issue tied to lifestyle.</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Diagrams should be intuitive, shouldn’t be ugly</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://excalidraw.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Excalidraw</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> is good, but too much energy wasted dragging things around</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://mermaid.js.org/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Mermaid</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> is good for seq diagrams</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Jim</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The best things come from one person who thinks deeply about one thing</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Christophi</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Recommended a book about the benefits of a diverse background: Range by David Epstein.</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Chris (</span><a href="https://www.treasuryprime.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Treasury Prime</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Most popular project is chessboardjs from 2011, just rewrote </span><a href="https://github.com/oakmac/chessboard2" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">v2</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> in ClojureScript</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Played a lot as a youth, 1800 rated. (That means he can beat most chess players every time).</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://github.com/HumbleUI/HumbleUI" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">HumbleUI</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> is awesome: Smooth, animation, save/reload</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Writing a FAQ for </span><a href="https://shaunlebron.github.io/parinfer/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">parinfer</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> to answer the many questions he gets about the status of the project.</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Lua is convenient for plugins</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Discussed HSDS and .app TLD requiring HTTPS</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The sponsors are mostly consulting firms this year</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Remembered my previous hallway track drawings. I haven’t been sketching this Conj, but he reminded me that I do like drawing charactures</span></p></li></ul></ul><div><span style="font-family: Arial;"><span style="font-size: 14.6667px; white-space: pre-wrap;"><br /></span></span></div><div><span style="font-family: Arial;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAxkc148ilZxcabT-pFNufaRpRLjoAjZzWrP7upHMsAFxgqEEUgkyIuCD0RIa5SFnXHfiYY9VceKj7d4391QRFSAfq7Ac8zYZtMv4g-f7cgXoAerkEDIMYlv-hHhwF4E0KJ_71KycGP0vTbtWR38M11PjO3PK3kSSdYVmzozVvXGDUIJfKZ_c/s1792/rich_vs_chris_chess.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1194" data-original-width="1792" height="426" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAxkc148ilZxcabT-pFNufaRpRLjoAjZzWrP7upHMsAFxgqEEUgkyIuCD0RIa5SFnXHfiYY9VceKj7d4391QRFSAfq7Ac8zYZtMv4g-f7cgXoAerkEDIMYlv-hHhwF4E0KJ_71KycGP0vTbtWR38M11PjO3PK3kSSdYVmzozVvXGDUIJfKZ_c/w640-h426/rich_vs_chris_chess.png" width="640" /></a></div><br /><span style="font-size: 14.6667px; white-space: pre-wrap;"><br /></span></span></div><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px; text-align: left;"><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://twitter.com/oakmac1/status/1653456872748220417" style="font-size: 11pt;">The correct way to pronounce Malli</a></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Alex (</span><a href="https://www.cognitect.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Cognitect</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">/</span><a href="https://nubank.com.br/en/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">NuBank</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">I wish tool makers would adopt the error reporting features in Clojure 1.10+</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Deps has finally overtaken Leiningen in popularity in this year’s survey.</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Elango (</span><a href="https://about.google/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Google</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The Clojure community is very approachable</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Brandon</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Saw the gap in the diagram solution space</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Working with security questionnaire forms using Mali</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Enjoys Clojurians Slack #ClojureScript channel</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Russell</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Electrical diagram layouts are tough due to the tendency towards overlapping lines</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Dustin (</span><a href="https://github.com/hyperfiddle/electric" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Electric</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">CSS styling spreadsheet controls</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Proctor (</span><a href="https://www.rate.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Guaranteed Rate</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">CLJS AWS Lambda functions</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Kimmo (</span><a href="https://www.metosin.fi/en/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Metosin</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Lots of Clojure happening in Finland</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Daniel2 (</span><a href="https://www.rate.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Guaranteed Rate</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Starting a new Mortgage underwriting automation (ML)</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Mathew</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">How can we best help people learn Clojure?</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Jayden</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Building an e-commerce platform (like Shopify)</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Interested in building a tool for thought</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Carin</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Book recommendation: The murderbot diaries</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Janet (</span><a href="https://janetacarr.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Freelance</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Enjoying the opportunity to network</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">David (</span><a href="https://vouch.io/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Vouch</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Life is busy when you have a 5 year old!</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Jeaye (</span><a href="https://www.ea.com/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">EA</span></a><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Building Jank</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Cling is a pain because releases are inconsistent</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Yes, you can use .cljc instead of .jank if you want to</span></p></li><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">let* instead of let</span></p></li></ul><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Pricing drives perceptions of quality</span></p></li></ul><li aria-level="1" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Rich</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Turns up to the talks, mixes with everyone</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Design, preparing to design, stuff happens before you can design</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">There’s more to it than hammock time</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Communicating progress and backtracking</span></p></li><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="3" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: square; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Tickets</span></p></li></ul><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Define the problem</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">List possible solutions</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Create a criteria for evaluating solutions</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Sleep on it before you code</span></p></li><li aria-level="2" style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; list-style-type: circle; vertical-align: baseline; white-space: pre;"><p role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Diagrams are useful for planning and documenting</span></p></li></ul></ul><div><span style="font-family: Arial;"><span style="font-size: 14.6667px; white-space: pre-wrap;"><br /></span></span></div><div><br /></div><br /><h2 style="text-align: left;">Final thoughts</h2></div><div><br /></div>My favorite part of the conference came from a humorous, cheeky, and insightful audience question: “Is there the risk of an unintended consequence of putting Clojure code measuring 7 lines in length right next to the output measuring hundreds of lines of length and causing a world wide crash in interest in languages like Java? On a more serious note, I’ve heard that Clojure has a weakness in error messages. Has the answer been staring us in the face? Can we compile Clojure to Rust and get the error messages from Rust?” Earning applause and laughs from the entire audience who could see both the appeal of the idea, and the funny/ironic side of it. To me this exemplified that Clojurists are innovative thinkers, open to new ideas, and retain a keen sense of humor. I really enjoyed soaking up the ideas, ambitions, enthusiasm, and humor from everyone I met at the Conj.</div>Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-15338088399464650372023-03-07T12:41:00.000-08:002023-03-07T12:41:54.781-08:00How to recognize collider bias<div><span id="docs-internal-guid-7beadf4b-7fff-9e69-40c0-9e7d2d8021a3"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">Marilyn vos Savant holds the highest recorded intelligence quotient (IQ) in the Guinness Book of Records. In 1990 she answered an intriguing question in her regular column in Parade magazine:</p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><i></i></span></p><br /></span></div><blockquote><div><span id="docs-internal-guid-7beadf4b-7fff-9e69-40c0-9e7d2d8021a3">You are on a game show and you are given the choice of three doors. Behind one door is a car. Behind the other doors are goats. You pick a door and the host (Monty Hall) always opens a different door that reveals a goat, never the car. Monty then asks you if you would like to stay with your original choice or choose a different door. <br /><br /></span></div><div><span>Is it to your advantage to switch your choice of doors?</span></div></blockquote><div><span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">This dilemma was named the Monty Hall problem after the host of a game show called "Let's make a deal" who had a reputation for playing mind games with contestants.</p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMsQH4xCkVfb5j5KlZs5EzWXIsYN5eikYwiViQ8HNfwmcw_b-esAyHo4vRDkKFXO8Cii-ZP-osiNt4b0S3vosaCJUY1KoBeCjTna27sdmoU5zAXTVdaHSqjCviOL4SVym9N-sf3kPAA4d0vnvBkPgAdRdyXNs48qGKS3adUpKWeKRCLedWVJ0/s600/montyhall.jpeg" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="195" data-original-width="600" height="130" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMsQH4xCkVfb5j5KlZs5EzWXIsYN5eikYwiViQ8HNfwmcw_b-esAyHo4vRDkKFXO8Cii-ZP-osiNt4b0S3vosaCJUY1KoBeCjTna27sdmoU5zAXTVdaHSqjCviOL4SVym9N-sf3kPAA4d0vnvBkPgAdRdyXNs48qGKS3adUpKWeKRCLedWVJ0/w400-h130/montyhall.jpeg" width="400" /></a></p></span></div><div><br /></div><p> </p><div><span><br /></span><div id="app"></div><p style="text-align: left;"><span><br /></span></p><p style="text-align: left;"><span>Marilyn correctly answered yes, you are twice as likely to win if you switch rather than stay. Switching will win 2 in 3 times, whereas staying will only win 1 in 3 times. She explained why, using the following table of outcomes when you choose door #1.</span></p></div><div><br /><span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-style: italic; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Table 1: Outcomes when you choose door #1</span></p><br /><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col><col></col><col></col><col></col><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Door 1</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Door 2</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Door 3</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Switch</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Stay</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Car</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Goat</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Goat</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Lose</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Win</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Goat</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Car</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Goat</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Win</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Lose</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Goat</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Goat</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Car</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Win</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Lose</span></p></td></tr></tbody></table></div><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Many people seeing the puzzle and solution for the first time find the answer hard to accept.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Common reactions include:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"></p><ul style="text-align: left;"><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">How could it make any difference if I stay or if I swap?</span></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">How can the probability of a door winning change?</span></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">With 2 doors remaining, aren't both 50/50?</span></li><li><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">There's something weird about this!</span></li></ul><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 14.6667px; white-space: pre-wrap;">The solution is counterintuitive, and has become a popular paradox to illustrate how event dependencies bias probabilities. The game show scenario is entirely contrived, but the abstract situation of understanding the biases introduced by event dependence is necessary to correctly assign causation. For that reason, it's worth delving a little deeper into why the scenario seems so strange, and how we can identify situations where such biases arise.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The table is a convincing argument in so much as we can clearly see 3 possible outcomes. But it may remain mysterious as to why each outcome is completely deterministic. </span><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Let's trace through the events in chronological order to see what is going on:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;"><br /></span></p><i>Figure 1: Chronological events of the game<br /></i><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYB1fjmT_lKSTzgmJvJXorqNI1mxGkry4GNhrNCSpbAliuBZ1MOF1mITteyWXyKgTdoxWi7J_b-3N9M3LiYGJ-nAhgDhfbEJgcO6hq8uZQ65z6OUgtiy1ZsySTPfMhUVwgzmjiU3s0k0v_2GX_VGkzQ-jH7hZKcK75_Tv01V8IXvdPTCPPTVQ/s1920/Monty-Hall-Problem.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="1920" data-original-width="1369" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYB1fjmT_lKSTzgmJvJXorqNI1mxGkry4GNhrNCSpbAliuBZ1MOF1mITteyWXyKgTdoxWi7J_b-3N9M3LiYGJ-nAhgDhfbEJgcO6hq8uZQ65z6OUgtiy1ZsySTPfMhUVwgzmjiU3s0k0v_2GX_VGkzQ-jH7hZKcK75_Tv01V8IXvdPTCPPTVQ/w456-h640/Monty-Hall-Problem.png" width="456" /></a></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">First, the game show producer sets up the goats and the car. We'll focus on the scenario of the car being placed behind door #1, and goats behind doors #2 and #3.</p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">Second, we make a choice:</p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"></p><ul style="text-align: left;"><li>If we choose door #1, then Monty will reveal either door #2 or door #3, because both contain goats. It doesn't matter which door he reveals. If we swap to the remaining door it will contain a goat, and we will lose. The only way we win is if we stay.</li><li>If we choose door #2, then Monty can only reveal door #3, because the car is behind door #1. Once he reveals door #3 we can only swap to door #1. So in this case we will win if we swap, and lose if we stay.</li><li>If we choose door #3, then Monty can only reveal door #2, because the car is behind door #1. Once he reveals door #2 we can only swap to door #1. Again we will win if we swap and lose if we stay.</li></ul><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">From the figure 1 we can see that 2 out of 3 times Monty has no choice at all. There is only one door that he can open, and the other door contains the car. We know that the most likely scenario is that our first guess was wrong, that Monty could only open one door, revealing that the car is behind the remaining other door. The wrong guess forces him to reveal the right answer.</p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;"><br /></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">When bound by the rules of the game, Monty's actions are dependent upon two prior events. </span><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Monty's event is constrained by the first event (placement of the car), and your guess (which was random). This configuration of events is called a "collider". Colliders occur whenever a variable has 2 causes.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;"><br /></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;"><i>Figure 2: A collider causal relationship of variables</i></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;"><br /></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWZvZeAsM-Lcy8ydcXN6CqNEzh59qe7eD9ubpRX7rHtztVIG_rDNFIbl5xMYE-YEwtwkZNJ8CLmSpSlKmqZCDGy0UM-VA74ylHOSDdx9x5Ngwjs2IvVdQgQsmxZutr0W_rwgiZE9zHLYtBXL-VkjzezHWYUW-TtIP4pB4BdaNRNe1BI_ledNA/s1920/Monty-Hall-3%20(1).png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1920" data-original-width="1920" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWZvZeAsM-Lcy8ydcXN6CqNEzh59qe7eD9ubpRX7rHtztVIG_rDNFIbl5xMYE-YEwtwkZNJ8CLmSpSlKmqZCDGy0UM-VA74ylHOSDdx9x5Ngwjs2IvVdQgQsmxZutr0W_rwgiZE9zHLYtBXL-VkjzezHWYUW-TtIP4pB4BdaNRNe1BI_ledNA/s320/Monty-Hall-3%20(1).png" width="320" /></a></div><br /></span><br />Collider bias (also known as <a href="https://en.wikipedia.org/wiki/Berkson%27s_paradox">Berkson's bias</a>) shows up in many situations. The precondition is that the action we are considering depends on 2 other events.<br /><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; white-space: pre-wrap;">Notable examples of colliders include:</span></p><span><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"></p><ul style="text-align: left;"><li><span style="font-family: Arial;"><span style="font-size: 14.6667px; white-space: pre-wrap;">Disease 1 -> Hospitalization <- Disease 2
</span></span>If you only look at cases where hospitalization occurred, you are removing cases that affect the frequency of 2 diseases being present.</li></ul><p></p><ul style="text-align: left;"><li><span style="font-family: Arial;"><span style="font-size: 11pt; white-space: pre-wrap;">Drug -> Blood Pressure -> Heart Attack <- Blood Pressure</span><span style="font-size: 14.6667px; white-space: pre-wrap;">
</span></span>If you only look at cases where a heart attack occurred, you are removing cases where the drug prevented the heart attack by lowering blood pressure.</li></ul><ul style="text-align: left;"><li><span style="font-family: Arial;"><span style="font-size: 11pt; white-space: pre-wrap;">Attractive -> Date <- Nice</span><span style="font-size: 14.6667px; white-space: pre-wrap;">
</span></span>If you only judge people you date, you are removing ugly mean people cases, and might mistakenly believe attractive people are mean.</li></ul></span><br />It's as though we flipped 2 coins many times independently, counting the results but ignoring all the times that double tails occurred. Naturally the data will indicate that heads usually cause tails, and tails usually cause heads. We don't set out to collect biased data, but it is easier to gather data on diseases from hospitalized individuals.<br /><br />Bias occurs when conditioning on the collider. Coming back to our original paradox, if we only look at cases where Monty reveals a goat, we are removing cases that affect the probability of winning a car. If Monty always reveals a door, my door causes Monty’s action (⅔ of the time), the frequency that switching wins increase.<br /><br />Conditioning on a collider creates a spurious association between the contributing variables. If 2 events contribute to an event that is always present, there is collider bias. To understand if 2 events cause such an event, one can draw a causal diagram of all the variables and which causes which.<br /><br /><h2 style="text-align: left;">Why the Monty Hall problem is counter-intuitive and entertaining</h2></div><div><br />Reframing the problem to focus on what causes Monty's action helps explain what is going on, but doesn't make us completely comfortable with the solution either. In the context of a game our expectation is that the other player will be trying to beat us.<br /><br />Consider what would happen if Monty could choose whether to reveal a door, or just open your chosen door. Now Monty can play the game adversarially. If you choose the wrong door, he opens it and you lose. If you choose the correct door, he shows a different door in an attempt to get you to switch your choice. Under such circumstances if he does open a door, it means you should stay with your current choice. Switching would lose every time. I think that when presented as a game, our intuition is correctly on alert that Monty is trying to trick us, that he is playing the game to win. Most games are fair or stacked against us, so it would be very strange for someone to propose a game biased in our favor. Furthermore Monty might not have explicitly stated the rules of the game. If we don't know if he had to open a door, or if he just had the opportunity to attempt to trick us, then the safe choice is to stay with our current guess.<br /><br />The paradox makes for interesting game show scenarios. They give us a chance to second guess ourselves. Most people want to stick to their original choice, perhaps reaffirming that they made the correct initial guess through some mystic connection. People who are in on the joke can laugh about it and decry the participants for not taking up twice the opportunity to win.<br /><br />Episodes 41 and 42 of Survivor featured the Monty Hall problem under the title of "do or die". In both cases the contestants chose to stay with their original choice instead of switching (which would have given them a higher chance of winning), and in both cases they actually won (against the odds). It really captured the counter-intuitive nature of the paradox, and the stark fact that we can easily assume we made a good decision because we saw a good outcome, when really we just got lucky.</div><div><div style="text-align: left;"><br /></div><div style="text-align: left;"><i>Figure 3: Survivor featured the opportunity to guess again for immunity</i></div><div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgCMUwvtQwkmOPQDr9uJYPIwy6htRWNwq2TbFaRHhcTvCsnLca7sZIop_oRL5S545IsXect7V_b7OhNOs3FRXTT_xFx_9XPHEEQ8lxAivCwYsXwNYKXHC04oQdjw1UqeV7cEe18rAJbQYqA4Jeu1s3qHoKavRmbzd2N7p47DFMyNWFDiB7Ue8/s1024/survivor-do-or-die-deshawn.webp" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="1024" data-original-width="825" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgCMUwvtQwkmOPQDr9uJYPIwy6htRWNwq2TbFaRHhcTvCsnLca7sZIop_oRL5S545IsXect7V_b7OhNOs3FRXTT_xFx_9XPHEEQ8lxAivCwYsXwNYKXHC04oQdjw1UqeV7cEe18rAJbQYqA4Jeu1s3qHoKavRmbzd2N7p47DFMyNWFDiB7Ue8/w323-h400/survivor-do-or-die-deshawn.webp" width="323" /></a></div></div><div><br /></div><h2 style="text-align: left;">Conclusion</h2><div><br /></div>The takeaway is that recognizing colliders can help us make better decisions. Recognizing a collider is noticing that we are only considering cases where a particular event happens, but that event has 2 causes. And it helps to draw a diagram to find them.<div><br /></div></div>
<script src="https://timothypratley.github.io/montyhall/cljs-out/webapp/main_bundle.js"></script>Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-76518793304729512152022-09-19T08:05:00.000-07:002022-09-19T08:05:20.648-07:00Equations for data transformation: Term Rewriting<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"></link>
<style>
/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment the following line when using as a custom stylesheet */
/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
html{font-family:sans-serif;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
b,strong{font-weight:bold}
abbr{font-size:.9em}
abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
dfn{font-style:italic}
hr{height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type=checkbox],input[type=radio]{padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,::before,::after{box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details{margin-left:1.25rem}
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}
details>summary::-webkit-details-marker{display:none}
details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
pre.pygments span.linenos{display:inline-block;margin-right:.75em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
li>p:empty:only-child::before{content:"";display:inline-block}
ul.checklist>li>p:first-child{margin-left:-1em}
ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt,summary{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]{border-bottom:1px dotted}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<div id="header">
<h1>Term rewriting equations and patterns</h1>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph normal">
<p>For a programmer, functions are the fundamental form of expression.
Programmers write functions that transform inputs into outputs.</p>
</div>
<div class="stemblock">
<div class="content">
\[f(x) = x + 1\]
</div>
</div>
<div class="paragraph">
<p>The inputs and outputs are complex data that represent something in the real world.
I think about the inputs and outputs as abstract data shapes.
I might start with a nested map that needs to be transformed into a sequence of sub-selections.
Data comes in one shape and goes out another.
Squares in, circles out.</p>
</div>
<div class="imageblock">
<div class="title">Figure 1. The square-to-circle machine ™</div>
<div class="content">
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp0IbWA6P1CmrZNlpBbR84yiR-Q14HAyhT1SkzmqM9bvlqHC7f-iMezePDG61rt7nUn_vpKh4_DNjpu3Im8M2I0l4Glk7NkYCh79N8HrQUuAIc1jUO06OBYjWgYPgLhsvKCOCJXabPfMDPBbR_t-0G5MMNjgUxcoNAUvR1VvNRRWXPSQ2TNLI/s1484/fx.png" style="display: block; padding: 1em 0px; text-align: center;"><img border="0" data-original-height="1116" data-original-width="1484" height="482" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp0IbWA6P1CmrZNlpBbR84yiR-Q14HAyhT1SkzmqM9bvlqHC7f-iMezePDG61rt7nUn_vpKh4_DNjpu3Im8M2I0l4Glk7NkYCh79N8HrQUuAIc1jUO06OBYjWgYPgLhsvKCOCJXabPfMDPBbR_t-0G5MMNjgUxcoNAUvR1VvNRRWXPSQ2TNLI/w640-h482/fx.png" width="640" /></a></div></div>
</div>
<div class="paragraph">
<p>For a mathematician, <em>equations</em> are the fundamental form of expression.
Equations define valid rules for transforming one expression into another.
Mathematicians solve problems through symbolic manipulation.</p>
</div>
<div class="stemblock">
<div class="content">
\[\begin{align*}
(a + b)^2 &= (a + b)(a + b)\\
&= aa + ab + ba + bb\\
&= a^2 + 2ab + b^2
\end{align*}\]
</div>
</div>
<div class="paragraph">
<p>Functions and equations are both fine ways to specify transformations.
So why only use functions for programming?
What does it look like to program data transformation like equations instead of functions?
Today we will look at some examples of an equation base approach to specifying data transformations.
Before diving into the examples, let’s first establish some definitions</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_definitions">Definitions</h2>
<div class="sectionbody">
<div class="hdlist">
<table>
<tbody><tr>
<td class="hdlist1">
Term rewriting
</td>
<td class="hdlist2">
<p><em>Replacing terms with other terms</em></p>
</td>
</tr>
<tr>
<td class="hdlist1">
Term
</td>
<td class="hdlist2">
<p><em>A variable or function application:</em> \(a\), \((a + b)\), \((a + b)^2\)</p>
</td>
</tr>
<tr>
<td class="hdlist1">
Variable
</td>
<td class="hdlist2">
<p>\(a,b\) can be assigned a value</p>
</td>
</tr>
<tr>
<td class="hdlist1">
Function
</td>
<td class="hdlist2">
<p><code>add</code>, <code>multiply</code>, <code>square</code></p>
</td>
</tr>
<tr>
<td class="hdlist1">
Rule
</td>
<td class="hdlist2">
<p><em>A pair of matching terms and substitution terms:</em> \((a + b)^2 \Rightarrow a^2 + 2ab + b^2\)</p>
</td>
</tr>
<tr>
<td class="hdlist1">
Matching
</td>
<td class="hdlist2">
<p>\((1 + 3)^2\) <em>can be made equal to</em> \((a + b)^2\) <em>when</em> \(a=1,b=3\)</p>
</td>
</tr>
<tr>
<td class="hdlist1">
Substitution
</td>
<td class="hdlist2">
<p><em>Creation of new terms from old ones</em> \((1 + 3)^2 \Rightarrow 1^2 + 2\cdot1\cdot3 + 2^2\)</p>
</td>
</tr>
</tbody></table>
</div>
<div class="admonitionblock note speaker">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Math equations are rules that strictly preserve equivalence.
Term rewriting is a more general concept that permits any transformation.</p>
</div>
</td>
</tr>
</tbody></table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_introducing_meander">Introducing Meander</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Meander is a term rewriting system packaged as a Clojure library for data transformation.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">[meander/epsilon "0.0.650"]</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(ns meander.examples
(:require [meander.epsilon :as m]))</code></pre>
</div>
</div>
<div class="paragraph">
<p>I prefer to call it a <em>structural pattern syntax</em> for data transformation.</p>
</div>
<div class="sect2">
<h3 id="_rewrite">Rewrite</h3>
<div class="paragraph">
<p>The <code>m/rewrite</code> macro is the swiss army knife of Meander.</p>
</div>
<div class="paragraph">
<p>Let’s do our first transformation from a vector to a map:</p>
</div>
<div class="stemblock">
<div class="content">
\[[a, b, c, d] \Rightarrow \{a, d\}\]
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">; Meander!
(m/rewrite [:a 1 :c 2] ; input
[?a ?b ?c ?d] ; a match pattern
{?a ?d}) ; a substitution pattern
;=> {:a 2 :c 1} ; output</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>The <em>match pattern</em> is a data literal with the shape of the input</p>
</li>
<li>
<p>The <em>substitution pattern</em> is a data literals with the shape of the output</p>
</li>
<li>
<p><code>?a</code> is called a <em>logic variable</em></p>
</li>
<li>
<p><code>_</code> is for ignoring unused terms</p>
</li>
</ul>
</div>
<div class="admonitionblock note speaker">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p><code>?x</code> is a valid symbol in Clojure.
Macros manipulate symbols rather than the values associated with those symbols. <code>m/rewrite</code> is a macro; it defines how to interpret the patterns.
I think of Meander as a syntax because I specify structure with data literals to pattern match and substitute.
Hence why I prefer to call it a "structural pattern syntax".</p>
</div>
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_logic">Logic</h3>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(m/rewrite [1 2] ; input
[?a ?a] ; a match pattern
{?a ?a}) ; a substitute pattern
;=> nil</code></pre>
</div>
</div>
<div class="paragraph">
<p>No match.</p>
</div>
<div class="paragraph">
<p><code>?a</code> cannot match both <code>1</code> and <code>2</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(m/rewrite [1 1] ; input
[?a ?a] ; a match
{?a ?a}) ; a substitution pattern
;=> {1 1}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Matched <code>?a</code> with <code>1</code></p>
</div>
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Unification means resolving logical matches.</p>
</div>
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_repeating_terms">Repeating terms</h3>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(m/rewrite [:a 1 :c 2 :d 3]
[!a ...]
[!a ...])
;=> [:a 1 :c 2 :d 3]</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>!a</code> is a memory variable: <em>an array that can collect many values</em><br />
<code>…</code> indicates 0 or more repeated occurrences</p>
</div>
<div class="admonitionblock note speaker">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The match pattern is symmetrical with the substitution pattern.
We got out exactly what we put in.</p>
</div>
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_repeat_scope">Repeat scope</h3>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(m/rewrite [1 :a 2 :b 3 :c]
[!x !y ...] ; match pairs
[!y !x ...]) ; substitute flipped pairs
;=> [:a 1 :b 2 :c 3]</code></pre>
</div>
</div>
<div class="paragraph">
<p>All terms preceeding <code>…</code> repeat.</p>
</div>
</div>
<div class="sect2">
<h3 id="_separator">Separator</h3>
<div class="paragraph">
<p>Limits the scope of repeats.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(m/rewrite [:hello 1 2 3]
[:hello . !v ...]
[:world . !v ...])
;=> [:world 1 2 3]</code></pre>
</div>
</div>
<div class="paragraph">
<p>Only the terms between <code>.</code> and <code>…</code> repeat.</p>
</div>
</div>
<div class="sect2">
<h3 id="_structurally_explicit">Structurally explicit</h3>
<div class="paragraph">
<p>Most Clojure functions will coerce their inputs to seqs.
Meander does not do this.</p>
</div>
<div class="hdlist">
<table>
<tbody><tr>
<td class="hdlist1">
<code>[!x …]</code>
</td>
<td class="hdlist2">
<p>will only match <strong>vectors</strong></p>
</td>
</tr>
<tr>
<td class="hdlist1">
<code>(!x …)</code>
</td>
<td class="hdlist2">
<p>will only match <strong>lists</strong> and <strong>seqs</strong></p>
</td>
</tr>
<tr>
<td class="hdlist1">
<code>{:k ?v}</code>
</td>
<td class="hdlist2">
<p>will only match <strong>maps</strong></p>
</td>
</tr>
<tr>
<td class="hdlist1">
<code>#{1}</code>
</td>
<td class="hdlist2">
<p>will only match <strong>sets</strong></p>
</td>
</tr>
<tr>
<td class="hdlist1">
<code>(m/seqable !x …)</code>
</td>
<td class="hdlist2">
<p>will match <strong>maps</strong>, <strong>vectors</strong>, <strong>strings</strong>, etc…</p>
</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>I like that it will only match the same types unless I choose to be more permissive with <code>m/seqable</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_nesting">Nesting</h3>
<div class="paragraph">
<p>As you might expect, match and substitute patterns may be nested.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(m/rewrite {:xs [1 2 3 4 5]
:ys [6 7 8 9 10]}
{:xs [!xs ...]
:ys [!ys ...]}
[!xs ... . !ys ...])
;=> [1 2 3 4 5 6 7 8 9 10]</code></pre>
</div>
</div>
<div class="paragraph">
<p>And Meander does the rearranging for you.</p>
</div>
</div>
<div class="sect2">
<h3 id="_pop_quiz">Pop Quiz</h3>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(m/rewrite [:hello 1 2 3]
[?k . !v ...]
[?k !v ...])
;=> [:hello 1 :hello 2 :hello 3]</code></pre>
</div>
</div>
<div class="paragraph">
<p>Why?</p>
</div>
<div class="admonitionblock note speaker">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>When matching, we expect a single value, then repeated values.
The substituting pattern has no separator, so we are substituting pairs.</p>
</div>
</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>Congratulations!
Enjoy these macarons as your reward.</p>
</div>
<div class="imageblock">
<div class="title">Figure 2. Delicious macarons</div>
<div class="content">
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNpVtvuHsJo1HsX6A_Z7jTA2uUajCw_OsfIRsZOYqMMWAy1oPZR0B4LWZp1EOIWW7_y7HN3Vcjf4IAOQazfNW-7qxfahyZkR75E7YEmD8s3M8BZCQcMzDjvEFcA869BU6GAIRouMbfAdDcOmbj3WCDL0I7le2uv5IjyL1Ed7LZS1FjpPJVJRs/s1432/macarons.png" style="display: block; padding: 1em 0px; text-align: center;"><img border="0" data-original-height="1162" data-original-width="1432" height="519" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNpVtvuHsJo1HsX6A_Z7jTA2uUajCw_OsfIRsZOYqMMWAy1oPZR0B4LWZp1EOIWW7_y7HN3Vcjf4IAOQazfNW-7qxfahyZkR75E7YEmD8s3M8BZCQcMzDjvEFcA869BU6GAIRouMbfAdDcOmbj3WCDL0I7le2uv5IjyL1Ed7LZS1FjpPJVJRs/w640-h519/macarons.png" width="640" /></a></div></div>
</div>
<div class="paragraph">
<p>Meander has a rich feature set.
I have only shown you the core syntax.
We are now going to shift gears and look at some examples in the wild.
I’ll introduce a few bits of unexplained but fairly obvious syntax as we go.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>For a comprehensive treatment of Meander features, see <a href="https://github.com/noprompt/meander">Meander on GitHub</a> which links to documentation, a cookbook, blog posts and a Strange Loop talk.</p>
</div>
</td>
</tr>
</tbody></table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_examples">Examples</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_rearranging_logic_expressions">Rearranging logic expressions</h3>
<div class="paragraph">
<p>This example is taken from <a href="https://github.com/timothypratley/justice">Justice</a> (an alternative syntax to Datalog queries).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(def rearrange-logic
(bottom-up
;; nested commutative logic is raised
((m/pred #{'and 'or} ?op) . !before ... (?op . !clauses ...) . !after ...)
(?op . !before ... !clauses ... !after ...)
;; moves `or` to the outside,
;; and `and` to the inside to match Datalog rule convention
(and . !before ... (or . !clauses ...) . !after ...)
(or . (and . ~@!before . !clauses . ~@!after) ...)
;; identity logic expressions are flattened
((m/pred #{'and 'or} ?op) ?body)
?body
;; double negatives are removed
(not (not ?body))
?body
;; moves `not` inside to match Datalog rule convention
(not (or . !clauses ...))
(and . (not !clauses) ...)
(not (and . !clauses ...))
(or . (not !clauses) ...)))</code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="https://github.com/timothypratley/justice/blob/master/src/justice/translation.cljc">source</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_web_scraping">Web scraping</h3>
<div class="paragraph">
<p>This example comes from the <a href="https://github.com/timothypratley/chartit">Chartit</a> (an ETL/analytics utility for tracking work)</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(def extract-employees
(s/search
(m/$
[:div {:class "directory-tables"} &
(m/scan
;; heading/table pairs
[:h3 {} ?department & _]
_
[:table &
(m/scan
[:tbody &
(m/scan
[:tr &
(m/separated
[:td & (m/scan [:a {} ?name & _])]
[:td {} ?title & _]
[:td & (m/scan [:a {:href ?mailto} & _])])])])])])
;;=>
{:department (str/trim ?department)
:name ?name
:title ?title
:email (subs ?mailto 7)}))</code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="https://github.com/timothypratley/chartit/blob/master/src/chartit/justworks.clj">source</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_parsing_defn_like_forms">Parsing defn like forms</h3>
<div class="paragraph">
<p>Here is another example from <a href="https://github.com/timothypratley/justice">Justice</a>.
where it was desirable to define a macro that behaves like <code>defn</code>.
This example can be directly compared with <a href="https://blog.klipse.tech/clojure/2016/10/10/defn-args.html">the Clojure.spec approach</a>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(defn wrap-defn
"Returns a function that will parse a form according to `defn` semantics.
Takes a function which will convert fn-spec forms."
[rewrite-fn-spec]
(m/rewrite
(m/and ((pred simple-symbol? ?name) .
(pred string? !?docstring) ...
(pred map? !?attr-map) ...
!tail ...)
(m/guard (<= (count !?docstring) 1))
(m/guard (<= (count !?attr-map) 1))
(let
(or (([(m/pred simple-symbol? !params) ... :as !param-list]
. !forms ... :as !fn-specs) ..1)
([(m/pred simple-symbol? !params) ... :as !param-list]
. !forms ... :as !fn-specs))
(list* !tail))
(m/guard (apply distinct? (map count !param-list))))
;;>
(defn ?name . !?docstring ... !?attr-map ...
~@(map rewrite-fn-spec !fn-specs))))</code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="https://github.com/timothypratley/justice/blob/master/src/justice/defn.cljc">source</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_schema_to_code">Schema to code</h3>
<div class="paragraph">
<p>This example shows the core functionality of <a href="https://github.com/timothypratley/happygapi">HappyGAPI</a> (a library that exposes Google APIs by generating code from schemas).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(defn summarize-schema [schema request depth]
"Given a json-schema of type definitions,
and a request that is a $ref to one of those types,
resolves $ref(s) to a depth of 3,
discards the distracting information,
and returns a pattern for constructing the required input."
(m/rewrite request
{:type "object"
:id ?id
:additionalProperties ?ap
:properties (m/seqable [!property !item] ...)}
;;>
{& ([!property (m/app #(summarize-schema schema % depth) !item)] ...)}
{:type "array"
:items ?item}
;;>
[~(summarize-schema schema ?item depth)]
{:type (m/pred string? ?type)}
;;>
(m/app symbol ?type)
{:$ref (m/pred string? ?ref)}
;;>
~(if (> depth 2)
(symbol ?ref)
(summarize-schema schema (get schema (keyword ?ref)) (inc depth)))))</code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="https://github.com/timothypratley/happygapi/blob/master/dev/happy/beaver.clj">source</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_ast_manipulation">AST manipulation</h3>
<div class="paragraph">
<p>This example comes from <a href="https://github.com/echeran/kalai">Kalai</a>, a transpiler from Clojure to Java and Rust.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-clojure" data-lang="clojure">(def propagate-types-from-bindings-to-locals
"We propagate type information which is stored in metadata
from the the place where they are declared on a symbol
to all future usages of that symbol in scope."
(s/rewrite
{:op :local
:form ?symbol
:env {:locals {?symbol {:form ?symbol-with-meta
:init ?init}}
:as ?env}
& ?more
:as ?ast}
;;>
{:op :local
:form ~(propagate-ast-type ?init ?symbol-with-meta ?ast)
:env ?env
& ?more}
;; otherwise leave the ast as is
?else
?else))</code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="https://github.com/echeran/kalai/blob/main/src/kalai/pass/kalai/b_kalai_constructs.clj">source</a></p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_reading_patterns_vs_functions">Reading patterns vs functions</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Those were some pretty complicated transformations.<br />
<em>Could you follow them?</em><br />
I bet it was easier than most code.<br />
<em>Is that normal?</em><br />
I claim that term rewriting expressions are much easier to read than functional transformations.</p>
</div>
<div class="paragraph">
<p>Next let’s compare what is involved in writing functions vs patterns.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_writing_functions_vs_patterns">Writing functions vs patterns</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_functions_are_like_recipes">Functions are like recipes</h3>
<div class="paragraph">
<p>Functions are all about what to do with inputs.</p>
</div>
<div class="paragraph">
<p>Clearly the big difference between functions and rewrite rules is what to do with the inputs.
To write a function is to specify the tasks in order to convert the ingredients into the desired meal.
A function is a verbal description of how to transform inputs to outputs in written form.</p>
</div>
<div class="imageblock">
<div class="title">Figure 3. A recipe for making beef stew</div>
<div class="content">
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3Nki-uUg3CzzBEb0wU5gZCfhc5LpJ0MV5TQWJ1jjqFf9aJJsb7iWatItTLIgxUflhN2OKrlempyHgI91O3mYTcHZeAvRP_HIynNYP-h45Q5yzV8fXJdpsHeD6BbtmfMCRTBXIokCbZF4-_X1uA8HyYaXCRhCOlfXUlNBi5holVhTeOmFaZH0/s1306/recipe.png" style="display: block; padding: 1em 0px; text-align: center;"><img border="0" data-original-height="1010" data-original-width="1306" height="494" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3Nki-uUg3CzzBEb0wU5gZCfhc5LpJ0MV5TQWJ1jjqFf9aJJsb7iWatItTLIgxUflhN2OKrlempyHgI91O3mYTcHZeAvRP_HIynNYP-h45Q5yzV8fXJdpsHeD6BbtmfMCRTBXIokCbZF4-_X1uA8HyYaXCRhCOlfXUlNBi5holVhTeOmFaZH0/w640-h494/recipe.png" width="640" /></a></div></div>
</div>
</div>
<div class="sect2">
<h3 id="_recipe_for_a_function">Recipe for a function</h3>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 50%;"></col>
<col style="width: 50%;"></col>
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">1. Prepare meat and potatoes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><em>(example inputs)</em></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">2. Mix ingredients in a pot</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><em>(function definition)</em><br />
- A little destructuring<br />
- a dash of <code>let</code> binding<br />
- some sequence seasoning <code>for</code> flavor<br />
- a teaspoon of <code>conj</code>, <code>update</code>, and <code>assoc</code><br />
- a splash of threading</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">3. Boil</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><em>(iterate, make it work)</em></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">4. Garnish with documentation</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><em>(docstring or types)</em></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">5. Simmer</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><em>(tests)</em></p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock warning speaker">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Easy to forget why it tastes good<br />
Easy to skip documentation and testing</p>
</div>
</td>
</tr>
</tbody></table>
</div>
</div>
<div class="sect2">
<h3 id="_patterns_are_more_like_before_and_after_pictures">Patterns are more like before and after pictures</h3>
<div class="paragraph">
<p>Rewrite rules are only the inputs and outputs; the <em>what to do</em> part is completely missing.</p>
</div>
<div class="imageblock">
<div class="title">Figure 4. A hearty pot of beef stew</div>
<div class="content">
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv4MFGS69HJTBYpkAgQZMGbKjpCYG1FlSvFfYFRak-BS97b6U-KGTTjfJPIkEXd-oVPHHoO9NiT3RuGGDKfNA-kk6TwxRljQawju6YvRDrck30dYZTuj5tB-E23JIG-d88AmXNG98SXjZTh7M2edm8QJLKGF6403o7AiyFlCX8q4jzqJ_3p8U/s1512/stew.png" style="display: block; padding: 1em 0px; text-align: center;"><img border="0" data-original-height="1454" data-original-width="1512" height="615" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv4MFGS69HJTBYpkAgQZMGbKjpCYG1FlSvFfYFRak-BS97b6U-KGTTjfJPIkEXd-oVPHHoO9NiT3RuGGDKfNA-kk6TwxRljQawju6YvRDrck30dYZTuj5tB-E23JIG-d88AmXNG98SXjZTh7M2edm8QJLKGF6403o7AiyFlCX8q4jzqJ_3p8U/w640-h615/stew.png" width="640" /></a></div></div>
</div>
</div>
<div class="sect2">
<h3 id="_depicting_a_pattern">Depicting a pattern</h3>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;"></col>
<col style="width: 66.6667%;"></col>
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">1. Prepare example inputs</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>[{:k "Meat"} {:k "Potatoes"}]</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">2. Parameterize</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>[{:k !ingredient} …]</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">3. Recombine as outputs</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>[!ingredient … "Stew"]</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">4. Create a test</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>(is (= [] (stew example)))</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">5. Verify the output</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>["Meat" "Potatoes" "Stew"]</code></p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock note speaker">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Examples are the best kind of documentation.
Writing Meander feels like writing examples.
It gives me confidence I solved the right problem, and it’s easy to see what it does coming back to it later.</p>
</div>
</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>I like pictures.
I especially like diagrams.
So much so that I built an app for making diagrams:
<a href="https://hummi.app">Hummi.app</a> and
I’ve written an article about <a href="https://hummi.app/news/why-diagrams">why diagrams</a> are important.
The thing I like most about Meander is that transformation patterns depict the shape of the data that is coming in and going out.</p>
</div>
</div>
<div class="sect2">
<h3 id="_when_should_we_use_term_rewriting_when_a_function">When should we use term rewriting, when a function?</h3>
<div class="paragraph">
<p>Term rewriting is suitable for largish data reshaping where you would otherwise do destructuring, updates, restructuring, and pipelines.
We have seen some compelling examples, but not every problem is such a clear-cut case of re-shaping.</p>
</div>
<div class="openblock speaker">
<div class="content">
<div class="paragraph">
<p>Why isn’t this the default way of writing code everywhere?</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Term rewriting systems are hard to implement, and rarely integrated with programming languages.</p>
</li>
<li>
<p>Functions are more familiar in the programming community.</p>
</li>
<li>
<p>Not a silver bullet.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="paragraph">
<p>Problems that require aggregation, reduction, and variables tend to be well expressed with a loop or recursive function.
Problems that are extract and reconstitute tend to be well expressed as patterns.</p>
</div>
</div>
<div class="sect2">
<h3 id="_limitations_of_term_rewriting">Limitations of term rewriting</h3>
<div class="sect3">
<h4 id="_syntax">Syntax</h4>
<div class="paragraph">
<p>The syntax available in data literals is limited by the host language.
Meander syntax is partially extensible through <code>defsyntax</code> to define custom operators.
Execution extension is available by function application (see <code>m/app</code> and <code>~</code>).
IDEs and linting tools have trouble error checking pattern expressions because they are an entirely new set of expressions with a new grammar.</p>
</div>
</div>
<div class="sect3">
<h4 id="_less_open_to_extension">Less open to extension</h4>
<div class="paragraph">
<p>Multi-methods and Protocols allow for the extension of behavior by including a new function and associating it with the output of the dispatch function.
Multi-method and Protocol dispatch is somewhat similar to a case statement in that we expect distinct values to be matched.
Multi-methods address the possibility of multiple matches by the introduction of hierarchies and preferences: “prefer-method creates an ordering between methods when they would otherwise be ambiguous” (see <a href="https://clojure.org/reference/multimethods">Clojure - Multi-methods and Hierarchies</a>).</p>
</div>
<div class="paragraph">
<p>Pattern matching by contrast often defines many overlapping matching patterns, and is thus sensitive to ordering.
The order that patterns are defined matters because the first value match will be used.
This makes extension more challenging because extensions need to be coordinated.</p>
</div>
<div class="paragraph">
<p>One possible approach for extending patterns would be to specify them relative to existing clauses.
You could add a pattern at the end, add a pattern before an existing pattern, or add a pattern after an existing pattern.
This could get complicated if several libraries participated in an extensible dispatch.
The order of extension might change the outcome.</p>
</div>
</div>
<div class="sect3">
<h4 id="_failure_to_match_is_opaque">Failure to match is opaque</h4>
<div class="paragraph">
<p>Similar to regular expressions, it can be difficult to figure out why a match isn’t made for a given input.
Simplifying and decomposing can help.
Function application can be used <code>(m/app #(doto % prn) ?x)</code> to spy on terms.</p>
</div>
</div>
<div class="sect3">
<h4 id="_reduction">Reduction</h4>
<div class="paragraph">
<p>Meander has recursion, so you can reduce.
But recursion has no syntactic advantage.
The next branch of Meander (zeta) contains a neater way to express reduction.</p>
</div>
</div>
<div class="sect3">
<h4 id="_memory_variable_correspondence">Memory variable correspondence</h4>
<div class="paragraph">
<p>Nested memory variables in a single expression can get confusing,
and often don’t behave as you’d expect.
There is potential for simplification <a href="https://github.com/noprompt/meander/issues/129">issue#129</a>.
The next branch of Meander (zeta) has flexibility in how variables behave, which may solve this.</p>
</div>
</div>
<div class="sect3">
<h4 id="_performance">Performance</h4>
<div class="paragraph">
<p>On par with hand rolled functions.
Meander is a high quality library: fast and reliable.</p>
</div>
</div>
<div class="sect3">
<h4 id="_decomposition">Decomposition</h4>
<div class="paragraph">
<p>The current options for creating sub-patterns are <code>m/with</code> and <code>m/app</code>,
Which are equivalent to functional decomposition.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_other_approaches_to_data_transformation">Other approaches to data transformation</h3>
<div class="paragraph">
<p>Here is a short list of other data transformation approaches in Clojure so that we can examine where they differ from Meander:</p>
</div>
<div class="hdlist">
<table>
<tbody><tr>
<td class="hdlist1">
<a href="https://clojure.github.io/clojure/#clojure.core">Clojure.core</a>
</td>
<td class="hdlist2">
<p>Clojure is concise and powerful.
Many functions defined on few primary data structures.
We operate on data but do not specify what the data is.
So it is common to be looking at a function that operates on <code>x</code>, and have no context about what <code>x</code> aught to be.
What is <code>x</code>?</p>
</td>
</tr>
<tr>
<td class="hdlist1">
<a href="https://clojure.org/guides/destructuring">Destructuring</a>
</td>
<td class="hdlist2">
<p>Does one job well.
No logic expressions or substitution.</p>
</td>
</tr>
<tr>
<td class="hdlist1">
<a href="https://github.com/clojure/core.match">Core.match</a>, <a href="https://github.com/clojure/core.logic">Core.logic</a>
</td>
<td class="hdlist2">
<p>Independently work great. There is no convenient syntax for substitution.</p>
</td>
</tr>
<tr>
<td class="hdlist1">
<a href="https://clojure.org/guides/spec">Clojure.spec</a>
</td>
<td class="hdlist2">
<p>Defines the shape of <code>x</code> as s-expression regexes.
Specs do not look like the data they describe.</p>
</td>
</tr>
<tr>
<td class="hdlist1">
<a href="https://github.com/redplanetlabs/specter">Specter</a>
</td>
<td class="hdlist2">
<p>Takes a navigator/action approach.
Improves data manipulation.
Very compact, linguistic.</p>
</td>
</tr>
</tbody></table>
</div>
<div class="paragraph">
<p>Each of these approaches have benefits and drawbacks.
The purpose of this article was to show where Meander sits relative to these methods, and hopefully inspire you to explore this approach.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="openblock speaker">
<div class="content">
<div class="paragraph">
<p>The mathematical style term rewriting approach to data transformation has several merits:</p>
</div>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>When reading expressions, inputs and outputs are instantly recognizable</p>
</li>
<li>
<p>When writing expressions, examples can be converted to patterns</p>
</li>
<li>
<p>The declarative style is symmetrically pleasing</p>
</li>
<li>
<p>Visual expression is more effective than verbal expression</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Meander provides term rewriting as a convenient library for Clojure.
I hope you will give it a try and find it as useful as I have.</p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text"><br /></div>
</div>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
messageStyle: "none",
tex2jax: {
inlineMath: [["\\(", "\\)"]],
displayMath: [["\\[", "\\]"]],
ignoreClass: "nostem|nolatexmath"
},
asciimath2jax: {
delimiters: [["\\$", "\\$"]],
ignoreClass: "nostem|noasciimath"
},
TeX: { equationNumbers: { autoNumber: "none" } }
})
MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
MathJax.InputJax.AsciiMath.postfilterHooks.Add(function (data, node) {
if ((node = data.script.parentNode) && (node = node.parentNode) && node.classList.contains("stemblock")) {
data.math.root.display = "block"
}
return data
})
})
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-80859896418904571892019-01-10T07:35:00.000-08:002019-01-10T07:35:12.028-08:00Meander: The answer to map fatigue<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW8a4csOT7bvlqSjdqO43olz79chD2LNcdITsiYcWLK1hjUdfrfRnWciNtWKDyx06Vip2Xr9ajHjpfsiNspnwqkf0dn2vXvtZwKtLMz60uD6gyRHnT5EAoKi3HQcVG-_uPl4bA9A/s1600/tired-corgi.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="194" data-original-width="259" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW8a4csOT7bvlqSjdqO43olz79chD2LNcdITsiYcWLK1hjUdfrfRnWciNtWKDyx06Vip2Xr9ajHjpfsiNspnwqkf0dn2vXvtZwKtLMz60uD6gyRHnT5EAoKi3HQcVG-_uPl4bA9A/s1600/tired-corgi.jpeg" /></a></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"><br /></span></div>
<br />
Map fatigue. You may already have it. You might know somebody who does. That debilitating depression that causes otherwise normal programmers to roll their eyes and sigh. It starts out innocuous enough. I’ll just reach into this widget entity value and pull out the description I need. Oh and I’ll grab the count from the inventory entity. Hmmm what’s this argument being passed in here, I wonder if it has the prices I need? Soon things escalate. The afflicted quickly proceed to the next stage; designing in-memory databases, writing style guides, building frameworks and stoically specing their specs. The final phase manifests as full-blown depression. Incapable of writing the simplest function, the programmer sits with their hands on brow muttering nonsensical chants about the shape of data.<br />
<br />
But new research has found the answer! Well, old research really. But now there is a library available to deliver it to you. The library is called <a href="https://github.com/noprompt/meander">Meander</a> by Joel “The Falcon” Holdbrooks. It is a pattern matching and expression rewriting library. Let’s jump right into an example to show how this library can be used to treat map fatigue.<br />
<br />
The scenario: I have been assigned the task of integrating data from several widget manufacturers. I need to take data about widgets from multiple sources in various shapes and produce a normalized version of it. The end result should be normalized as manufacturer-code, widget-code, description.<br />
<br />
Here is the data format of the first manufacturer:<br />
<br />
<pre><code class="clojure">
(def skynet-widgets
[{:basic-info {:producer-code "Cyberdyne"}
:widgets [{:widget-code "Model-101"
:widget-type-code "t800"}
{:widget-code "Model-102"
:widget-type-code "t800"}
{:widget-code "Model-201"
:widget-type-code "t1000"}]
:widget-types [{:widget-type-code "t800"
:description "Resistance Infiltrator"}
{:widget-type-code "t1000"
:description "Mimetic polyalloy"}]}
{:basic-info {:producer-code "ACME"}
:widgets [{:widget-code "Dynamite"
:widget-type-code "c40"}]
:widget-types [{:widget-type-code "c40"
:description "Boom!"}]}])
</code></pre>
I’ll start off with with a stock standard hand rolled bit of code to process the data into the shape I want. Warning: Proceed with caution. High risk of map fatigue ahead.<br />
<br />
<pre><code class="clojure">
(for [{:keys [widgets widget-types basic-info]} skynet-widgets
:let [{:keys [producer-code]} basic-info
descriptions (into {} (for [{:keys [widget-type-code description]} widget-types]
[widget-type-code description]))]
{:keys [widget-code widget-type-code]} widgets
:let [description (get descriptions widget-type-code)]
:when description]
[producer-code widget-code description])
</code></pre>
Well, that’s one way to do it. You might implement it slightly differently. Maybe you prefer to use `map` instead of `for`, or transducers, or different destructuring, or no destructuring at all. One of the subtle barbs of map fatigue is indecision over implementation choices.<br />
<br />
Now here is the awesome solution using the Meander library:<br />
<br />
<pre><code class="clojure" data-external-libs="https://raw.githubusercontent.com/timothypratley/meander/cljs/src">
(require '[meander.match.alpha :as m])
(m/search skynet-widgets
(scan {:basic-info {:producer-code ?producer-code}
:widgets (scan {:widget-code ?widget-code
:widget-type-code ?widget-type-code})
:widget-types (scan {:widget-type-code ?widget-type-code
:description ?description})})
[?producer-code ?widget-code ?description])
</code></pre>
<br />
Structure! You can clearly see the shape of the data in this solution.<br />
<br />
Logic! Those unusual symbols prefixed with `?` are logic variables. The `scan` expression indicates that meander should attempt to match any items in a sequence. A logical join occurs automatically on `widget-type-code` because it appears twice in the overall expression; once in each of the sibling scan expressions. We do not need to aggregate a map of `widget-type-code` to descriptions; the relationship is implicit in the expression. This is the magic sauce of the Meander library; combining pattern matching with logic solving.<br />
<br />
Lack of choice! The declarative nature of meander expressions leaves me with no implementation choices to make. I specify the shape of my data and the shape of my desired result, and hey presto it just works! The declarative approach produces compact expressions and elides transformation code.<br />
<br />
Based on science! Joel didn’t create this library in a vacuum. He drew inspiration from existing research on expression rewriting. One of his main inspirations was Stratego -- Strategies for Program Transformation. The techniques used here are very effective for code manipulation.<br />
<br />
In Clojure, where code is data, techniques for manipulating code translate well to manipulating data. Expression rewriting is a more general problem definition of data rewriting. Meander is well suited to become a pervasive glue for describing data extraction and recombination because it goes beyond destructuring and pattern matching to providing logic solving.<br />
<br />
If you or a loved one suffers from map fatigue, take a dose of <a href="https://github.com/noprompt/meander">Meander</a>™ and suffer no more.
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
<script>
window.klipse_settings = {
selector: '.clojure'
};
</script>
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js?v=7.7.1-a"></script>
</body>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-16715345160277771862018-01-12T07:38:00.000-08:002018-01-12T08:46:00.959-08:00A Puzzle Worth SolvingEvery year school coordinators around the globe have to make a duty roster. A teachers job doesn't end at the class room, they also take turns monitoring key areas of the school at key times to keep the students safe. A duty roster is a schedule of who is responsible for each area for each time slot that requires supervision.<br />
<br />
Figure 1: A randomly selected duty roster schedule<br />
<br />
<div class="well" style="background-color: white; display: grid;">
<div style="border-bottom: 1px solid black; grid-area: 1 / 1 / auto / auto;">
<strong>Duty</strong></div>
<div style="border-bottom: 1px solid black; grid-area: 1 / 2 / auto / auto;">
<strong>Start</strong></div>
<div style="border-bottom: 1px solid black; grid-area: 1 / 3 / auto / auto;">
<strong>mon</strong></div>
<div style="border-bottom: 1px solid black; grid-area: 1 / 4 / auto / auto;">
<strong>tues</strong></div>
<div style="border-bottom: 1px solid black; grid-area: 1 / 5 / auto / auto;">
<strong>wed</strong></div>
<div style="border-bottom: 1px solid black; grid-area: 1 / 6 / auto / auto;">
<strong>thurs</strong></div>
<div style="border-bottom: 1px solid black; grid-area: 1 / 7 / auto / auto;">
<strong>fri</strong></div>
<div style="grid-area: 2 / 1 / auto / auto;">
Morning Bus</div>
<div style="grid-area: 3 / 1 / auto / auto;">
Recess</div>
<div style="grid-area: 4 / 1 / auto / auto;">
Lunch</div>
<div style="grid-area: 5 / 1 / auto / auto;">
Library</div>
<div style="grid-area: 6 / 1 / auto / auto;">
Evening Bus</div>
<div style="grid-area: 2 / 2 / auto / auto;">
08:00</div>
<div style="grid-area: 3 / 2 / auto / auto;">
11:00</div>
<div style="grid-area: 4 / 2 / auto / auto;">
13:00</div>
<div style="grid-area: 5 / 2 / auto / auto;">
13:00</div>
<div style="grid-area: 6 / 2 / auto / auto;">
15:00</div>
<div style="grid-area: 2 / 3 / auto / auto;">
Barry</div>
<div style="grid-area: 3 / 3 / auto / auto;">
Kirstin</div>
<div style="grid-area: 4 / 3 / auto / auto;">
Barry</div>
<div style="grid-area: 5 / 3 / auto / auto;">
Barry</div>
<div style="grid-area: 6 / 3 / auto / auto;">
Kim</div>
<div style="grid-area: 2 / 4 / auto / auto;">
Barry</div>
<div style="grid-area: 3 / 4 / auto / auto;">
Brian</div>
<div style="grid-area: 4 / 4 / auto / auto;">
Bob</div>
<div style="grid-area: 5 / 4 / auto / auto;">
Kirstin</div>
<div style="grid-area: 6 / 4 / auto / auto;">
Kim</div>
<div style="grid-area: 2 / 5 / auto / auto;">
Barry</div>
<div style="grid-area: 3 / 5 / auto / auto;">
Kate</div>
<div style="grid-area: 4 / 5 / auto / auto;">
Bob</div>
<div style="grid-area: 5 / 5 / auto / auto;">
Barry</div>
<div style="grid-area: 6 / 5 / auto / auto;">
Kate</div>
<div style="grid-area: 2 / 6 / auto / auto;">
Kate</div>
<div style="grid-area: 3 / 6 / auto / auto;">
Kim</div>
<div style="grid-area: 4 / 6 / auto / auto;">
Brian</div>
<div style="grid-area: 5 / 6 / auto / auto;">
Kirstin</div>
<div style="grid-area: 6 / 6 / auto / auto;">
Bob</div>
<div style="grid-area: 2 / 7 / auto / auto;">
Kate</div>
<div style="grid-area: 3 / 7 / auto / auto;">
Kirstin</div>
<div style="grid-area: 4 / 7 / auto / auto;">
Bob</div>
<div style="grid-area: 5 / 7 / auto / auto;">
Brian</div>
<div style="grid-area: 6 / 7 / auto / auto;">
Kirstin</div>
</div>
<div>
<span style="color: #222222; font-family: "calibri" , sans-serif;"><span style="font-size: 14.6667px;"><br /></span></span></div>
<br />
A randomly generated schedule like the one presented in figure 1 does not suffice. There are some obvious rules that need to be followed such as not giving too many duties to one particular person. Lets consider some simple rules to apply to a schedule.<br />
<div>
<br />
<ul>
<li>Nobody should do more than one duty per day.</li>
<li>Nobody should do more than four duties per week.</li>
<li>Support staff do Morning Bus Duty and Library Duty.</li>
</ul>
<div>
<br /></div>
We might choose to represent the data for this problem like this:<br />
<div>
<br />
<ul style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 12.8px; margin-bottom: 0cm; margin-top: 0cm;" type="disc">
</ul>
<div>
<span style="color: #222222; font-family: "calibri" , sans-serif;"><span style="font-size: 14.6667px;"><br /></span></span></div>
<pre style="background-color: white;"><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:duties </span><span style="font-family: "fira code";">{</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Morning Bus" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:start-time </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"08:00"</span><span style="font-family: "fira code";">}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Recess" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:start-time </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"11:00"</span><span style="font-family: "fira code";">}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Lunch" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:start-time </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"13:00"</span><span style="font-family: "fira code";">}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Library" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:start-time </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"13:00"</span><span style="font-family: "fira code";">}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Evening Bus" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:start-time </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"15:00"</span><span style="font-family: "fira code";">}}
</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:people </span><span style="font-family: "fira code";">{</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Kate" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:role </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"coordinator"</span><span style="font-family: "fira code";">}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Kim" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:role </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"teacher"</span><span style="font-family: "fira code";">}
</span><span style="color: green; font-family: "fira code";"><b>"</b><b>Kirsten</b><b>" </b></span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:role </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"teacher"</span><span style="font-family: "fira code";">}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Barry" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:role </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"teacher"</span><span style="font-family: "fira code";">}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Brian" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:role </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"support"</span><span style="font-family: "fira code";">}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Bob" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:role </span><span style="color: green; font-family: "fira code"; font-weight: bold;">"support"</span><span style="font-family: "fira code";">}}
</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:roles </span><span style="font-family: "fira code";">{</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Support" </span><span style="font-family: "fira code";">{}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Teacher" </span><span style="font-family: "fira code";">{}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Coordinator" </span><span style="font-family: "fira code";">{}}
</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:rules </span><span style="font-family: "fira code";">{</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Match duty role" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:rule </span><span style="font-family: "fira code";">[</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:in </span><span style="font-family: "fira code";">[</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:people :role</span><span style="font-family: "fira code";">] [</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:duties :roles</span><span style="font-family: "fira code";">]]}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Duties per day" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:rule </span><span style="font-family: "fira code";">[</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:<= </span><span style="font-family: "fira code";">[</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:match :people :duties :day-of-week</span><span style="font-family: "fira code";">] </span><span style="color: blue; font-family: "fira code";">1</span><span style="font-family: "fira code";">]}
</span><span style="color: green; font-family: "fira code"; font-weight: bold;">"Total duties" </span><span style="font-family: "fira code";">{</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:rule </span><span style="font-family: "fira code";">[</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:<= </span><span style="font-family: "fira code";">[</span><span style="color: #660e7a; font-family: "fira code"; font-style: italic;">:match :people :duties</span><span style="font-family: "fira code";">] </span><span style="color: blue; font-family: "fira code";">4</span><span style="font-family: "fira code";">]}}}</span></pre>
<br />
<br />
So... how do we solve it?<br />
<br />
I have built a UI for entering the data but I'm stuck on building the solver.<br />
I have not found a constraint solver capable of solving this problem that runs in the browser.<br />
Running a back-end is undesirable as this is a free, open source project with no budget.<br />
<br />
I'm looking for ideas, references, examples, suggestions, collaborators or a mentor.<br />
My preferences run to ClojureScript and JavaScript.</div>
</div>
<div>
<br /></div>
<div>
Can you help? There is a comments forum on this blog, or feel free to link to your favorite social site or email me directly timothypratley@gmail.com for discussion.</div>
<div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSPhmr3KPHJc_yfzo5LxhYmKK0B2_L4Ckz4fE8TIwckPYX4ITbk6TV__j8KUL5xdSGy5wwQ4iJQNBfsGla1YHBd14P78XuNQP6af6FtGsdmYbqahWBREd1z0fwOLB-QGrRaRjHoA/s1600/puzzle.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1000" data-original-width="1000" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSPhmr3KPHJc_yfzo5LxhYmKK0B2_L4Ckz4fE8TIwckPYX4ITbk6TV__j8KUL5xdSGy5wwQ4iJQNBfsGla1YHBd14P78XuNQP6af6FtGsdmYbqahWBREd1z0fwOLB-QGrRaRjHoA/s320/puzzle.jpg" width="320" /></a></div>
<br /></div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-40348047720226194722017-03-23T23:00:00.000-07:002018-03-16T08:11:55.273-07:00Ryzen is for ProgrammersWhen I heard about AMD’s Ryzen series CPU launch I immediately wondered how much performance I could get out of one. This article relates my experience building a PC to replace my much loved but aging 2013 Macbook Pro with a Ryzen powerhouse. First I’ll share the reasons why I wanted to build my own PC. Then I’ll run through the build process. At the end are my programmer oriented task timings comparing my new machine with my laptop.<br />
<div>
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5sCih7rmp4Cp41yt8yUWiQvdhvg9gGEP9LmbO_X1mso8I6426B0Mdxw4Mf_E905PQQQr8IG2s02nOXRcdAi-TjTq1gBO_CgOY1_gCvYjJuuhyphenhyphenGe66uPRWk06nl_-Fi6GhXOsEUw/s1600/gandalf2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5sCih7rmp4Cp41yt8yUWiQvdhvg9gGEP9LmbO_X1mso8I6426B0Mdxw4Mf_E905PQQQr8IG2s02nOXRcdAi-TjTq1gBO_CgOY1_gCvYjJuuhyphenhyphenGe66uPRWk06nl_-Fi6GhXOsEUw/s320/gandalf2.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h2>
Why build a PC?</h2>
Tim, do you *really* need a faster computer? Aren’t you just editing text files?<br />
<br />
Ha ha ha yes. Ultimately I am just editing text files, and could easily do that on my favorite PC of all time, my university days 386. But those little text files drive a system, and that system is a hungry bear. My work involves back-end development, web interface development, browser testing, and video conferencing. So let’s take a look at what a day in my development life looks like, and why compute power matters to me.<br />
<br />
<h3>
Back-end service development:</h3>
Our development environment consists of 12 docker containers in a VirtualBox host. The containers provide a database, the core logic service, an event pipeline, data service, and Nginx routing. All of them except for the database and Nginx proxy are Clojure services that use between 500 MB and 8 GB of memory in production. In development they squeeze into 8 GB of memory. On my Macbook Pro I cannot fit two customer specific environments into the 16 GB of memory available. An attempt to run them side by side results in so much thrashing that the only way to regain control is to hard reboot. It’s a fairly heavy development environment, I’ll be the first to agree, but not unusually so. The biggest advantage of running a full stack is that it is pretty close to how the software gets deployed to production, so I can do end to end testing.<br />
<br />
<h3>
Web interface development:</h3>
Our User Interface (UI) is a Reagent/ClojureScript app. As you might have guessed already from previous posts I really like Reagent/ClojureScript. Using the same language, tools, and mental models on the front end and back end make it easy to work features that traverse the whole system. Our UI has grown to be about 7k lines of code. Still a small/medium sized code base by most measures, but in the realm that performance while developing is becoming a drag. ClojureScript has slow initial compile times. As your project gets large you end up with many dependencies and this leads to hundreds of files being loaded in development mode. And if you want to test the advanced mode compilation output, you need to be patient for a full build on every change (so don’t make too many mistakes with that!). Also the page itself can end up doing more work than I’d like. In order to be able to tune it, I need to be able to run and profile the slow code, then do it all again.<br />
<br />
<h3>
Browser testing:</h3>
I run virtual machines for IE8, 9, 10, 11, and Edge. On my Macbook Pro they crawl.<br />
<div>
<br />
<h3>
Video conferencing:</h3>
Our development team consists entirely of promiscuous pair programming practitioners. That means we all spend all day with video chat on and sharing our screens. Tmate is the least resource intensive way for us to share our terminals, and often prefered for this reason. Typing responsiveness is excellent, so bouncing between driver and observer is easy. Sometimes Tmate isn’t enough. Sometimes we need to share a browser screen. I like using Cursive (a Clojure IDE), which doesn’t work over Tmate, so I share that too. We use screen sharing regularly. Google Hangouts was way too resource intensive for doing work while conferencing. Instead we use zoom.us, which is a native video chat application. Zoom is much less resource intensive, but still uses at least 15% of my Macbook Pro when pairing. When I screen share that goes up to 30% and starts to affect the responsiveness of my system. Sometimes we stop sharing and mute video while a process is starting up. Pair programming is fantastic, but it can be hampered when my system is under heavy load.<br />
<br />
<h3>
Tim, why didn’t you buy a 2017 Macbook Pro?</h3>
Macs are nice. They work out of the box. The look fantastic. They are reliable. They are best of class. They have great software. Homebrew works pretty well for development dependencies. However the latest Macbook Pro is not a big leap forward in processing power. I do think the latest Macbook is great… But according to the pre-launch benchmarks, the Ryzen CPU was ahead on horsepower. When I do travel, my trusty 2013 Macbook Pro is perfectly fine. So I am willing to go with a desktop solution if it provides a performance improvement.<br />
<br />
<h3>
But why build when you can buy an out of the box PC or iMac?</h3>
I want the fastest hardware at a reasonable price. Pre-built high end systems are expensive, and are often optimized for use cases that aren’t quite what I have in mind. I can do better. Building a PC is easy. Modular components make up a computer. You just order them and put them together inside a case. All you need is a screwdriver. Components only fit in their assigned spot, and there are plenty of instructions accompanying the components and on the web to guide you through the process. The end result is a low cost, high spec custom built desktop. Also, I must admit to harboring a little hubris; to me building my PC is akin to building my own light-saber.<br />
<br />
<h2>
The programmer’s guide to a DIY PC build:</h2>
<div>
<br />
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoaFJfot3e7GJjgNb_5LYsLl6EoMja113IlG-bLceDLmav6dS_5Th1_rmBVAAZu2njVYN0dI_VNDMdQKz3oglNRQMDH9tEoBVij5h00L12PMoZTCTqe0YTZpXkhHtiu8-tkYnLcQ/s1600/building.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoaFJfot3e7GJjgNb_5LYsLl6EoMja113IlG-bLceDLmav6dS_5Th1_rmBVAAZu2njVYN0dI_VNDMdQKz3oglNRQMDH9tEoBVij5h00L12PMoZTCTqe0YTZpXkhHtiu8-tkYnLcQ/s320/building.jpg" width="320" /></a></div>
<div>
<br /></div>
<br />
<ol>
<li>Set a budget</li>
<ul>
<li>Performance goal: I’m aiming for double the power of my 2013 model Macbook Pro</li>
<li>Cost of a boxed solution: A fully loaded 2017 Macbook Pro retails for $3500. A premium desktop PC retails between $2000-$3000</li>
<li>Value judgement: I can build a *better* than premium desktop for $2000. Everything will be customized exactly how I want it</li>
</ul>
<li>Allocate the budget between components</li>
<ul>
<li>CPU, Memory, Storage, Motherboard/Power Supply Unit/Case, Peripherals.</li>
<li>An equal distribution between these categories is a good starting point. About $400 in each of the categories listed above. Most premium PCs are gaming systems, and are dominated by a hefty graphics card. For programming I am focused on CPU/Memory/Storage.</li>
</ul>
<li>Select a draft build. Use <a href="https://www.pcpartpicker.com/">PCPartPicker</a> to spec out the system. This tool will check the compatibility of parts, highlight any missing parts, and compare prices from multiple vendors.</li>
<ul>
<li>It helps to have a CPU in mind to narrow down the options.</li>
<li>Sorting parts by price and choosing something appealing in the price range allocated in the component budget is a good starting point for the draft.</li>
<li>Visiting a premium PC builder site such as <a href="https://ibuypower.com/">iBuyPower</a> can provide some ideas. Keep in mind that they have a different pricing model and cater to gaming, so no need to follow them too closely.</li>
</ul>
<li>Research benchmarks and reviews for each component from your draft. Find candidate components to swap around. Lock in on 1 to 3 products per component that are excellent value propositions in the desired budget range.</li>
<ul>
<li><a href="https://www.cpubenchmark.net/high_end_cpus.html">High end CPU benchmarks</a></li>
<li><a href="https://www.cpubenchmark.net/high_end_cpus.html#cpuvalue">High end CPU value</a></li>
<li>YouTube: <a href="https://www.youtube.com/user/paulshardware">Paul's hardware</a>, <a href="https://www.youtube.com/user/AwesomeSauceNews">Bitwit</a>, <a href="https://www.youtube.com/user/LinusTechTips">Linus tech tips</a>, <a href="https://www.youtube.com/channel/UCCss3QxegBkF8BAetIo0qXA">Tech deals</a>.</li>
</ul>
<li>Prepare to purchase.</li>
<ul>
<li>Are all the selected components compatible? Read the product specifications carefully.</li>
<li>Verify your expected power consumption.</li>
<li>Does your case fit you components?</li>
<li>Do you have a rough idea of how everything will fit together?</li>
<li>Are any of the components over spec or under spec?</li>
</ul>
<li>Buy. Don’t pay full price!</li>
<ul>
<li><a href="https://pcpartpicker.com/">Pcpartpicker</a> compares prices from several vendors, but is not comprehensive or up to date. If you search around you will definitely find even better deals. I recommend doing a Google search by part number. Then a <a href="https://slickdeals.net/deals/computers/">Slickdeals</a> search. Then visit Newegg, Bestbuy, and Amazon directly and search for the product.</li>
<li>30% off list price is a good deal on components and peripherals. 10% off is not great, you might want to keep searching… or look at a different product in the same performance band that has a better deal. Compare the bottom line cost, because some seller “discounts” aren’t.</li>
<li>Buy parts individually as you find a good deal. These days free shipping is never a problem, so there is rarely any advantage to consolidating an order. Don’t try to fill your cart up with everything you need because:</li>
<ul>
<li>It is complicated to figure out an optimal set of purchases across vendors that contains all your parts.</li>
<li>It is simpler to focus on making one good purchase at a time.</li>
<li>Missing a good deal can delay the build while looking for an alternative.</li>
<li>There are enough vendors and products that you can be confident of filling your full system order, and you already have a baseline from <a href="https://pcpartpicker.com/">PCPartPicker</a> on the worst case scenario.</li>
</ul>
<li>Consider joining Ebates. It is very easy to sign up for. You receive a 1-5% cashback on purchases sent as a check to you in the mail. If you join as a referral from the green button here you will also get a $10 bonus after your first purchase (and I’ll get a $5 bonus!)<br /><a href="https://www.ebates.com/r/TEOHSN?eeid=28585" rel="nofollow" target="_blank"><img alt="Ebates Coupons and Cash Back" border="0" height="31" src="https://www.ebates.com/images/referral/2017/ebates-referral-button.png" width="171" /></a><br />All the major component suppliers have Ebates cash back, so you will definitely end up with an extra $30-60 in your pocket. And yes, you will still earn points or cashback from your credit card separately.</li>
</ul>
<li>Assembly.</li>
<ul>
<li>Start with the case, it will have a build manual which will walk through the important steps such as attaching your CPU and RAM to the motherboard before putting it in the case. As you unbox each component it will have a manual. Read the manual. Most manuals are short, pictorial, and cryptic. However they contain essential information that will make the build go smoothly.</li>
<li>Take your time. Components fit together easily and neatly by following the diagrams.</li>
<li>There are heaps and heaps of build guides and in depth analysis of all sorts of topics like thermal paste application, fan configuration, and every possible consideration you might have on the web. I chose the “pea” method for thermal paste, and front mounted my radiator based on advice from enthusiasts on YouTube. You can read more about thermal paste application <a href="https://thegreatsetup.com/how-to/correctly-apply-thermal-paste-cpu-gpu/">here</a>.</li>
</ul>
</ol>
<div>
<br /></div>
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/XcoKOulCqks" width="560"></iframe><br />
<br />
<br />
<h2>
My component purchases:</h2>
<br />
<h3>
Case $80 Fractal Design Define R5</h3>
I love this case. Roomy! Removing all the drive bays was easy and freed up even more space. Who uses 5.25” bays? There is a spot for SSDs behind the motherboard. Cables are nicely hidden away behind the motherboard backing plate and tied neatly with velcro straps. Thumbscrews appear pretty much everywhere which makes building much less fiddly than regular screws. Two good fans are included. Top mounted power and USB ports are easy to access over a monitor. The build instructions were clear and well presented. The top holes for CPU power are too small to pass the CPU power cable head through while the rubber grommet is in place, so I needed to kink the grommet temporarily to pass it through. Replacing the top removable panels takes a bit of care; one end is a lock while the other is a latch. The case has removable dust filters. I did not connect the front panel fan controller (what benefit is there to manually changing fan speed?) Instead I connected my fans to motherboard pins. I had no use for the second included fan because of the large cooling unit I installed.<br />
<br />
<h3>
Power supply $80 EVGA SuperNOVA 550 G3, 80+ GOLD, 550W</h3>
I loved unboxing and installing this PSU. It looks fantastic, is small, fully modular and easy to install. Cabling was easy, neat and fast.<br />
<br />
<h3>
CPU $500 Ryzen 1800X</h3>
The Ryzen proposition is that you get equal performance to the best consumer grade Intel CPU at half the price. I bought it at launch which was a bit of a risk, but that price is very attractive for the best CPU performance available. This value proposition is the catalyst for the build. So yeah, gonna go for the big one here. Now that the launch is over, it seems that the 1700 and 1700X are definitely better value. I am happy with my 1800X, it might not have been as cost efficient, but it is the fastest! And really that’s my whole goal here, to have a dramatically faster system than my Macbook Pro.<br />
<br />
<h3>
Cooling $100 Cooler Master MasterLiquid Pro 280</h3>
Urrrgh! I have angst about this component. I bought this cooler believing that it would support AM4 (the Ryzen socket) with a bracket. But the bracket is not available and will not be until April at the earliest. So how exactly am I running my system? The cooler is jerry rigged on with wire and a cable tie. The good news is that it’s running nice and cool between 30-40 Celsius. But really, is this a good idea? No. The sad thing is that all I need is a little plate of metal with the right dimensions to allow me to screw the cooler head securely to the motherboard. Searching for a cooler (air or water) was a real let down. It was very confusing as to which products have brackets now as opposed to promising them in the future. There was only a very limited selection of AM4 CPU cooling options at launch. When I was able to find a product (water or air), it was out of stock or overpriced. The MasterLiquid Pro 280 specifically has some issues. The pipes are quite rigid, which makes it difficult to position. The pipes are also only just long enough. These two factors cause torque on the CPU attachment. This made it very challenging to jerry rig because the CPU attachment had a tendency to twist and slide away from the correct position. It also produces more noise that I would like even with the fans configured to “silent” setting in BIOS. On the other hand my CPU never goes above 40 Celsius under medium load with the radiator fans running at 20%. Temperatures of up to 80 Celsius are considered normal for operation. This cooler is working very effectively! There is something cool about water. The installment looks awesome in the box. I'm happy with the component now that it is installed. If you don't like the water cooling options, then go with a heat-sink and fan air cooler instead. Temperature only makes a difference for overclocking, and air coolers are very good. Air is cheaper, easier to install, quieter and plenty cool enough. Whichever you choose, check the AM4 compatibility carefully. I hope <a href="http://www.calyos-tm.com/calyos-fanless-pc-workstation/">fanless</a> cooling systems become available.</div>
<div>
<br />
<h3>
Motherboard $100 Asus PRIME B350-PLUS ATX AM4</h3>
B350 is not the premium chipset (X370), but the only difference is the lack of SLI. SLI is only necessary if you want to run dual ultra graphics cards. I do not. You will need to pick up a dedicated video card (the motherboard has a HDMI port, but there are no integrated video CPUs available in AM4 yet). The Asus PRIME was the only motherboard I could find available for purchase aside from the more expensive Asus X370 gaming offering. Supposedly there should be a selection of motherboard options, but to actually buy one the choices at launch were limited. Fortunately the Asus PRIME is a *really* nice board at an inexpensive price point. By default the BIOS fan settings are at full power. I changed the chassis fans from DC to PWR mode silent, and the CPU to silent. But it isn’t silent. There is still audible fan noise. The board looks great, is well built, and has excellent specifications. The only minor annoyance I had was that it is actually slightly smaller than the full ATX form factor, which means that it has a few less riser connections to the case, so the right side of the board remains a tad flexible.<br />
<br />
<h3>
Memory $185 G.Skill TridentZ Series 32GB (2 x 16GB) DDR4-3200</h3>
Choosing memory sounds easy. You need a good amount of any brand named RAM with reasonable specs. Unfortunately the RAM market is extremely variable for such a standard component. For example the exact same RAM I bought for $185 currently retails at $325. You have to shop around between many different manufacturers and ratings to find a good price. After I got my system setup and Ubuntu installed I went into the BIOS and used DCOSP to detect my RAM and rebooted for 3200… and nothing. It is a sickening feeling when your wiz-bang computer will not even boot to BIOS anymore. It made me queasy resetting the BIOS. I popped out the CMOS battery and shorted a special pair of pins with a screwdriver as per the motherboard manual. After replacing the battery, the machine booted, almost! It did about 5 false starts before it finally loaded back into BIOS. Just because I have 3200 memory doesn’t mean I can use it at that speed. No big deal, I only got it because it was cheap from a reliable brand. The default/stable DDR4 speed for my motherboard is 2333, so I do not need anything rated higher. There is a <a href="https://docs.google.com/spreadsheets/d/1hXCogI15jJpz_0DBigyrufsU2mvsfzanS86DVPwmJ-s">spreadsheet</a> that is useful for comparing various speed and latency ratings on RAM.<br />
<br />
<h3>
Storage $480 Samsung 960 Evo 1TB M.2-2280 Solid State Drive</h3>
This was an expensive component. The performance specs are far out in front of “normal” SSDs, and leave physical HDDs for dead. Given it outperforms “normal” SSDs by such a large margin, I was willing to pay the price. As a programmer I host databases, process large files, and load virtual machines from disk regularly. I anticipate fast storage having a significant impact on overall system performance for my usage. 1TB is possibly more than I need. My Macbook Pro has 512GB and I’ve only had to clean out files once. It is nice to have double the capacity, but the read/write performance is what I’m primarily after here. An attractive alternative is to get a smaller 256GB M.2 drive, and a large “normal” SSD. It would be much cheaper. I went with the 1TB seeing as there is only one M.2 drive slot, and I’d rather not have to think too much about where files belong. Installing the M.2 form factor was super easy, just one screw in. Gosh they are small!<br />
<br />
<h3>
<div class="separator" style="clear: both; font-size: medium; font-weight: normal; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgThMqfGV8_Mx-WnahEdwK6lKqJVXW1xy-gAowrOtRwq9f4kGtUkxgJ2aU2YtXkWi3Uv81vRt8EYj3R2BA62iem75iOR3tCcyWatg8JYur4PGaKXdMV5eXvWYpFKj9Me4-5f-4y3A/s1600/IMG_3396.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br class="Apple-interchange-newline" /><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgThMqfGV8_Mx-WnahEdwK6lKqJVXW1xy-gAowrOtRwq9f4kGtUkxgJ2aU2YtXkWi3Uv81vRt8EYj3R2BA62iem75iOR3tCcyWatg8JYur4PGaKXdMV5eXvWYpFKj9Me4-5f-4y3A/s320/IMG_3396.JPG" width="240" /></a></div>
<div class="separator" style="clear: both; font-size: medium; font-weight: normal; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; font-size: medium; font-weight: normal; text-align: center;">
<br /></div>
</h3>
<h3>
Total cost</h3>
All up these components cost $1525 excluding peripherals. Considering that these are all premium components with higher specs than you can even select for a pre-built system, that’s a screaming deal! It is also worth noting that choosing a “normal” SSD and going with the slightly slower Ryzen 1700 with included wraith spire instead of water cooling you can still build a pretty fantastic system for under $1000.<br />
<br />
<h2>
Performance comparison</h2>
I now have a desktop PC that is 2X better in every dimension than my Macbook Pro, on paper. But does it help me with my day to day activities? I fired up Slack and Chrome on both systems (as I always have these open) and got my stopwatch ready. All times quoted herein are wall-clock times formatted as minutes:seconds.</div>
<div>
<br /></div>
<div>
<h3>
Paper match up:</h3>
Macbook Pro (2013 model):<br />
2.3 GHz Intel Core i7 (4 cores),<br />
16 GB 1600 MHz DDR3,<br />
512G SSD.<br />
<br />
Ryzen PC (2017 custom build):<br />
3.6GHz Ryzen 1800X (8 cores),<br />
32 GB 2333 MHz DDR4,<br />
1TB NVMe SSD.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4iMjKKUOC5v_ehIPx_J942WrsaoTOzqbixBr8QPnT3O-Ay6GlQOdwLsO33gjDEqvXAIFzR5n3fIkkaOpsTP1MVKiu5vU8WBKEh_L0Rr03kQ0YfKHYkAtDBMDhse_IZqTQtpGxqg/s1600/lightsaber-625x350.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4iMjKKUOC5v_ehIPx_J942WrsaoTOzqbixBr8QPnT3O-Ay6GlQOdwLsO33gjDEqvXAIFzR5n3fIkkaOpsTP1MVKiu5vU8WBKEh_L0Rr03kQ0YfKHYkAtDBMDhse_IZqTQtpGxqg/s320/lightsaber-625x350.jpg" width="320" /></a></div>
<br />
<br />
<h3>
Docker-compose up:</h3>
This is the command I use to fire up my development environment, when I switch customers, and when I make a system configuration change. It starts the 12 docker containers in an already running host.<br />
<br />
Ryzen: 1:30 1:28 with zoom share 1:41 1:41<br />
MBP: 2:45 2:38 with zoom share 2:59 2:54<br />
323/178 == <b>1.8</b> with zoom share 353/202 == <b>1.7</b><br />
<br />
Just short of 2X.<br />
<br />
How about docker-compose up two environments simultaneously?<br />
<br />
Ryzen: with zoom share 2:30 2:29<br />
<b>MBP: Cannot do</b><br />
<br />
I can fire up two customer specific environments simultaneously in 30 seconds less than it takes to fire up one customer specific environment on my Macbook Pro! I think this result due to the extra cores in my Ryzen. The Macbook Pro has 4 cores, while the Ryzen has 8 cores. For this particular scenario I’m getting <b>more than 2X</b> performance, but it is not a direct comparison because the Macbook Pro cannot fit both environments in memory at the same time. This test highlights that the extra capacity provides a capability that was missing before (running two customer specific environments simultaneously).<br />
<br />
<h3>
Make up:</h3>
This command sets up an environment from scratch. It builds containers. It compiles ClojureScript, migrates database schemas, and does a docker up.<br />
<br />
Ryzen: 4:30 4:38 with zoom sharing: 4:59 5:14<br />
MBP: 6:24 6:12 with zoom sharing: 7:12 7:18<br />
756/548 == <b>1.38</b> with zoom sharing: 870/613 == <b>1.42</b><br />
<br />
Pretty handy, but not the the full 2X I was hoping for. Maybe `make up` is bottle-necking in other departments. I did a little digging and vagrant up takes about the same time on both systems, accounting for 30 seconds. So there is an example of something that was not improved at all. Perhaps I’m bottle-necking on network for some tasks?<br />
<br />
During these tests my Macbook Pro goes bonkers. Full fans, hot to touch. The Ryzen? Fans at the same low hum, CPU sitting cool at 40 celsius. But the most noticeable difference is that when I did subsequent runs I also did some multi-tasking. <b>The Ryzen remains fully responsive and completely usable.</b> You wouldn’t even know that it had anything else to do. The Macbook Pro has some serious lag going on.<br />
<br />
I had expectations that my PC would be more responsive. The Ryzen greatly exceeded those expectations. The whole experience of using my Ryzen PC is a big leap from using my Macbook Pro. On the Ryzen everything is noticeably snappier. Having the extra capacity available is great.<br />
<br />
<h3>
Compiling ui.js</h3>
I was hoping that ClojureScript compile times would be dramatically better with fast disk and more CPU. To my surprise it wasn’t as much as I anticipated. Perhaps the compiles are dominated by single thread CPU performance?<br />
<br />
Ryzen: 0:18 0:19 0:18 with zoom sharing 0:20 0:20<br />
MBP: 0:24 0:24 0:24 with zoom sharing: 0:27 0:27<br />
72/55 == <b>1.3</b> with zoom sharing 54/40 == <b>1.4</b><br />
<br />
<h3>
Profiling a web page:</h3>
<div>
Ryzen: Scripting 1109ms</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAv3E7-WcZPePZeahfWUxZoGZHqEQ-xXBQ3_EwiY181U_GIADWaBWSWDOqqOc2dljcMD7RBgzwQTbBvBOkE34-5TWUAC4FgTV6-urpmJLT6N5e_0OKxiC0iEjtZFA5zQzG9q0ptA/s1600/ryzen_profiling.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="372" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAv3E7-WcZPePZeahfWUxZoGZHqEQ-xXBQ3_EwiY181U_GIADWaBWSWDOqqOc2dljcMD7RBgzwQTbBvBOkE34-5TWUAC4FgTV6-urpmJLT6N5e_0OKxiC0iEjtZFA5zQzG9q0ptA/s640/ryzen_profiling.png" width="640" /></a></div>
<br />
<insert images=""><br /></insert>
<insert images="">MBP: Scripting 1681ms</insert><br />
<insert images=""><br /></insert>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdhYHc67F7Zgj-8YdkhtK3hLIg_3E_BtPPFVmBYmlmlZVx8SLCPPlLIdgyL4oHajXIzWWAlLbkdIaAQNOAFzqM_r3LaBBWubmQrOBBo0jaOJjxsA4mjH1fQg0U4hBcFGlIbQn69Q/s1600/mbp_profiling.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="398" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdhYHc67F7Zgj-8YdkhtK3hLIg_3E_BtPPFVmBYmlmlZVx8SLCPPlLIdgyL4oHajXIzWWAlLbkdIaAQNOAFzqM_r3LaBBWubmQrOBBo0jaOJjxsA4mjH1fQg0U4hBcFGlIbQn69Q/s640/mbp_profiling.png" width="640" /></a></div>
<insert images=""><br />1681/1109 == <b>1.5</b></insert><br />
<insert images=""><br /></insert>
To be able to tune and improve web pages, it helps to be able to profile them quickly. The Ryzen gets a 1.5X advantage. This page is a pretty standard public facing high traffic site. I wasn't expecting the extra compute power to have such a dramatic impact on normal browsing times. But so much of the web is JavaScript driven these days that it actually makes a big difference. So for profiling, bench-marking, and general web browsing there is a significant advantage here.<br />
<insert images=""><br /></insert>
<br />
<h3>
Loading an IDE (Cursive/IntelliJ)</h3>
<div>
I use VIM to edit files here and there because it opens instantly, and jump into Cursive for longer coding sessions.</div>
<div>
<br /></div>
Ryzen: 0:9 0:9 0:9 with zoom sharing: 0:12 0:11<br />
MBP: 0:11 0:12 0:12 with zoom sharing: 0:14 0:15<br />
35/27 == <b>1.3</b> with zoom sharing: 29/23 == <b>1.3</b></div>
<div>
<br />
<h3>
Starting figwheel:</h3>
<div>
For interactive web development I first need to fire up `lein figwheel`.</div>
<div>
<br /></div>
Ryzen with zoom sharing: 0:12 0:11 0:11<br />
MBP with zoom sharing: 0:16 0:15 0:15<br />
46/34 == <b>1.4</b><br />
<br />
Less waiting, more coding.<br />
<br />
<h3>
Processing a large text file:</h3>
<div>
I took a 9GB file containing 300k events and used wc (word count) and ag (silver searcher, like grep):</div>
<div>
<br /></div>
<insert example="">wc enriched_300k.tsv</insert><br />
<insert example="">Ryzen: 0:54 0:54 0:54</insert><br />
<insert example="">MBP: 0:24 0:24 0:24</insert><br />
72/162 == <b>0.4</b><br />
<insert example=""><br /></insert>
<insert example="">wc -l enriched_300k.tsv</insert><br />
<insert example="">Ryzen: 0:01 0:01 0:01</insert><br />
<insert example="">MBP: 0:09 0:09 0:09</insert><br />
<insert example="">27/3 == <b>9</b></insert><br />
<insert example=""><br /></insert>
ag foo enriched_300k.tsv<br />
Ryzen: 0:09 0:09 0:09<br />
MBP: <b>Cannot do</b> (<span style="background-color: white; color: #222222; font-family: "arial" , sans-serif; font-size: x-small;">ERR: expected to read 2445307116 bytes but read 4294967295)</span><br />
<br />
grep foo enriched_300k.tsv<br />
Ryzen: 0:01 0:01 0:01<br />
MBP: 1:45 1:46 1:44<br />
315/3 == <b>105</b><br />
<br />
These results are all over the map. I don't understand why wc full is faster on the Macbook Pro. Or why grep is ridiculously faster on the Ryzen. Perhaps the implementations differ between OSX and Ubuntu?<br />
<br />
<h3>
Starting an IE9 Virtual Machine (from saved state):</h3>
Ryzen: 2s 2s 2s<br />
MBP: 10s 9s 9s<br />
28/6 == <b>4.7</b> ?!??!!<br />
<br />
Using an IE9 Virtual Machine is very smooth on the Ryzen, it feels like a native browser. I can run the developer tools and profile the page comfortably. On the MBP, the virtual browser is painfully sluggish. I don’t see why the Ryzen does so much better here. Probably there is some VM specific factor at play.<br />
<br />
<h2>
Switching to Linux</h2>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGx8C2hvXWXF_0UIbEb5q5NjAdJgBUbLC0Iep71-Xc8lKLHMJ6c-4qrgT28JqFx5JNLYeuO8w2DmUkTdSFt2zlmBZndco6R5zlj71VJ-H7EN1mDODUNoSDvn7PWjefDgeMYGvvFA/s1600/madagascar-the-penguins-madagascar-penguins--1440x900.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGx8C2hvXWXF_0UIbEb5q5NjAdJgBUbLC0Iep71-Xc8lKLHMJ6c-4qrgT28JqFx5JNLYeuO8w2DmUkTdSFt2zlmBZndco6R5zlj71VJ-H7EN1mDODUNoSDvn7PWjefDgeMYGvvFA/s320/madagascar-the-penguins-madagascar-penguins--1440x900.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
I installed Ubuntu. It was really easy to install. I made a bootable USB jump drive on my Macbook Pro, put it in the PC, bam, installed! Amazingly fast. Everything worked out of the box. No driver problems. Great!<br />
<br />
I like the Ubuntu interface. Window management is better than mac out of the box. Unity has hotkey and drag dock left right maximize and corners. Positioning windows is a breeze. PrintScreen takes a screenshot of the desktop, and Alt+PrintScreen captures the current application. It brings up a preview so you can edit/rename the image.<br />
<br />
The jarring difference from OSX is the slightly different use of common keyboard shortcuts. Copy + paste in the terminal require either shift+ins or ctrl+shift+c/v. I find this slows me down because I have to think about whether I’m in a terminal or not, and use different keys. Ctrl-a Ctrl-e don’t go to start of line or end of line in Ubuntu apps, so I am retraining myself to use the home/end key.<br />
<br /></div>
<div>
For programmers Linux is an upgrade over OSX or Windows. Why? Apt-get. Software dependency management with apt-get is fast, easy and works. Homebrew on OSX attempts to emulate apt-get, but is more wild west and falls short.</div>
<div>
<br />
<h2>
Final thoughts:</h2>
<div>
Should you build a Ryzen based PC? Yes! Absolutely! There is a discontinuity in the CPU price to performance ratio introduced by the launch of this new CPU range. Programmers and enthusiasts stand to benefit with more raw power for their dollar.</div>
<div>
<br /></div>
Building a PC is fun. It is just like Christmas with all sorts of interesting packages are arriving at your front door. I got a sense of accomplishment from putting my PC together. The final result looks and performs great. It did take a significant investment of time, effort, and attention to detail. As a computing enthusiast, that time was well spent.<br />
<br />
The Ryzen build specs on paper promised to be 2X better than my previous hardware. In most programming oriented tasks it delivered an improvement factor of around 1.5X. Responsiveness while multitasking was vastly improved. The cost of $1525 excluding peripherals was well below a premium off the shelf equivalent.<br />
<br />
Can Linux cut it? Yes! Ubuntu just keeps getting better. Linux on the desktop is really, really good these days.<br />
<br />
Is Intel doomed? Doubtful. On March 19 the Intel i7-6900K retails for over $1000; twice the price of the equivalent AMD Ryzen 1800X. It sure looks to me like AMD has jumped way ahead of Intel in CPUs for programmers. Such a direct comparison is for a small segment of the CPU market. There are other price points where Intel still beats AMD; ultra servers, low end servers, and laptops.<br />
<br />
Should you feel bad if you still prefer Mac? No! OSX software and Mac hardware are pretty great. It is impressive that a 4 year old laptop can still be in the same league as a modern desktop.<br />
<br />
Thank you for reading my blog, and have a great day!<br />
<br /></div>
</div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-68464816104162266002017-02-26T21:48:00.000-08:002017-02-26T21:48:27.829-08:00Big Trouble in Little Taiwan<h2>
小台灣的大麻煩</h2>
<div>
<br /></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipCA0hGEPgZuuNbNv82a1uKCb7rouOA3AT8SaTuKIE6sF9agSTA5aZxcvA79tzorQUdMMqoYwXEr8J7VlBB8dxnuRbSd5F1MEETg3hbH_n7fiyhkeB-ZTjaDGQDdxNMNwKbuHJ3Q/s1600/lunar.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipCA0hGEPgZuuNbNv82a1uKCb7rouOA3AT8SaTuKIE6sF9agSTA5aZxcvA79tzorQUdMMqoYwXEr8J7VlBB8dxnuRbSd5F1MEETg3hbH_n7fiyhkeB-ZTjaDGQDdxNMNwKbuHJ3Q/s640/lunar.png" width="640" /></a><br />
<br />
Lunar New Year in Taiwan.<br />
Everybody is celebrating with firecrackers, lanterns, food, gifts, and having fun.<br />
<br />
農曆新年在台灣。<br />
大家都在慶祝爆竹,燈籠,食品,禮物和樂趣。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtKVLGVY8bp9qgWUwUA8FhkUGPGTIeffHNW6GFQIPAMFllykZvVnmc_fhPm3LohEiNOsUZvjBQqZBhVTNaotVE2tUtd4g8vMEvT3b7O_vSFBgfFgm_01PtZJgYmhPd5oKxU6SmFg/s1600/eating.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtKVLGVY8bp9qgWUwUA8FhkUGPGTIeffHNW6GFQIPAMFllykZvVnmc_fhPm3LohEiNOsUZvjBQqZBhVTNaotVE2tUtd4g8vMEvT3b7O_vSFBgfFgm_01PtZJgYmhPd5oKxU6SmFg/s640/eating.png" width="640" /></a><br />
<br />
The citizens of Taiwan are eating all the best tasty foods with their families...<br />
But where is the fruit this year?<br />
<br />
台灣公民都在家裡跟家庭一起吃所有的美味。<br />
但是今年為什麼沒有水果哪?<br />
<div>
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj35VcRngVJPEs0quaGBBIswSSm9W3H_MSFDN4guoiwA85-5C5dGfqia1OVh9B-iGlldHd4JXXfovWM7Y2bePypYSiSBhNYXd8k_SjP2xKPi_FfSIctk4BfUfBZL3ZOp1WfDlypCQ/s1600/riding.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj35VcRngVJPEs0quaGBBIswSSm9W3H_MSFDN4guoiwA85-5C5dGfqia1OVh9B-iGlldHd4JXXfovWM7Y2bePypYSiSBhNYXd8k_SjP2xKPi_FfSIctk4BfUfBZL3ZOp1WfDlypCQ/s640/riding.png" width="640" /></a><br />
<br />
Smiling Uncle: "I'll pop out and get some fruit from the market. Be right back."</div>
<div>
<br /></div>
<div>
微笑叔叔:“我出去市場買水果,馬上就回來。”<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSH59-CjofrOPwX6ub9mtWZCFZfYgG9QrzOrK86AXvVJH0BXbbTWqM6IFAL_8GOiKaI51PAkll0dbctNaTauu1tA3TugcxA3Jh9mFPPUt4Upxmng6ZeZAIByJOJYGXDSlCDCIg9A/s1600/sold_out.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSH59-CjofrOPwX6ub9mtWZCFZfYgG9QrzOrK86AXvVJH0BXbbTWqM6IFAL_8GOiKaI51PAkll0dbctNaTauu1tA3TugcxA3Jh9mFPPUt4Upxmng6ZeZAIByJOJYGXDSlCDCIg9A/s640/sold_out.png" width="640" /></a><br />
<br />
Papa: "We don't have any fruit."<br />
Mama: "Our supplier ran out of fruit two days ago and cannot get any more stock."<br />
<br />
<div>
<div>
爸爸:“我們一點水果都沒有。</div>
<div>
媽媽:“前兩天我們的供應商已經沒有送貨來了。他找不到水果來買。真沒有貨啊!</div>
</div>
</div>
<div>
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNuwxP-LpPov6imoIbQ1_l-s3DwINFaVmvhk3aiNBiGZwuI2fJqF6rDRjKaS8xKk3CL7ZE6JkZqOyDf5URmox-q8BIFVJkPRwCU62qQVRE9kaC2v56EDGPXLxL8AdacrvpDic3Nw/s1600/general.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNuwxP-LpPov6imoIbQ1_l-s3DwINFaVmvhk3aiNBiGZwuI2fJqF6rDRjKaS8xKk3CL7ZE6JkZqOyDf5URmox-q8BIFVJkPRwCU62qQVRE9kaC2v56EDGPXLxL8AdacrvpDic3Nw/s640/general.png" width="640" /></a><br />
<br />
Premier of Taiwan: "A fruit shortage is threatening to ruin Lunar New Year for our citizens. Assemble a task force and save our fruit. Our country is relying on you."<br />
Colonel Cheerful: "I will protect Taiwan!"<br />
<br />
台灣總理:「水果短缺威脅著我們市民的農曆新年,組建一支專責小組,拯救我們的果子,我們的國家依賴你。<br />
上校快樂:“我會保護台灣!<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpUaAZ6eIozUomCl9zd5k5kx0CCiUZNOoxkK5A2f9E_YVCeBEfz5EWzxwFz2uOcF-CdADbmHw31A9hiAN38zIVB8KHZZp7920ZQ_mcn2ceuURb9gZXLD0Qmmhe5FzYx0bWWQ_klg/s1600/funsister.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpUaAZ6eIozUomCl9zd5k5kx0CCiUZNOoxkK5A2f9E_YVCeBEfz5EWzxwFz2uOcF-CdADbmHw31A9hiAN38zIVB8KHZZp7920ZQ_mcn2ceuURb9gZXLD0Qmmhe5FzYx0bWWQ_klg/s640/funsister.png" width="640" /></a><br />
<br />
Colonel Cheerful: "Fun Sister, we need you."<br />
Fun Sister: "I'm already on my way!"<br />
<br />
上校快樂:“好玩的姐姐,我們需要你。<br />
好玩的姊姊:“我已經來著啦!<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmR7udx5I_r81imlC7ULF4Gh-5L4kB_zXA2lO6dDbgmYBpP1yhr9cqvM7infYo622ykiQ420AuTSpL4VnnMs804iHkTRtkd9eAckFJyaULbJGQ5i7XE22FRLHjU0T_CY9IfLUiXg/s1600/kick.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmR7udx5I_r81imlC7ULF4Gh-5L4kB_zXA2lO6dDbgmYBpP1yhr9cqvM7infYo622ykiQ420AuTSpL4VnnMs804iHkTRtkd9eAckFJyaULbJGQ5i7XE22FRLHjU0T_CY9IfLUiXg/s640/kick.png" width="640" /></a><br />
<br />
Colonel Cheerful: "Taekwondo Sister, I need your special kicking ability."<br />
Taekwondo Sister: "Absolutely! I just need a moment to apply some makeup..."<br />
<br />
上校快樂:“跆拳道姐姐,我需要你特殊的踢能力。<br />
跆拳道姐姐:“當然啊!我先搽口紅 化妝臉 就來了”<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnPKDXxWB3gQfLZ38woqS1xkoqatsoyqMyfDA2_a8NwKTu8RE-LCDieMOO5sHdUbeq78QwciGJvPaW9l24fR7e83aPfPFKo6Lbz9G_beaTGuIALxNxAB7xWjlW3Xi-7SgZxvRRJg/s1600/jj.png">
<img border="0" height="638" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnPKDXxWB3gQfLZ38woqS1xkoqatsoyqMyfDA2_a8NwKTu8RE-LCDieMOO5sHdUbeq78QwciGJvPaW9l24fR7e83aPfPFKo6Lbz9G_beaTGuIALxNxAB7xWjlW3Xi-7SgZxvRRJg/s640/jj.png" width="640" /></a><br />
<br />
Colonel Cheerful: "Stop showing off Pop Star, we have work to do."<br />
Pop Star stops his performance to join the team.<br />
Many female fans scream deliriously as their hero goes to serve his country.<br />
<br />
上校快樂:“停止炫耀流行歌星,我們有工作要做。<br />
流行歌星停止他的表現加入團隊。<br />
許多女性粉絲狂熱地尖叫著他們的英雄去服務他的國家。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijpL4fCTcZSc0qwjM4eB9Cj9uXQUcwH_ZwzPFgkEzXSpWVj-V6FMQzf0HCggpo4UkXKFBwNbPay5CbpvWclIi7UGeVKMDRDm9NMVm_duSkUFQhZh2yfZv15psPhWKJfwaFcc7plg/s1600/sleeping.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijpL4fCTcZSc0qwjM4eB9Cj9uXQUcwH_ZwzPFgkEzXSpWVj-V6FMQzf0HCggpo4UkXKFBwNbPay5CbpvWclIi7UGeVKMDRDm9NMVm_duSkUFQhZh2yfZv15psPhWKJfwaFcc7plg/s640/sleeping.png" width="640" /></a><br />
<br />
Sleepy Sister: "Hmmm I'm sleeping, can we go later? zzzzZZzzzZZZzz..."<br />
Colonel Cheerful: "Wake UP! Taiwan is in danger."<br />
Sleepy Sister: "Huh? O.K. I'll help."<br />
<br />
貪睡者妹妹:“哈,我在睡覺,我們可以晚一點才去嗎?<br />
上校快樂:“起床了!台湾处于危险之中<br />
貪睡者妹妹:"什麼?好的,我來幫幫忙。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBIJIuPZj_Dhbq_-Sb_NHQMiMiiPBlv_fEW3BjpI7Bmm4_XW08-VdF6ArIGFQbim7m3WCcJ-JAbUkzh1OrFEUc4s8Q9kiG1ffyUidC_HPeQfc2CMvVcNDDHbK43jgkl6eqw1EcyQ/s1600/professor.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBIJIuPZj_Dhbq_-Sb_NHQMiMiiPBlv_fEW3BjpI7Bmm4_XW08-VdF6ArIGFQbim7m3WCcJ-JAbUkzh1OrFEUc4s8Q9kiG1ffyUidC_HPeQfc2CMvVcNDDHbK43jgkl6eqw1EcyQ/s640/professor.png" width="640" /></a><br />
<br />
Colonel Cheerful: "Professor Cute, your country needs you."<br />
Professor Cute: "Oh sure! I was just chatting with my friends. This will be fun!"<br />
<br />
上校快樂:“可愛教授,你的國家需要你。<br />
可愛教授:“哦,我只是和朋友聊天,這很有趣!<br />
<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_ezDiY6WDNnSjt82fmE4lrDXZnQQhzGB_4Abjxzwas2lulDJYjAw37ARzv4GtE8wCE15o3ZA60MiK62MtvtOuv3kKGqWVYb-BzheYgd0quaMUG9D1yN7V-gb81gORPX1u_QFX2A/s1600/specialops.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_ezDiY6WDNnSjt82fmE4lrDXZnQQhzGB_4Abjxzwas2lulDJYjAw37ARzv4GtE8wCE15o3ZA60MiK62MtvtOuv3kKGqWVYb-BzheYgd0quaMUG9D1yN7V-gb81gORPX1u_QFX2A/s640/specialops.png" width="640" /></a><br />
<br />
"This is Spy reporting. There is suspicious activity occurring on Little Liuqiu Island. All the fruit is being taken by force from the growers. Please send the task force here immediately."<br />
<br />
“這是間諜報導,小柳秋島發生了可疑的活動,所有的水果都是從種植者身上帶走的,請立即派遣特遣部隊去。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwnQINRi7f8OVcqj6UGLp4RWv_C6TjmOa8YEbIpmNKhEqZy5N2_QaBTMHruDrqvj9UFDenyKJmUC4YeGamFC9kP9RTWFfkU6qVRuIVJgR8iFv9x9X5KMFiJ-Vj8CT_GwBTjXB3bQ/s1600/car.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwnQINRi7f8OVcqj6UGLp4RWv_C6TjmOa8YEbIpmNKhEqZy5N2_QaBTMHruDrqvj9UFDenyKJmUC4YeGamFC9kP9RTWFfkU6qVRuIVJgR8iFv9x9X5KMFiJ-Vj8CT_GwBTjXB3bQ/s640/car.png" width="640" /></a><br />
<br />
Aunt: "Hop in! I'll drive the team to the rendezvous."<br />
<br />
阿姨:“加油!我会把车队带到会合处。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_JReA0qpuNYC9vSUQdwiQ-jOyEslpCVmfcVu4BCyycnfBwVq5twBd0AKTlwsd_a2dJMfQ4jfvR1KVIku96M_BsgUvc3ki2_NwoR9mZh4agBJQxzfHIqjYeP7zILrfJmbptc6C9g/s1600/car_jump.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_JReA0qpuNYC9vSUQdwiQ-jOyEslpCVmfcVu4BCyycnfBwVq5twBd0AKTlwsd_a2dJMfQ4jfvR1KVIku96M_BsgUvc3ki2_NwoR9mZh4agBJQxzfHIqjYeP7zILrfJmbptc6C9g/s640/car_jump.png" width="640" /></a><br />
<br />
Aunt: "The last ferry is leaving... but we can make it. Hold on!"<br />
<br />
阿姨:“最後一個渡輪離開......但我們可以做到,堅持下去!<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg8528kam2DEmM7gOmS01HB0GSmR1WrkOV869WnbjdBTVm1WbLoxr4Nf3GJD-xaOpcasgUCuGkwWiGQmMedjpPJedBt2ecQaCdj1bJA4jIIfGjbaqUfiii9RHkYsSLpDMDJzn0_g/s1600/scooters.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg8528kam2DEmM7gOmS01HB0GSmR1WrkOV869WnbjdBTVm1WbLoxr4Nf3GJD-xaOpcasgUCuGkwWiGQmMedjpPJedBt2ecQaCdj1bJA4jIIfGjbaqUfiii9RHkYsSLpDMDJzn0_g/s640/scooters.png" width="640" /></a><br />
<br />
Colonel Cheerful: "O.K. team, we made it. Spread out and find the stolen fruit."<br />
<br />
上校快樂:“我們團隊,我們做了,展開,找到被盜的水果。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgT1YFA6RZjsAWf2aVjWX7iYoP4aSKkPW3F16xcDQl9a1z2p627riJcEtx_arGqz_2_4pzOxKx91l0HRTdFtxwYs7omfpBzfO8EiFwXrYhkANPGqs_0Ns9hyAKRm4_UNQXX3EZd-Q/s1600/diving.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgT1YFA6RZjsAWf2aVjWX7iYoP4aSKkPW3F16xcDQl9a1z2p627riJcEtx_arGqz_2_4pzOxKx91l0HRTdFtxwYs7omfpBzfO8EiFwXrYhkANPGqs_0Ns9hyAKRm4_UNQXX3EZd-Q/s640/diving.png" width="640" /></a><br />
<br />
Professor Cute: "I've found someone in a submarine packed full with fruit. Come to Flower Vase Rock."<br />
<br />
可愛教授:“我發現有人在一個充滿水果的潛艇,來到花瓶岩。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPaUIZY8XBGzd3WyPAWW1jruwj4uVji5yTmW9K8-qZNAupNm9vCzDGfdl92HSHVDFpTCJDL2-e-ogP8nPwf_d5rjfkXXPR2Cc4mOM8zoS_AKeebK87JOortRBpkjmwFhtjzYlHZQ/s1600/rowing.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPaUIZY8XBGzd3WyPAWW1jruwj4uVji5yTmW9K8-qZNAupNm9vCzDGfdl92HSHVDFpTCJDL2-e-ogP8nPwf_d5rjfkXXPR2Cc4mOM8zoS_AKeebK87JOortRBpkjmwFhtjzYlHZQ/s640/rowing.png" width="640" /></a><br />
<br />
Colonel Cheerful: "Team! Follow that submarine. The submarine is headed back to the mainland. Everybody paddle hard."<br />
<br />
上校快樂:“團隊!跟著那艘潛艇,潛艇回到大陸,每個人都堅強。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOViLYVNl9CL_jqJ2QJhmeUAEecnx95KvEgOwY724Ll-_6b0_Ov5Kk6V2E0UCbiNaGnlf9nq0gIZhYvFbKeNbzVPi0pBN-TKovg33SJqQ21vOPTBoAORhKxeFrWIhv4CAIkEM8OQ/s1600/ahchim.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOViLYVNl9CL_jqJ2QJhmeUAEecnx95KvEgOwY724Ll-_6b0_Ov5Kk6V2E0UCbiNaGnlf9nq0gIZhYvFbKeNbzVPi0pBN-TKovg33SJqQ21vOPTBoAORhKxeFrWIhv4CAIkEM8OQ/s640/ahchim.png" width="640" /></a><br />
<br />
Fruit Thief: "So... you finally caught up with me here in Kenting. I have collected all the fruit in Taiwan. I am going to eat it all myself. You cannot stop me..."<br />
<br />
水果賊:“所以......你終於在墾丁跟我在這裡,我收集了台灣的所有水果,我要自己吃,你不能阻止我...”<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBMJzOP5wQIBcSO7GtFxBQ2RzjK6B0QX6g3c9FUb3E9VG9pw1lOqWTsUHZ_JvX9bHa3SeeNy6fBCAweQCdJtfnrCgYRIfaeKk6RIOsNxA8ZpO_-6miJ51ZaQxz-xFvqpwnINTxKA/s1600/tank.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBMJzOP5wQIBcSO7GtFxBQ2RzjK6B0QX6g3c9FUb3E9VG9pw1lOqWTsUHZ_JvX9bHa3SeeNy6fBCAweQCdJtfnrCgYRIfaeKk6RIOsNxA8ZpO_-6miJ51ZaQxz-xFvqpwnINTxKA/s640/tank.png" width="640" /></a><br />
<br />
Fruit Thief: "I have a tank. Ha ha ha!"<br />
<br />
<br />
水果竊賊:“我有一個坦克,哈哈哈!<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi418XWFngpBGoxhmAFe6Ji-M5hNXF2iiFOjN5X6F2W5D3_tdwdel9D8tBOhWDpGfKLD4QOd2hPAw2xqTkyLLUDa0SYGnMP83Jmh72R3SpiP8NolnGuv1SDgLf74BSPMwIM2kDu6Q/s1600/temple.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi418XWFngpBGoxhmAFe6Ji-M5hNXF2iiFOjN5X6F2W5D3_tdwdel9D8tBOhWDpGfKLD4QOd2hPAw2xqTkyLLUDa0SYGnMP83Jmh72R3SpiP8NolnGuv1SDgLf74BSPMwIM2kDu6Q/s640/temple.png" width="640" /></a><br />
<br />
Fun Sister: "How can we stop a tank?"<br />
Pop Star: "Let's buy firecrackers from the temple."<br />
<br />
趣味姐妹:“我們怎麼可以阻止坦克?<br />
流行明星:“讓我們從寺廟買爆竹。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjptboYajGJWfhmgnyRvHyzBU5SGUb4fzF7VnUz3dWBw9KkRwBKmJGx38ugp6BgmtRY6m-bJ5r8fQFufRbfs2H5H1l8_IzGQK77vVVzClnZrEnuOXWb19YQp8T1oj85-AXlunOpyQ/s1600/tank_destroyed.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjptboYajGJWfhmgnyRvHyzBU5SGUb4fzF7VnUz3dWBw9KkRwBKmJGx38ugp6BgmtRY6m-bJ5r8fQFufRbfs2H5H1l8_IzGQK77vVVzClnZrEnuOXWb19YQp8T1oj85-AXlunOpyQ/s640/tank_destroyed.png" width="640" /></a><br />
<br />
Pop Star: "Light the firecrackers and throw them into the tank hatch."<br />
Fruit Thief: "Oh no! I'm getting out of here!"<br />
<br />
流行明星:“點燃鞭炮,把它們扔進坦克艙口。<br />
水果竊賊:“哦不,我要離開這裡了!<br />
<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_2xxqN25DBVr-5dG9K6d9dls5sfKJ3Qr5uBUguPLnH6tfLiNohnXvzNg6AiDwgCyxsiNpAyMhxTqX3SzLYCFAvJl48fBqAxR8rDTvTqRI3TUDDMQk34Ggul9G4IA0Im6Qlm4n7A/s1600/capital.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_2xxqN25DBVr-5dG9K6d9dls5sfKJ3Qr5uBUguPLnH6tfLiNohnXvzNg6AiDwgCyxsiNpAyMhxTqX3SzLYCFAvJl48fBqAxR8rDTvTqRI3TUDDMQk34Ggul9G4IA0Im6Qlm4n7A/s640/capital.png" width="640" /></a><br />
<br />
Spy: "Fruit Thief fled toward the ancient capital Tianan. We must pursue him."<br />
<br />
間諜:“水果賊逃向古都天安,我們必須追求他。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9RxMGxtAca5jPrLjmzFVpdH63CbBjmx9qioYJsM6twSsgolLw4VaFMCGhqB41iooAXeG0VoEjYGljCgjvg6kd7Ma-fXaavEviN9TM7TtxMtesedU88b9cplzK0lI6C3gdwvQqfw/s1600/ghost_girls.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9RxMGxtAca5jPrLjmzFVpdH63CbBjmx9qioYJsM6twSsgolLw4VaFMCGhqB41iooAXeG0VoEjYGljCgjvg6kd7Ma-fXaavEviN9TM7TtxMtesedU88b9cplzK0lI6C3gdwvQqfw/s640/ghost_girls.png" width="640" /></a><br />
<br />
Fruit Thief: "You fell into my trap! Ghosts, attack!!"<br />
Sleepy Sister: "Oh no, it's an ambush! Those are ghosts, how can we stop them?"<br />
<div>
<br /></div>
<div>
<div>
水果竊賊:“你陷入我的陷阱!鬼,攻擊!</div>
<div>
睡姐姐:“哦不,這是埋伏!那些是鬼,我們怎麼能阻止他們?</div>
</div>
<div>
<div>
<br /></div>
</div>
<div>
<br /></div>
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgS_R9PZ2moNBXwCDtHKKMxtQbfowhJv3zgl288xR4L0lFC1wz_73gRrOELwaxOer8K1uViGvaPt1hc_MORJPtxItzlDsLqKYLRouc4zdiHXtS-L9H_Es0oR_x447NeC_L2h7F0w/s1600/helidrop.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgS_R9PZ2moNBXwCDtHKKMxtQbfowhJv3zgl288xR4L0lFC1wz_73gRrOELwaxOer8K1uViGvaPt1hc_MORJPtxItzlDsLqKYLRouc4zdiHXtS-L9H_Es0oR_x447NeC_L2h7F0w/s640/helidrop.png" width="640" /></a><br />
<br />
Sports Sister: "Did someone call for air support?"<br />
Sleepy Sister: "Look, it's a helicopter!"<br />
Sports Sister: "These lucky charms will clear up your ghost problem."<br />
<br />
體育姐妹:“有人打電話給空中支援嗎?<br />
睡姐妹:“看,直升機!<br />
體育姐妹:“這些幸運的魅力會消除你的幽靈問題。<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMNQidH1GX7rilxTv1cb0HHyDhlX9YTqvrWQOLzVeLMl80Bl51s-48NknC3k7EhWs6GbKSWx-6FuB3L4THpq4XthnogOsN3OEDNO0dKDKIGp3rGAsXb3vQQGoInIdaHMqfoBcGkw/s1600/yay.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMNQidH1GX7rilxTv1cb0HHyDhlX9YTqvrWQOLzVeLMl80Bl51s-48NknC3k7EhWs6GbKSWx-6FuB3L4THpq4XthnogOsN3OEDNO0dKDKIGp3rGAsXb3vQQGoInIdaHMqfoBcGkw/s640/yay.png" width="640" /></a><br />
<br />
Sports Sister: "Yay! I destroyed the ghosts."<br />
Professor Cute: "But where is Fruit Thief?"<br />
<br />
體育姐妹:“耶!我毀了鬼。<br />
可愛教授:“但是水果賊在哪裡?<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPQPsb8ZjIjcyKuSYbX1gShkRBxSCpRctHh8N_zpIri38IqpvP11J5ixa867k9EFDdiXWBQ3kriqhvCAj0DRYGNiOtcW8i0zyRiu7oa9Y-HmQj_JqECCV1ZujmrLajcM6M8hgWUQ/s1600/hauling.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPQPsb8ZjIjcyKuSYbX1gShkRBxSCpRctHh8N_zpIri38IqpvP11J5ixa867k9EFDdiXWBQ3kriqhvCAj0DRYGNiOtcW8i0zyRiu7oa9Y-HmQj_JqECCV1ZujmrLajcM6M8hgWUQ/s640/hauling.png" width="640" /></a><br />
<br />
Colonel Cheerful: "While we were fighting the ghosts, Fruit Thief went to Salt Mountain and stole all the salt! Things are getting out of hand. We must stop him now."<br />
<br />
上校快樂:“當我們打鬼的時候,水果賊去鹽山,偷走了所有的鹽!事情都不見了,我們現在必須阻止他。<br />
<br />
<div>
<br /></div>
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6xbV5o4WEMyzguHNPc0KEsdxqTvS5Sllumoabn7PedUFm0T1Ijt6TLfTCcJBEC4qQLX6OWSBJmTLdeUsJKMVx0Xpq9TGvYJgB4Kgo3GHYRH0ePTH0VoIR71Q5Ehmj3eEBaAy1Ww/s1600/action.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6xbV5o4WEMyzguHNPc0KEsdxqTvS5Sllumoabn7PedUFm0T1Ijt6TLfTCcJBEC4qQLX6OWSBJmTLdeUsJKMVx0Xpq9TGvYJgB4Kgo3GHYRH0ePTH0VoIR71Q5Ehmj3eEBaAy1Ww/s640/action.png" width="640" /></a><br />
<br />
Taekwondo Sister: "Drive next to the truck and I will kick Fruit Thief out of the driver's seat!"<br />
Pop Star: "I'm on it."<br />
Taekwondo Sister: "Flying kick!"<br />
<br />
跆拳道姐姐:“驅動旁邊的卡車,我會把水果賊從駕駛座!<br />
流行歌星:“我在上。<br />
跆拳道姐姐:“飛踢!<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJYzsZZC9lWuOP0807XPZawCkVk21-eluWJ_xFFmKSr2YIg5Jx99YHRpn52n1MWgnFFSpfN4dELGjZRWzVWBYsuN7HtbOIptSmhokaVJYOPgzy-RnjMZJ1ud9iJV4r5koedhwo0g/s1600/stopping.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJYzsZZC9lWuOP0807XPZawCkVk21-eluWJ_xFFmKSr2YIg5Jx99YHRpn52n1MWgnFFSpfN4dELGjZRWzVWBYsuN7HtbOIptSmhokaVJYOPgzy-RnjMZJ1ud9iJV4r5koedhwo0g/s640/stopping.png" width="640" /></a><br />
<br />
Fun Sister: "Stop the truck! There is a cliff ahead!"<br />
<br />
有趣的姐妹:“停車!前面有懸崖!<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguBYtrWu_FWaRd_XaBTi720u6LSaP4sMgzO9H8McX-gvtk8cj6KntThSv6Pubbn2TP0uwcmO-SL2GROr8zU9s9xAX_-aUFdiBfao6Azta8FqXw4yQHLaLjeAt2u0duDh_UxTFLsw/s1600/just_made_it.png" imageanchor="1"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguBYtrWu_FWaRd_XaBTi720u6LSaP4sMgzO9H8McX-gvtk8cj6KntThSv6Pubbn2TP0uwcmO-SL2GROr8zU9s9xAX_-aUFdiBfao6Azta8FqXw4yQHLaLjeAt2u0duDh_UxTFLsw/s640/just_made_it.png" width="640" /></a><br />
<br />
Taekwondo sister: "Phew. that was close."<br />
Colonel Cheerful: "All the salt has mixed with the fruit."<br />
Fun Sister: "I have an idea..."<br />
<br />
跆拳道姐姐:“噢,那就近了。<br />
上校快樂:“所有的鹽都混合了水果。<br />
有趣的姐妹:“我有一個想法...”<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIaDQeHYTNqE4s7Cp5QIlmkAr2O4KvKyPEbF_wS94IRfjYcm5pFrYDVbXk8sRpOPWn7r9TSvNRRN6m9tiZvvN02KwoTcxWT9rwL7X5R6G8mkbTL0xRra58xSpMGCPriJkwtMWXtQ/s1600/snacks.png">
<img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIaDQeHYTNqE4s7Cp5QIlmkAr2O4KvKyPEbF_wS94IRfjYcm5pFrYDVbXk8sRpOPWn7r9TSvNRRN6m9tiZvvN02KwoTcxWT9rwL7X5R6G8mkbTL0xRra58xSpMGCPriJkwtMWXtQ/s640/snacks.png" width="640" /></a><br />
<br />
Taiwan Fruit Task Force: "Happy new year everyone! Enjoy these salty fruit snacks! Be healthy! Be lucky! Be happy!"<br />
<br />
台灣水果工作組:“新年快樂大家!享受這些鹹水果小吃!健康!幸運!快樂!<br />
<br />
<br />
<br />
<br />
The End.<br />
<br />
結束。<br />
<br /></div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-64631091582505818132017-01-19T22:08:00.001-08:002017-01-22T16:33:39.825-08:00Reagent deep dive part 4: Application principles<link href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css" rel="stylesheet" type="text/css"></link>
<style>
body { background-color: #eeeeee; }
pre, code { font-size: 16px; background-color: white; }
</style>
<br />
<pre style="display: none;"><code class="klipse">
(ns my.reagent-examples
(:require
[clojure.string :as string]
[reagent.core :as reagent]
[reagent.ratom]))
(enable-console-print!)
(defn a-better-mouse-trap [mouse]
(let [mice (reagent/atom 1)]
(fn render-mouse-trap [mouse]
(into
[:div
[:button
{:on-click
(fn [e]
(swap! mice (fn [m]
(inc (mod m 4)))))}
"Catch!"]]
(repeat @mice mouse)))))
</code></pre>
Welcome to the fourth and final leg of the Reagent deep dive tour!<br />
<br />
In <a href="http://timothypratley.blogspot.com/2017/01/reagent-deep-dive-part-1.html">part 1</a> we saw how to define components and react to change.<br />
In <a href="http://timothypratley.blogspot.com/2017/01/reagent-deep-dive-part-2-lifecycle-of.html">part 2</a> we observed the lifecycle of a component.<br />
In <a href="http://timothypratley.blogspot.com/2017/01/reagent-deep-dive-part-3-sequences.html">part 3</a> we grappled with the nuances of sequences of components.<br />
<br />
Today, in part 4, we will construct a scribble application.
The scribble application allows users to create line drawings with their mouse.
We will not encounter any new Reagent features; rather I shall be pointing out some principles as we go which
will be easier to observe at the application level.
I'll be highlighting them as numbered principles, which is to overstate their weight.
They are my well intentioned advice and opinions, not official principles.
I offer them because I find that when starting something new it can be helpful to have some guiding principles on how to choose a path forward between multiple options. I hope they are helpful to you.<br />
<br />
You can edit the examples in this page (thanks to <a href="https://github.com/viebel/klipse">KLIPSE</a>). Simply modify the code in the example boxes to complete the exercises.<br />
<br />
Let's get swimming.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsZSewUgO1wKzwQ30CrjsKMD7XP4lUM02uHM79KpeWHZWsOvIauQNRDaZ09A3-pd0Bdm1BA5FpWcwRo6f0FzI5mxJ9AGdy1MTFCnFlrUnVNnc9JDBr6LikNlrIbpDk_vOBA6p1gg/s1600/turtle.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsZSewUgO1wKzwQ30CrjsKMD7XP4lUM02uHM79KpeWHZWsOvIauQNRDaZ09A3-pd0Bdm1BA5FpWcwRo6f0FzI5mxJ9AGdy1MTFCnFlrUnVNnc9JDBr6LikNlrIbpDk_vOBA6p1gg/s200/turtle.png" width="200" /></a></div>
<br />
<br />
<br />
<h2>
Drawing paths</h2>
<em>Life is the art of drawing without an eraser. -- John W. Gardner</em>
<br />
<br />
<br />
In order to create a scribble application, we will be handling mouse strokes. Scribbles will be represented as an SVG element consisting of paths. In HTML a <code>path</code> is defined with a
<code>d</code> attribute indicating commands to draw:<br />
<pre><code class="html"><svg width="100%" height="200">
<path stroke="black" d="M 50 100 L 150 100"/>
</svg>
</code></pre>
<br />
The first command is "Move To" (M) followed by a coordinate pair. In this case we "Move To" (50,100).
The next command is "Line To" (L), which draws a line from the current position to a new position. In this case we "Line To" (150,100).
L can be followed by one or many points to chain together.
You can read more about paths in the <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths">SVG reference</a> which also describes how to draw curves. To draw an SVG with a horizontal line in Reagent we write:
<br />
<br />
<h3>
Example Y: A horizontal line path</h3>
<pre><code class="reagent">
[:svg {:width "100%" :height 200}
[:path {:stroke "black" :d "M 50 100 L 150 100"}]]
</code></pre>
<br />
To create a scribble application we will need to display a bunch of paths representing user drawn lines.
Let's start by defining the model of what a drawing should be; a drawing shall be a vector of lines,
where lines are vectors of coordinates to connect.
<br />
<br />
<h3>
Example Z: A basic drawing model, a vector of vectors</h3>
<pre><code class="klipse">
(def my-drawing
(reagent/atom []))
(swap! my-drawing conj [50 100 150 100])
</code></pre>
<br />
To add a new line to a drawing we <code>conj</code> a new vector of coordinates onto the drawing vector.
Seems logical right?
<br />
<br />
<strong>Principle 1:</strong> Start with data.
<br />
<br />
Defining some sample data up front really helps flesh out the key ideas behind an application.
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio-oDABxCRVAA9oU3b1ls_KZefGs80xBf9HRVCpyegDL2VQ78nCZED91GXqDnAq88__pGeN-dUyiz6opHwBMnbJWIGXiouPYHXbbXH2Ufeze6HOhZYxshoiJlQULckznVUqfDh2w/s1600/data.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio-oDABxCRVAA9oU3b1ls_KZefGs80xBf9HRVCpyegDL2VQ78nCZED91GXqDnAq88__pGeN-dUyiz6opHwBMnbJWIGXiouPYHXbbXH2Ufeze6HOhZYxshoiJlQULckznVUqfDh2w/s320/data.jpg" width="320" /></a></div>
<br />
<br />
<br />
So given this model, how would we render it? Easy! We just convert each vector into a path element and shove
them into an SVG element parent.
<br />
<br />
<h3>
Example AA: A basic drawing view that renders a basic model of a drawing</h3>
<pre><code class="reagent">
(defn scribble1 [drawing]
(into
[:svg
{:width "100%"
:height 200
:stroke "black"
:fill "none"}]
(for [[x y & more-points] @drawing]
[:path {:d (str "M " x " " y " L " (string/join " " more-points))}])))
[scribble1 my-drawing]
</code></pre>
<br />
We have seen these constructs in previous parts of the tour,
But there are some slight difference about the examples I'll present
in this part of the tour which bear pointing out. These differences exist because
we are working toward a more unified goal as opposed to isolated examples.<br />
<br />
The first difference to notice is that <code>scribble1</code> takes a drawing as an argument.
Explicitly defining your dependencies is a good thing.
<br />
<br />
<strong>Principle 2:</strong> Prefer passing in state over a global reference to it.
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyKtZkL2on2Xp5P75ukUDHRheJyNzgS_Lesgl6ghAmeekSaS9t2NoP2AAG4AOzqGYztGgyGwYsEVp09WR_5X0H_P1gFF2etk0GwJJ0tTviuz1NTEQD9b2_DfFbqIQ7YI18lerWlA/s1600/messi.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyKtZkL2on2Xp5P75ukUDHRheJyNzgS_Lesgl6ghAmeekSaS9t2NoP2AAG4AOzqGYztGgyGwYsEVp09WR_5X0H_P1gFF2etk0GwJJ0tTviuz1NTEQD9b2_DfFbqIQ7YI18lerWlA/s320/messi.jpg" width="320" /></a></div>
<br />
<br />
<br />
Components that are explicit about the data they need and the change they will effect are testable in isolation.
Occasionally they also crystalize a reusable abstraction.
<br />
<br />
<strong>Exercise:</strong>
Refactor <code>scribble1</code> to allow a parent component to modify the background of the SVG.
<br />
<br />
It is quite tricky to know all the inputs to a component upfront.
If we add them as we go, chances are that we will develop a long argument list of highly specific inputs.
Here is a tip to keep components flexible without being overly specific;
situationally it can be useful to pass a map of attributes to merge.
In the case that we want a background parameter, that can just be specified as an attribute to merge.
Background is nested in style inside the attributes, so a plain merge wont do.
We need a deep merge. In Clojure <code>merge-with merge</code> does the job.
<br />
<br />
<h3>
Example AB: A more flexible drawing view which merges attributes</h3>
<pre><code class="reagent">
(defn scribble2 [attrs drawing]
(into
[:svg
(merge-with merge attrs
{:width "100%"
:height 200
:stroke "black"
:stroke-width 4
:fill "none"
:style {:border "1px solid"
:box-sizing "border-box"
:cursor "crosshair"}})]
(for [[x y & more-points] @drawing]
[:path {:d (str "M " x " " y " L " (string/join " " more-points))}])))
(def my-drawing2 (reagent/atom [[50 100 75 150 100 100 125 150 150 100]]))
[scribble2 {:style {:background-color "lightgreen"}} my-drawing2]
</code></pre>
<br />
<strong>Exercise:</strong> Get comfortable with the lines abstraction by adding more lines to draw a "Z" character.<br />
<div>
<br /></div>
Not every component we define needs to follow the passed attributes pattern. Accepting attributes is a pattern that tends to work well situationally, but would be tedious to do everywhere. My rule of thumb is to keep an eye out for excessive customization arguments, they may indicate an opportunity to pass attributes instead.<br />
<br />
<strong>Principle 3:</strong> Keep an eye out for opportunities to pass customization attributes.<br />
<br />
We have a model and a view.
Now if only we had a way to draw the lines with a mouse!<br />
<br />
<h2>
Handling mouse events</h2>
<em>All strange and terrible events are welcome, but comforts we despise.
-- Cleopatra</em>
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbT-3HqAwQcQdLNgrFR3lm2I750coU0swWlXRiD8tzfJ6n_bS3HXf25EiiRKlJ0lsbzzmoOzv5lRYEiFXdOA-YNiHWzNvjlsv7iTEfcbDuMQ7TdUZ5lO0aYuHZ49_UPkcGnoKBpQ/s1600/Screen+Shot+2017-01-17+at+9.52.17+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbT-3HqAwQcQdLNgrFR3lm2I750coU0swWlXRiD8tzfJ6n_bS3HXf25EiiRKlJ0lsbzzmoOzv5lRYEiFXdOA-YNiHWzNvjlsv7iTEfcbDuMQ7TdUZ5lO0aYuHZ49_UPkcGnoKBpQ/s320/Screen+Shot+2017-01-17+at+9.52.17+PM.png" width="270" /></a></div>
<br />
<br />
<br />
To scribble we need to listen to <code>mousedown</code> to detect starting a path,
<code>mousemove</code> to detect continuing a path, and
<code>mouseup</code> to detect completing a path.
Less obvious is <code>mouseleave</code> which should also complete a path,
as it indicates the mouse cursor has left the SVG area and no sensible path can be drawn.
All handlers of these events need to know the position of the mouse cursor relative to the SVG image.
So our first task is to calculate the position of a mouse event:
<br />
<br />
<h3>
Example AC: Detecting coordinates of a mouse click</h3>
<pre><code class="reagent">
(defn xy [e]
(let [rect (.getBoundingClientRect (.-target e))]
[(- (.-clientX e) (.-left rect))
(- (.-clientY e) (.-top rect))]))
(def my-drawing3 (reagent/atom []))
[:div
[scribble2
{:on-click
(fn [e]
(js/alert (pr-str (xy e))))}
(reagent/atom [])]
[:h3 "Click the empty SVG!"]]
</code></pre>
<br />
Clicking now alerts the coordinates of the click event.
<br />
<br />
The <code>xy</code> function takes an event and calculates the coordinates
of that event using <code>getBoundingClintRect</code> on the target of the event.
In this case the target of the event is our SVG element.
<br />
<br />
Notice that because the <code>scribble2</code> function can take attributes
we can easily supply an <code>on-click</code> handler without modifying the component.
I like how this allows us to separate our view and control behaviors.
It is just regular function and data composition.
<br />
<br />
Now let's define how the model should handle mouse events.
We will use a <code>reagent/atom</code> named <code>pen-down?</code> to record if we are currently drawing a path or not.
When a path is started we will insert two coordinate pairs because an SVG path requires
a starting location to "Move To" and one or more locations to "Line To" from there.
It is fine that they will be the exact same points.<br />
<br />
To continue a line we append a coordinate pair to a path.<br />
<br />
To Finish a line we reset the <code>pen-down?</code> state to indicate we are no longer drawing a path.
<br />
<br />
<h3>
Example AD: Event handling that updates a drawing model</h3>
<pre><code class="reagent">
(defn mouse-handlers [drawing]
(let [pen-down? (reagent/atom false)
start-path
(fn start-path [e]
(when (not= (.-buttons e) 0)
(reset! pen-down? true)
(let [[x y] (xy e)]
(swap! drawing conj [x y x y]))))
continue-path
(fn continue-path [e]
(when @pen-down?
(let [[x y] (xy e)]
(swap! drawing (fn [lines]
(let [last-line-idx (dec (count lines))]
(update lines last-line-idx conj x y)))))))
end-path
(fn end-path [e]
(when @pen-down?
(continue-path e)
(reset! pen-down? false)))]
{:on-mouse-down start-path
:on-mouse-over start-path
:on-mouse-move continue-path
:on-mouse-up end-path
:on-mouse-out end-path}))
(def my-drawing3 (reagent/atom []))
[:div
[scribble2 (mouse-handlers my-drawing3) my-drawing3]
[:h3 "Scribble on me!"]]
</code></pre>
<br />
I think it is pretty cool how we can separate the event handler code from the view like this.
But what if we had many sub-components
that all had tasks to do that modified a shared model? A single attribute input would not suffice because there is no explicit mapping of the handlers to the sub-components requirements. However I am sure that you can also see that
there is a logical next step of supplying multiple attribute maps, or a map of named handlers, or a dispatch function
to accept and route events. The idea of supplying a dispatch function is the heart of
<a href="https://github.com/Day8/re-frame">reframe</a>; a popular approach to structuring updates to the model. The point is that complex components may require some structure to route changes to the model, and that there are some good patterns for doing that when you need to.<br />
<br />
Regardless of how we structure event handlers, it is often a good idea to have a separate model namespace that only deals with domain transformations and operations. Even if the model is simple (indeed it should be), having colocated logic for data operations is a great boon. We don't have to search through all the view code to reason about the underlying behaviors of the application. The view code just renders the data it is passed.<br />
<br />
<strong>Principle 4:</strong> Separate view, model, and event handling code.
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg66crA9CyNfou_vTDrWFVEQtj0OIiS4DpcPMVIxImyi94FV55fKYatWG_YB483qwRLxR1fr3OcF-Sm04abi4bREScUnP11T9wYpquFqJDtCGkCJs5uEJD2CqloSIU8M6ICwPwT5A/s1600/threeninjas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg66crA9CyNfou_vTDrWFVEQtj0OIiS4DpcPMVIxImyi94FV55fKYatWG_YB483qwRLxR1fr3OcF-Sm04abi4bREScUnP11T9wYpquFqJDtCGkCJs5uEJD2CqloSIU8M6ICwPwT5A/s320/threeninjas.png" width="296" /></a></div>
<br />
<br />
<br />
<strong>Exercise:</strong> Draw a scribble with many overlapping lines. Notice anything amiss?
<br />
<br />
Things are starting to come together but we have hit a snag!
Drawing over the top of an existing line causes an erratic jump, leaving weird dashes in the upper left of the SVG.
Why is this happening?
The problem is that our <code>xy</code> function is calculating the position
relative to the target of the mouse event, but when our mouse is over a path,
the path is the target of the event, not the SVG container.
Two solutions spring to mind:
<br />
<ol>
<li>We could capture the SVG DOM node and calculate the coordinates relative to it,</li>
<li>We can suppress paths from triggering the event.</li>
</ol>
<br />
<strong>Principle 5:</strong> Prefer HTMLy solutions over DOMy solutions.
<br />
<br />
Option (1) is reminiscent of the jQuery approach; find the element you want to deal with and just do what you need to do. There's nothing wrong with that in the small, but it can get complicated as new requirements come to light.<br />
<br />
In this case option (2) exists which is to express our intent in HTML, so we don't need to keep a reference to the SVG element for coordinate lookup.
<br />
<br />
Let's side track just a bit to look at another example which presents a similar choice; a button activated input field that should acquire focus when revealed. This could be useful if we were to add an optional title to our squiggles.
<br />
<br />
<h3>
Example AE: An input field that acquires focus when revealed</h3>
<pre><code class="reagent">
(defn optional-title []
(let [show? (reagent/atom false)]
(fn []
[:div
[:button
{:on-click
(fn [e]
(swap! show? not))}
(if @show? "Hide" "Add a title")]
(when @show?
[:input {:auto-focus true}])])))
</code></pre>
<br />
<strong>Exercise:</strong> Clicking the button currently drops the cursor into the input box. Remove the <code>auto-focus true</code> attribute from the input. Without <code>auto-focus</code>
we have to click into the text field to enter text... annoying!
<br />
<br />
We could have created our own auto focus behavior by having <code>component-did-mount</code> or <code>ref</code> function call <code>focus</code> on the element directly. But seeing as there is a built in facility in HTML, it is cleaner to avoid calling code where adding an attribute will suffice.
<br />
<br />
So coming back to the problem of paths triggering events, let's go with option (2) because it feels more HTMLy. There is a style <code>pointer-events "none"</code> which will prevent our paths from raising events.
<br />
<br />
<h3>
Example AF: Preventing paths from raising events</h3>
<pre><code class="reagent">
(defn paths [drawing]
(into
[:g
{:style {:pointer-events "none"}
:fill "none"
:stroke "black"
:stroke-width 4}]
(for [[x y & more-points] @drawing]
[:path {:d (str "M " x " " y "L "(string/join " " more-points))}])))
(defn scribble3 [attrs drawing]
[:svg
(merge-with merge attrs
{:width "100%"
:height 400
:style {:border "1px solid"
:box-sizing "border-box"
:cursor "crosshair"}})
[paths drawing]])
(def my-drawing4 (reagent/atom []))
[scribble3 (mouse-handlers my-drawing4) my-drawing4]
</code></pre>
<br />
We have modified the definition of our view slightly.
Rather than attaching the <code>pointer-events "none"</code> style to every individual path,
I opted to use a <code>g</code> tag to group the paths and apply the style once to the entire group.<br />
<br />
Success! We can draw scribbles.
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHsIFoQExFBjneWug4W6LN0fz9UfOFg-X0ZjPgzW8JpXLZ2StamgusFXdFCR_-EsVofYacx9xnNh8IFGOrg1bH_9CSoRlV5FRzj7vatorzhF8v1rWkKy1RDZ-lk6EyDA9SIyKNSg/s1600/successkid.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHsIFoQExFBjneWug4W6LN0fz9UfOFg-X0ZjPgzW8JpXLZ2StamgusFXdFCR_-EsVofYacx9xnNh8IFGOrg1bH_9CSoRlV5FRzj7vatorzhF8v1rWkKy1RDZ-lk6EyDA9SIyKNSg/s200/successkid.png" width="175" /></a></div>
<br />
<br />
<br />
<strong>Exercise:</strong> Modify example AF to scribble in a different color.
<br />
<strong>Exercise:</strong> Add a "clear" button to example AF that resets <code>my-drawing4</code>
to an empty vector.<br />
<div>
<br /></div>
<br />
<br />
<h2>
Compose, compose, compose</h2>
<em>No one is an artist unless he carries his picture in his head before painting it, and is sure of his method and composition.</em><br />
<em>-- Claude Monet</em>
<br />
<br />
<br />
Yes, you guessed it... in this section we are going to put our scribbles inside the mouse trap we defined in part 2.
Clicking on the catch button will create new scribbles.
<br />
<br />
<h3>
Example AG: A self contained widget</h3>
<pre><code class="reagent">
(defn scribble-widget []
(let [a-drawing (reagent/atom [])
handlers (mouse-handlers a-drawing)]
[scribble3 handlers a-drawing]))
[a-better-mouse-trap [:div [scribble-widget]]]
</code></pre>
<br />
<br />
Reagent components compose well because they are quite literally functions and data. We used a higher order component (a component that takes another component as an argument) for our mouse trap, but hopefully you didn't give it a second thought until I pointed it out. It seems quite natural to pass a function to a function in Clojure. Reagent transfers Clojure's super power of function and data composition to the world of HTML.<br />
<br />
<strong>Principle 6:</strong> Use functions liberally.
<br />
<br />
The underlying theme of this entire series of posts is composition. Representing components the Reagent way really does put the focus on functions and data, and that in turn makes composition both central and seamless. We didn't have to make all our decisions upfront.
It was easy to adapt and reorganize because components are functions, and the contents of components are amenable to splitting and extracting further as a function.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4G4LfX2GMk7m0UvR8_u6GlOXcof1HS3bqi5Bzs7umnyuedawfVqAc5kikPyhJOZ9SqYYYMayqQx5tJ89Gt5QIuSGRj7XJOskeqWM3p9fjiuVU1PCDBUVWn5MienC4TVLMDZAJTA/s1600/Claude_Monet%252C_Impression%252C_soleil_levant.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br class="Apple-interchange-newline" /><img border="0" height="498" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4G4LfX2GMk7m0UvR8_u6GlOXcof1HS3bqi5Bzs7umnyuedawfVqAc5kikPyhJOZ9SqYYYMayqQx5tJ89Gt5QIuSGRj7XJOskeqWM3p9fjiuVU1PCDBUVWn5MienC4TVLMDZAJTA/s640/Claude_Monet%252C_Impression%252C_soleil_levant.jpg" width="640" /></a></div>
<br />
<div style="text-align: center;">
<i><br /></i></div>
<div style="text-align: center;">
<i>By <a class="extiw" href="https://en.wikipedia.org/wiki/Claude_Monet" title="en:Claude Monet">Claude Monet</a> - <a class="external text" href="http://faculty.wartburg.edu/wilson/arthistory/images/29/29-20.jpg" rel="nofollow">wartburg.edu</a>, Public Domain, <a href="https://commons.wikimedia.org/w/index.php?curid=5504881">Link</a></i></div>
<br />
<br />
While you can never have too many functions, you can have too many <code>reagent/atoms.</code><br />
<br />
<strong>Principle 7:</strong> Avoid fragmenting your model.<br />
<br />
We have spent much of this series observing cases where a <code>reagent/atom</code> serves to toggle a show state for a component. I want to make a distinction here between state that serves a localized interface goal such as toggling a text box, versus state that represents a logical model. It's usually a good idea to have one central model. If I find myself having a hard time reasoning about the behavior, it's usually a good sign that I need to take a step back and focus on defining the underlying model more clearly.<br />
<br />
<br />
<h2>
Conclusion</h2>
<em style="font-family: monospace;">The most important thing my Physics professor taught me was not formulas but - how to think. </em><em style="font-family: monospace;">-- Carin Meier</em><br />
<em style="font-family: monospace;"><br /></em>
<em style="font-family: monospace;"><br /></em>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_9fgViU5yznPwFzUNm_ef4zRkRc1gAEyrCgbpQCb2UozB3MZz9UvTSmHiKZARHtMwO3QMZ8rJQ93wyNosXW614WSZh60rlySlUNOk-5iiYR91cwg1GMa5XSvapmDV1iykwgDceA/s1600/Screen+Shot+2017-01-05+at+7.26.52+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_9fgViU5yznPwFzUNm_ef4zRkRc1gAEyrCgbpQCb2UozB3MZz9UvTSmHiKZARHtMwO3QMZ8rJQ93wyNosXW614WSZh60rlySlUNOk-5iiYR91cwg1GMa5XSvapmDV1iykwgDceA/s200/Screen+Shot+2017-01-05+at+7.26.52+PM.png" width="198" /></a></div>
<em style="font-family: monospace;"><br /></em>
<br />
<br />
Reagent provides a consistent model that feels true to the spirit of Clojure. The core abstractions of functions and data serve us well in component construction and composition. We were able to leverage strong abstractions for data representation and data transformation.<br />
<br />
At heart Reagent is beautifully simple. A component is a function that returns a UI element. Change can be observed. Yet we covered a variety of nuances in the application of this simple idea. Perhaps this is not so shocking in light of just how wide the scope of HTML is. Much of the code presented in this tour felt to me like we were working very close to HTML.<br />
<div>
<br /></div>
The most important thing Reagent taught me was not how to make web applications, but how to compose HTML.<br />
<br />
Thank you for reading these articles. I hope that you enjoyed the somewhat circuitous deep dive tour and are as excited as I am about the capabilities Reagent offers for quickly and concisely expressing web applications.<br />
<div>
</div>
<div>
<br /></div>
<div>
Have a great day!</div>
<div>
<br /></div>
<script>
window.klipse_settings = {
selector: '.klipse',
selector_reagent: '.reagent'
};
</script>
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-31852620478944168192017-01-15T23:29:00.000-08:002017-01-22T16:33:26.124-08:00Reagent deep dive part 3: Sequences<link href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css" rel="stylesheet" type="text/css"></link>
<style>
body { background-color: #eeeeee; }
pre, code { font-size: 16px; background-color: white; }
</style>
<br />
<pre style="display: none;"><code class="klipse">
(ns my.reagent-examples
(:require
[clojure.string :as string]
[reagent.core :as reagent]
[reagent.ratom]))
(enable-console-print!)
</code></pre>
Welcome to the third leg of the Reagent deep dive tour.<br />
<br />
In <a href="https://timothypratley.blogspot.com/2017/01/reagent-deep-dive-part-2-lifecycle-of.html">part 2</a> we got into the thick of the component lifecycle. Today we move on to nuances surrounding the representation of sequences. This will be quite a change of pace as it is a somewhat less tangible topic. We'll need to use our imaginations a little.<br />
<br />
You can edit the examples in this page (thanks to <a href="https://github.com/viebel/klipse">KLIPSE</a>). Simply modify the code in the example boxes to complete the exercises.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMbEu73X4No7biAXE9h3luQoeI-dNQGavg3mk5lLWMtp-Rcbky456KPearR9EiGYjcludMJk5ElPrjC6Z13Haq9vGGFlDdW0GqbYJA8Kyi3t9kNOPcGlTjX_MhP0mmeJjKFvezHA/s1600/nemo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMbEu73X4No7biAXE9h3luQoeI-dNQGavg3mk5lLWMtp-Rcbky456KPearR9EiGYjcludMJk5ElPrjC6Z13Haq9vGGFlDdW0GqbYJA8Kyi3t9kNOPcGlTjX_MhP0mmeJjKFvezHA/s320/nemo.png" width="320" /></a></div>
<br />
<br />
<h2>
Sequences</h2>
<em>Seven. Apparently this is the minimum number of times a person has to try to do something before said person actually succeeds in doing it. -- Rusty Bentley</em>
<br />
<br />
<br />
In <a href="http://timothypratley.blogspot.com/2017/01/reagent-deep-dive-part-1.html">part 1</a> it was noted that <code>[[:div] [:div]]</code> is not a valid Reagent component. However <code>[:div [[:div] [:div]]]</code> <em>is</em> a valid component, and has a special meaning. Reagent forms do allow a sequence as a child of a tag. A sequence is interpreted as a React array, which is interpreted to mean a collection of elements which may get swapped around.<br />
<br />
So we can write a component as:<br />
<br />
<h3>
Example U: A sequence of circles</h3>
<pre><code class="reagent">
[:svg {:width 200 :height 200}
(for [i (range 30)]
[:circle
{:r (* (inc i) 5)
:cx 100
:cy 100
:fill "none"
:stroke (str "rgb(0, " (* i 10) "," (* (- 30 i) 10) ")")}])]
</code></pre>
<br />
Instead of using into like we have been thus far:
<br />
<pre><code>(into [:svg] (for ...))</code></pre>
<br />
Note that the former results in something like:<br />
<pre><code>[:svg [[:circle] [:circle] ...]]</code></pre>
<br />
While the later results in something like:<br />
<pre><code>[:svg [:circle] [:circle] ...]</code></pre>
<br />
What difference does it make? Not much that we can notice at first glance.
We'll only notice a difference if we open the developer console.
Example U causes React to print warnings in the developer console:
<br />
<pre><code>Warning: Every element in a seq should have a unique :key</code></pre>
<br />
What the heck is React complaining about?<br />
<br />
Well, imagine for a moment that you had written the following list:<br />
<ul>
<li>Princess torte.</li>
<li>Black forest gateau.</li>
<li>Apple pie.</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCUWp-Q0DVJNZj03KsBoSj8Umdm4xh9nqN4ePZBu-GSw9U1SKX134D2Xa4xrIgQn_WVSnp87lIqceJ0KqGqAtxwGO2ynLPoNWsfl6hjHcvnuC8F2fLdErzSKVqaFOwQdDqH4Lxlw/s1600/Screen+Shot+2017-01-09+at+10.15.18+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCUWp-Q0DVJNZj03KsBoSj8Umdm4xh9nqN4ePZBu-GSw9U1SKX134D2Xa4xrIgQn_WVSnp87lIqceJ0KqGqAtxwGO2ynLPoNWsfl6hjHcvnuC8F2fLdErzSKVqaFOwQdDqH4Lxlw/s200/Screen+Shot+2017-01-09+at+10.15.18+PM.png" width="197" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
And then you realized that ice cream should be in the list, at the very top. The logical thing to do would be to insert ice cream above everything else. But another way to get the final list would be to replace princess torte with ice cream, then replace black forest gateau with princess torte, replace apple pie with black forest gateau, and finally add apple pie at the very end. The reason that we chose the first, more logical and faster method was because we knew that we wanted to insert a new element.<br />
<br />
Imagine for a moment that you emailed me the initial list and I put it into a spreadsheet. Then you sent me the final list. I could probably figure out to insert ice cream at the top is the most efficient change. So I make the update. But what if you emailed me a list of about 100 things, and then later emailed me the same list but with 20 items inserted, moved, deleted, or changed? It would be really hard for me to figure out the minimal updates required to update my spreadsheet. So instead I would copy the entire list over the top of my spreadsheet.<br />
<br />
There is however another solution! When you sent me the list you could have assigned each logical item an id number. That way when I got the second list, I could quickly identify the minimal updates required. I could walk through the new list, looking only at the id number. If the number was in the existing list and at the same position I could check that the item contents haven't changed and then move on. If the existing list had the id number at a different position I could move it to the correct position. If the existing list didn't have the id number, I would know to insert a new row. This sounds a little tedious, but I hope it is clear that there is a mechanical process available that which would ultimately result in less change to the spreadsheet.<br />
<br />
This minimal update scenario applies to HTML elements. React's job is to figure out the minimal changes to make to a HTML page in order to transition from the existing view state to the desired view state. If it can swap HTML elements around, that is far fewer updates than recreating them in different positions. You can give React the hint it needs by passing a unique key per element. To specify a key on an element in a sequence, use the mysterious <code>^{:key k}</code><br />
<code><br /></code><code></code>
<strong>Tip:</strong> The item key is represented in metadata attached to the item. <code>^</code> is shorthand to set the metadata of an object. You can also pass <code>:key</code> as an attribute of a hiccup form.<br />
<div>
<br /></div>
<div>
Here is how we represent a sequence of keyed entities:<br />
<br /></div>
<h3>
Example V: A sequence of sub-components, with identity keys</h3>
<pre><code class="reagent">
(def favorites
(reagent/atom
{"d632" {:name "Princess torte."
:order 2}
"1ae2" {:name "Black forest gateau."
:order 3}
"5117" {:name "Apple pie."
:order 4}
"42ae" {:name "Ice cream."
:order 1}}))
(defn list-by [entities sort-k]
[:ul
(for [[k v] (sort-by (comp sort-k val) @entities)]
^{:key k}
[:li (:name v)])])
(defn favorites-by-order-and-name []
[:div
[:h3 "By order"]
[list-by favorites :order]
[:h3 "By name"]
[list-by favorites :name]])
</code></pre>
<br />
The idea in this example is that each item has a unique ID,
perhaps assigned by a database, generated when the item was created.
That unique ID is used as the key in the sequence.
<br />
<br />
This example is clearly contrived! These components aren't big enough or numerous enough for us to waste our precious brainpower worrying about their rendering performance. For small lists of small components, you wont notice any performance difference between the three options for representing them; as direct children of a parent element, as a keyless sequence of elements, or as a keyed sequence of elements. However this tiny example does allow us to discuss exactly what those 3 alternatives look like, and why in the broader picture we should care.<br />
<br />
<ol>
<li>We could have used <code>into</code> to make all the list items direct children of the unordered list. This would have resulted in no warnings and not required the key. Updating the list with new data would result in some unnecessary DOM updates.</li>
<li>We could have left off the <code>^{:key k}</code> and ignored the warnings in the developer console. Updating the list with new data would result in some unnecessary DOM updates.</li>
<li>As presented there was a natural key available, so we annotated the each list item with metadata. There are no warnings in the console and updating the list will result in fewer DOM updates.</li>
</ol>
<br />
The pattern of "I have a bunch of entities I need to render" pops up here and there in practice. That is why the distinction between expressing sequences exists. By way of illustration I shall describe one such scenario. In my Napkindo app I display a gallery of drawings. Each drawing is an entity that has a database assigned id, and various information attached to it such as the drawing title, the line paths in the drawing, and the owner. For very large collections of large elements, assigning React keys improves UI performance. And it turns out that many of the large collections of large elements we run into fit this pattern nicely.<br />
<br />
There is another scenario where entity identity should be preserved; animating transitions of elements. Generally we don't care which elements in our DOM contain what HTML because it all looks the same once the updates are applied. But when rendering transitions, it becomes obvious which elements are linked to which logical entities. Visual identity must follow logical identity. I'll let you ponder that.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2Z0jS50pIINrJdD9AD6WYXzK-cFoIMdkwlt1OFw5gp8KqwkItG-k-HV-euWuFqjd3JZd3LRfDs8qm5FK6V28LUbViGUz0KGXrPs6BKZFQKsL2GWAp36NfoEbIigfrHz3hEex0Iw/s1600/icecream.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2Z0jS50pIINrJdD9AD6WYXzK-cFoIMdkwlt1OFw5gp8KqwkItG-k-HV-euWuFqjd3JZd3LRfDs8qm5FK6V28LUbViGUz0KGXrPs6BKZFQKsL2GWAp36NfoEbIigfrHz3hEex0Iw/s320/icecream.png" width="307" /></a></div>
<br />
<br />
In this highly technical diagram each box is logically bound to its contents. Keys provide the mechanism to make this binding.<br />
<br />
Coming back to our options... now that we have pondered at length the 3 ways of expressing sequences we can happily choose whichever we prefer, realizing that it wont make any difference in most circumstances. My personal opinion is that warnings are best heeded, so I avoid keyless sequences. My rule of thumb is to key my sequences if there is a natural key available. If there is no natural key, I default to using <code>into</code> the parent tag instead. I think it best to avoid the temptation to make up a sequential key by assigning each item in the sequence an index number. Doing so complicates the code to avoid a warning by deceiving React about the identity semantics of the sequence. The semantic of a key is that it identifies a unique entity in the sequence. In short, if there is a natural key, use it. If there is no natural key, put the items into the parent tag as children.<br />
<br />
There is one final consideration when using sequences; lazy deref is not allowed.
<br />
<br />
<h3>
Example W: A button that does not work due to lazy deref</h3>
<pre><code class="reagent">
(def message (reagent/atom "Everything is fine"))
(defn bad []
[:div
[:button
{:on-click
(fn [e]
(reset! message "Oh no, oh dear, oh my."))}
"Panic!"]
(for [i (range 3)]
[:h3 @message])])
</code></pre>
This example does not work! It will produce a warning:
<br />
<pre><code>Warning: Reactive deref not supported in lazy seq, it should be wrapped in doall</code></pre>
From Reagent's perspective calling <code>bad</code> does not deref <code>message</code>.
Rendering <code>[:h3 @message]</code> occurs later but at that point Reagent no longer knows that the parent component is <code>bad</code>.
Because Reagent doesn't evaluate the lazy sequence immediately it is unaware that <code>bad</code>
should respond to changes in <code>message</code>.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg37Px70RqptSlac3jNYq8QVtzQaN1uVjRYdGgHO5h_peZK_BuQQqmDA10DZtc4oT6ZLQxtRA6SfTm0gSXFRhmEWv8BXwzpNyo-chMCQ0aKbX6lHe3sl0NkuPY4E0BRomImPxZzsg/s1600/fine.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg37Px70RqptSlac3jNYq8QVtzQaN1uVjRYdGgHO5h_peZK_BuQQqmDA10DZtc4oT6ZLQxtRA6SfTm0gSXFRhmEWv8BXwzpNyo-chMCQ0aKbX6lHe3sl0NkuPY4E0BRomImPxZzsg/s200/fine.png" width="200" /></a></div>
<br />
<br />
We can force evaluation of a lazy sequence that derefs by wrapping it in a <code>doall</code>,
or by using using <code>vec</code> or <code>into</code> to realize the sequence, and then it will work just fine.
<br />
<br />
<strong>Exercise:</strong> Pressing the panic button does nothing. Fix example V by forcing evaluation of the lazy sequence, and press the panic button.<br />
<br />
Fortunately this somewhat confusing circumstance of deref inside a lazy sequence occurs rarely, and produces a warning with the advice on how to remedy it. Notice that we don't need to force a lazy sequence that consumes a deref, which is far more common. For example <code>(for [x @xs] ...)</code> does not need to be forced because the deref is not
<em>inside</em> the lazy sequence.<br />
<br />
<h2>
An intuition for sequence performance</h2>
<em>The proof is in the pudding. -- Unknown</em>
<br />
<br />
<br />
I made some pretty bold claims about the impact of keys and haven't provided a guide for
exactly when performance starts to be impacted aside from some vague notion of "large".
It is not a simple thing to quantify and will of course be situational, but we can gain a bit of intuition here of what a "large" sequence is.
Let's play out our earlier thought experiment about a list of tasty desserts getting updated.
<br />
<br />
<h3>
Example X: A large keyed sequence of delectable disposition</h3>
<pre><code class="reagent">
(def words
["ice" "cream" "chocolate" "pastry" "pudding" "raspberry" "mousse"
"vanilla" "wafer" "waffle" "cake" "torte" "gateau" "pie" "cookie"
"cupcake" "mini" "hot" "caramel" "meringue" "lemon" "marzipan" "mocha"
"strawberry" "tart" "custard" "fruit" "baklava" "jelly" "banana" "coconut"])
(defn rand-name []
(string/capitalize (string/join " " (take (+ 2 (rand-int 5)) (shuffle words)))))
(def desserts (reagent/atom ()))
(defn make-a-dessert [e]
(swap! desserts conj {:id (random-uuid)
:name (rand-name)}))
(defn make-many-desserts [e]
(dotimes [i 100]
(make-a-dessert nil)))
(defn color-for [x]
(str "#" (.toString (bit-and (hash x) 0xFFFFFF) 16)))
(defn dessert-item [{:keys [id name]}]
[:li
[:svg {:width 50 :height 50}
[:circle
{:r 20 :cx 25 :cy 25 :fill (color-for id)}]
[:rect {:x 15 :y 15 :width 20 :height 20 :fill (color-for name)}]]
[:span [:em [:strong name]]]])
(defn desserts-list []
[:ol
(for [dessert @desserts]
^{:key (:id dessert)}
[dessert-item dessert])])
(defn dessertinator []
[:div
[:button {:on-click make-a-dessert} "Invent a new dessert"]
[:button {:on-click make-many-desserts} "Invent 100 new desserts"]
[desserts-list]])
</code></pre>
<br />
<strong>Exercise:</strong> <code>desserts-list</code> currently keys each <code>dessert-item</code>.
Invent 2000 desserts by pressing the "100" button 20 times.
Then add another single dessert. Creating desserts should be fairly fast. Next delete the <code>^{:key (:id dessert)}</code> line in <code>desserts-list</code>
and perform the same steps. At about 2000 desserts, it takes noticeably longer to create new desserts!<br />
<br />
<strong>Tip:</strong> Every time you change the code, the desserts list is reset, so you might want to make another change
to the code so that you can finish the article when you are done experimenting.<br />
<br />
As you can see, computers are amazing and it really does take a very large sequence before performance is impacted by the lack of a key. With the keyed approach we can preserve performance with many, many items in our sequence.<br />
<br />
<br />
<h2>
Conclusion</h2>
<em>A gene is a long sequence of coded letters, like computer information. Modern biology is becoming very much a branch of information technology.
-- Richard Dawkins</em>
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5eBCep47TPhu76EzBHigWd1-LppLYlLilpq1TCqbpOLrN9lBUNv14cJD0k6jtmBTIuv8OXxY1yjr7eN02v9jQykqJeGJ7oM_aQ_XzdMhmEPidseHJJLmWNGCDmoPGh-EAXTLcHg/s1600/Screen+Shot+2017-01-14+at+8.31.07+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5eBCep47TPhu76EzBHigWd1-LppLYlLilpq1TCqbpOLrN9lBUNv14cJD0k6jtmBTIuv8OXxY1yjr7eN02v9jQykqJeGJ7oM_aQ_XzdMhmEPidseHJJLmWNGCDmoPGh-EAXTLcHg/s200/Screen+Shot+2017-01-14+at+8.31.07+PM.png" width="173" /></a></div>
<br />
<br />
Phew! We made it to our third stop off.<br />
<br />
We observed 3 different ways to express a sequence of elements and discussed how React treats them. In the 4th leg of the tour we shall not be encountering any new concepts. Instead we will be applying some of the concepts we have already encountered to build out a mini sketching application.<br />
<br />
I hope to see you again soon for <a href="http://timothypratley.blogspot.com/2017/01/reagent-deep-dive-part-4-application.html">part 4</a>, where we'll handle some more practical UI challenges.
<script>
window.klipse_settings = {
selector: '.klipse',
selector_reagent: '.reagent'
};
</script>
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>
<br />
<br />Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-47605519372036917582017-01-14T15:21:00.002-08:002020-10-06T14:58:39.702-07:00Reagent deep dive part 2: The lifecycle of a component<link href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css" rel="stylesheet" type="text/css"></link>
<script src="https://ajax.googleapis.com/ajax/libs/threejs/r83/three.min.js"></script>
<style>
body { background-color: #eeeeee; }
pre, code { font-size: 16px; background-color: white; }
</style>
<br />
<pre style="display: none;"><code class="klipse">
(ns my.reagent-examples
(:require
[reagent.core :as reagent]
[reagent.dom]
[reagent.ratom]))
(enable-console-print!)
</code></pre>
<br />
Welcome to the second leg of the Reagent deep dive tour!<br />
<br />
In <a href="http://timothypratley.blogspot.com/2017/01/reagent-deep-dive-part-1.html">part 1</a> we saw how to create components and react to change. Now we move on to some more challenging aspects of UI building. Today we examine how to represent component local state and how to access the rendered DOM nodes. We will need these capabilities in order to create some more interesting components and make use of third party JavaScript libraries so that we can render a 3D scene.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirdjncGbrKPqq5xpNigfknWXCjbM7URnu7cRvOr6kjLW7wZ0YWiQzlZ2bnGBEGtWHeim1OLkzwICfAD6hWBkqtLBz2J-k-PM1xsSAUhE_wNKyEgvWXPyiuG8U9Afen06AXBVcsoQ/s1600/Screen+Shot+2017-01-11+at+8.10.37+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="165" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirdjncGbrKPqq5xpNigfknWXCjbM7URnu7cRvOr6kjLW7wZ0YWiQzlZ2bnGBEGtWHeim1OLkzwICfAD6hWBkqtLBz2J-k-PM1xsSAUhE_wNKyEgvWXPyiuG8U9Afen06AXBVcsoQ/s200/Screen+Shot+2017-01-11+at+8.10.37+AM.png" width="200" /></a></div>
<br />
<br />
<h2>
Component forms</h2>
<em>One of the basic things about a string is that it can vibrate in many different shapes or forms,
which gives music its beauty. -- Edward Witten</em>
<br />
<br />
<br />
We have a mechanism for change, which we examined in part 1, and it works great for everything we can define in Reagent
that has access to an external <code>reagent/atom</code>.
But not everything falls into this neat view of the world. There are two exceptions:
<br />
<ol>
<li>
What if we want a self-contained component with it's own state? We want to be able to define and retain a local <code>reagent/atom</code>
instead of relying on one from the surrounding environment in order to build reusable components.
</li>
<li>
What if we want a component to call JavaScript functions on to the actual HTML elements after they are rendered?
There are many great JavaScript libraries that operate directly on HTML elements,
and in order to use them we need to be able to invoke them after the component has created the elements.
</li>
</ol>
The Reagent answer comes in two additional forms of specifying what a component is. So far we have been using the first form, a function that returns hiccup.
In total there are 3 important forms for specifying components:<br />
<ol>
<li>A function that returns a hiccup vector.</li>
<li>A function that returns a function.</li>
<li>A function that returns a Class.</li>
</ol>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://commons.wikimedia.org/wiki/File%3AMoodswingerscale.svg" style="margin-left: 1em; margin-right: 1em;" title="By Moodswingerscale.jpg: Y Landman derivative work: W axell (Moodswingerscale.jpg) [Public domain], via Wikimedia Commons"><img alt="Moodswingerscale" height="320" src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Moodswingerscale.svg/512px-Moodswingerscale.svg.png" width="288" /></a></div>
<br />
<br />
We saw plenty of examples of form 1 in part 1 of the deep dive tour. All the examples were functions that returned hiccup. So let's examine form 2 more closely now.<br />
<br />
<br />
<h3>
Example K: Reagent component form 2 - A function that returns a function</h3>
<pre><code class="reagent">
(defn greetings []
(fn []
[:h3 "Hello world"]))
</code></pre>
<br />
Here is a function that returns a function. The returned function (the inner function) returns a hiccup vector representing HTML. The outer function just returns the inner function.<br />
<br />
<strong>Exercise:</strong> Is a function that returns a function that returns a function a valid Reagent component? Find out by modifying the examples above. Wrap the inner function in yet another function.
<br />
<br />
Form 2 is useful for performing initial setup for a component.
A common usage of this form is to establish some local state.
Consider this example which creates a <code>reagent/atom</code> counter per instance:<br />
<br />
<h3>
Example L: Reagent component form 2 - A function that returns a function</h3>
<pre><code class="reagent">
(defn a-better-mouse-trap [mouse]
(let [mice (reagent/atom 1)]
(fn render-mouse-trap [mouse]
(into
[:div
[:button
{:on-click
(fn [e]
(swap! mice (fn [m] (inc (mod m 4)))))}
"Catch!"]]
(repeat @mice mouse)))))
[:div
[a-better-mouse-trap
[:img
{:src "https://www.domyownpestcontrol.com/images/content/mouse.jpg"
:style {:width "150px" :border "1px solid"}}]]
[a-better-mouse-trap
[:img
{:src "https://avatars1.githubusercontent.com/u/9254615?v=3&s=150"
:style {:border "1px solid"}}]]]
</code></pre>
<br />
These mice traps each have their own count of mice per trap. Compare this example to the previous counter in part 1 example H, which relied on a single global count.
Global state, and state passed as arguments tend to be useful for application features.
Local state tends to be useful for self contained components.<br />
<br />
Notice that this example is really just a closure (variable capture) occurring inside a function.<br />
<br />
Seeing that this is a common pattern, Reagent also provides <code>with-let</code> which will take care of the inner function for you:<br />
<br />
<h3>
Example M: Using with-let to avoid returning a function</h3>
<pre><code class="reagent">
(defn lambda [rotation x y]
[:g {:transform (str "translate(" x "," y ")"
"rotate(" rotation ") ")}
[:circle {:r 50, :fill "green"}]
[:circle {:r 25, :fill "blue"}]
[:path {:stroke-width 12
:stroke "white"
:fill "none"
:d "M -45,-35 C 25,-35 -25,35 45,35 M 0,0 -45,45"}]])
(defn spinnable []
(reagent/with-let [rotation (reagent/atom 0)]
[:svg
{:width 150 :height 150
:on-mouse-move
(fn [e]
(swap! rotation + 30))}
[lambda @rotation 75 75]]))
(defn several-spinnables []
[:div
[:h3 "Move your mouse over me"]
[a-better-mouse-trap [spinnable]]])
</code></pre>
<br />
<br />
This is a slightly more compact way of expressing the same concept. The rotation atom is created only once, while the component will be re-rendered when the rotation value is modified.<br />
<br />
<br />
O.K. so what about form 3? Let's look at how to create a Class:
<br />
<br />
<h3>
Example N: Reagent component form 3 - A function that returns a Class</h3>
<pre><code class="reagent">
(defn announcement []
(reagent/create-class
{:reagent-render
(fn []
[:h3 "I for one welcome our new insect overlords."])}))
</code></pre>
<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/8lcUHQYhPTE" width="560"></iframe>
<br />
<br />
<br />
This code should look familiar in that the <code>reagent-render</code> function is exactly like
any other component function we have seen before.
It has been wrapped explicitly in a <code>create-class</code> call.
The only difference is that we can also specify other lifecycle functions, which we will make use of soon. <br />
<br />
A React Class lifecycle consists of:<br />
<h4>
Mounting (Occurs once when the component is created)</h4>
<ul>
<li>constructor</li>
<li>componentWillMount</li>
<li>render</li>
<li>componentDidMount</li>
</ul>
<h4>
Updating (Occurs many times as the component reacts to change)</h4>
<ul>
<li>componentWillReceiveProps</li>
<li>shouldComponentUpdate</li>
<li>componentWillUpdate</li>
<li>render</li>
<li>componentDidUpdate</li>
</ul>
<h4>
Unmounting (Occurs once when the component will be removed from the DOM)</h4>
<ul>
<li>componentWillUnmount</li>
</ul>
You can read more about the component lifecycle in the <a href="https://facebook.github.io/react/docs/react-component.html">React docs.</a><br />
<br />
Fortunately we can take a much simpler view of the world in Reagent! The two lifecycle functions that can be of additional use to us are the componentDidMount function, which will allow us to interact with the created DOM elements, and the componentWillUnmount which will allow us to do cleanup.<br />
<div>
<br /></div>
Before we go much further into what <code>create-class</code> allows us to do,
let's take a brief interlude to examine a case where it looks like we need to use a class,
but in fact we can avoid using one.
<br />
<br />
<h3>
Example O: Performing cleanup</h3>
<pre><code class="reagent">
(defn mouse-position []
(reagent/with-let [pointer (reagent/atom nil)
handler (fn [e]
(swap! pointer assoc
:x (.-pageX e)
:y (.-pageY e)))
_ (js/document.addEventListener "mousemove" handler)]
[:div "Pointer moved to: " (str @pointer)]
(finally
(js/document.removeEventListener "mousemove" handler))))
</code></pre>
<br />
The <code>finally</code> clause of <code>with-let</code> will run when
<code>mouse-pos</code> is no longer tracked anywhere,
in this case when <code>tracked-pos</code> is unmounted.
The same thing could be achieved with a Class that specified a <code>component-will-unmount</code>.<br />
<br />
Reagent has well thought out facilities that allow us to write our components
as simple functions that respond to change.
Knowing the model of how those function relate to the underlying React lifecycle is useful for reasoning about component behaviors
and being able to choose concise functions as much as possible.
The bottom of the abstraction is creating the Class directly.
Why do we still need the ability to create a Class?
<br />
<br />
<br />
<img src="https://www.howitworksdaily.com/wp-content/uploads/2016/01/Ant-architects.jpg" style="width: 600px;" />
<br />
<br />
<br />
Well... one case is that when we need to access the DOM node of the component we constructed.
This comes up when making use of non-React JavaScript UI libraries.
For instance if you want to use Google Charts;
you need to call a render function on a target element after it is created.
This is where the the <code>component-did-mount</code> lifecycle method becomes valuable.<br />
<br />
<strong>Tip:</strong> You can alternatively provide a <a href="https://facebook.github.io/react/docs/refs-and-the-dom.html">ref function</a> as an attribute to a hiccup form to access DOM nodes. React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts. ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks. For now we'll focus on using the class lifecycle.<br />
<br />
Let's see how to make use of form 3 by creating a ThreeJS canvas.
<br />
<br />
<h3>
Example P: Reagent component form 3 - Creating a ThreeJS canvas</h3>
<pre><code class="klipse">
(defn create-renderer [element]
(doto (js/THREE.WebGLRenderer. #js {:canvas element :antialias true})
(.setPixelRatio js/window.devicePixelRatio)))
(defn three-canvas [attributes camera scene tick]
(let [requested-animation (atom nil)]
(reagent/create-class
{:display-name "three-canvas"
:reagent-render
(fn three-canvas-render []
[:canvas attributes])
:component-did-mount
(fn three-canvas-did-mount [this]
(let [e (reagent.dom/dom-node this)
r (create-renderer e)]
((fn animate []
(tick)
(.render r scene camera)
(reset! requested-animation (js/window.requestAnimationFrame animate))))))
:component-will-unmount
(fn [this]
(js/window.cancelAnimationFrame @requested-animation))})))
</code></pre>
<br />
This is a more involved example to show the use of a non-React JavaScript UI library.
We are using the ThreeJS library to render a scene. The important thing to look for in this example code
is the use of lifecycle methods; <code>reagent-render</code> is a component function that returns hiccup HTML,
<code>component-did-mount</code> is called when the element is mounted into the page, and
<code>component-will-unmount</code> is called just before the element leaves the page.
<br />
<br />
There are 2 interop tasks we do in our ThreeJS component:
<br />
<ol>
<li>
We start a request animation frame loop to render the scene.
But we are careful to stop the animation loop when the component is unmounted.
This will allow our component to play nicely with our page if we add and remove it.
</li>
<li>
We create a renderer that targets the DOM node after it is mounted into the page.
</li>
</ol>
Ok great, but where's our scene?
We need to construct some lights and objects to see anything interesting.
Let's make a 3D version of the concentric circles we made in SVG earlier.<br />
<br />
<h3>
Example Q: A ThreeJS version of concentric circles</h3>
<pre><code class="reagent">
(defn create-scene []
(doto (js/THREE.Scene.)
(.add (js/THREE.AmbientLight. 0x888888))
(.add (doto (js/THREE.DirectionalLight. 0xffff88 0.5)
(-> (.-position) (.set -600 300 600))))
(.add (js/THREE.AxisHelper. 50))))
(defn mesh [geometry color]
(js/THREE.SceneUtils.createMultiMaterialObject.
geometry
#js [(js/THREE.MeshBasicMaterial. #js {:color color :wireframe true})
(js/THREE.MeshLambertMaterial. #js {:color color})]))
(defn fly-around-z-axis [camera scene]
(let [t (* (js/Date.now) 0.0002)]
(doto camera
(-> (.-position) (.set (* 100 (js/Math.cos t)) (* 100 (js/Math.sin t)) 100))
(.lookAt (.-position scene)))))
(defn v3 [x y z]
(js/THREE.Vector3. x y z))
(defn lambda-3d []
(let [camera (js/THREE.PerspectiveCamera. 45 1 1 2000)
curve (js/THREE.CubicBezierCurve3.
(v3 -30 -30 10)
(v3 0 -30 10)
(v3 0 30 10)
(v3 30 30 10))
path-geometry (js/THREE.TubeGeometry. curve 20 4 8 false)
scene (doto (create-scene)
(.add
(doto (mesh (js/THREE.CylinderGeometry. 40 40 5 24) "green")
(-> (.-rotation) (.set (/ js/Math.PI 2) 0 0))))
(.add
(doto (mesh (js/THREE.CylinderGeometry. 20 20 10 24) "blue")
(-> (.-rotation) (.set (/ js/Math.PI 2) 0 0))))
(.add (mesh path-geometry "white")))
tick (fn []
(fly-around-z-axis camera scene))]
[three-canvas {:width 150 :height 150} camera scene tick]))
(defn lambda-3d-counter []
[a-better-mouse-trap [lambda-3d]])
</code></pre>
<strong><br /></strong>
Tada! We have a 3D scene.<strong><br /></strong><br />
<strong><br /></strong>
<strong>Exercise:</strong> Add some more meshes to the scene. Complete the Lambda symbol (λ) by adding a diagonal down mesh.<br />
<br />
What I find really neat is that this 3D scene composes well with our existing components, here it is inside the mouse trap:
<br />
With attention to the component lifecycle we were able to make use a library that was not designed with React or Reagent in mind. We didn't need a complicated wrapper; creating a class was the easy bit. Most of our effort was specifying the scene itself.<br />
<br />
Seeing as we created a 3D scene, let's make use of it to draw something else.
A Sierpinski 3D gasket is a recursively defined object with volume that approaches zero each step,
while the surface area remains constant. That's pretty weird huh?<br />
<br />
<h3>
Example S: Sierpinski Gasket in 3D</h3>
<pre><code class="reagent">
(def pyramid-points
[[-0.5 -0.5 0 "#63B132"] [-0.5 0.5 0 "#5881D8"] [0.5 0.5 0 "#90B4FE"] [0.5 -0.5 0 "#91DC47"] [0 0 1 "white"]])
(defn add-pyramid [scene x y z size color]
(.add scene
(doto
(let [g (js/THREE.Geometry.)]
(set! (.-vertices g)
(clj->js (for [[i j k] pyramid-points]
(v3 i j k))))
(set! (.-faces g)
(clj->js (for [[i j k] [[0 1 2] [0 2 3] [1 0 4] [2 1 4] [3 2 4] [0 3 4]]]
(js/THREE.Face3. i j k))))
(mesh g color))
(-> (.-position) (.set x y z))
(-> (.-scale) (.set size size size)))))
(defn add-pyramids [scene x y z size color]
(if (< size 4)
(add-pyramid scene x y z (* size 1.75) color)
(doseq [[i j k color] pyramid-points]
(add-pyramids scene
(+ x (* i size))
(+ y (* j size))
(+ z (* k size))
(/ size 2)
color))))
(defn gasket-3d []
(let [camera (js/THREE.PerspectiveCamera. 45 1 1 2000)
scene (doto (create-scene)
(add-pyramids 0 0 0 32 "white"))
tick (fn [] (fly-around-z-axis camera scene))]
[three-canvas {:width 640 :height 640} camera scene tick]))
</code></pre>
<div>
<br />
Suggested soundtrack for appreciating the Sierpinski gasket:<br />
<br /></div>
<iframe frameborder="0" height="100" src="https://www.youtube.com/embed/IIrCDAV3EgI" width="100%"></iframe>
<br />
<strong><br /></strong><strong><br /></strong><br />
<strong>Exercise:</strong> Can you make a tetrahedral gasket by modifying the points and faces list?
(Hint: you just need to delete one of the base points to make a triangular prism.)
How about a cubic gasket?
<br />
<br />
ClojureScript really shines in it's facilities for avoiding repetitive boilerplate.
<br />
<br />
<br />
<h2>
Lifecycle Review</h2>
<em>Twice and thrice over, as they say, good is it to repeat and review what is good. -- Plato</em>
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-f0OgCK-hwF9Q1YGTiZVlD6stzxobvEjA67b2Ob-6OYQkqp_0rbEz6JC5b7d0O44U6_Wniez_2E2udLAOkicYCQ9aY6KXt4umpH1N_wIzRL-Y4DM_cxCfWSSGI1_aloWtZDi3qA/s1600/Screen+Shot+2017-01-10+at+11.43.53+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-f0OgCK-hwF9Q1YGTiZVlD6stzxobvEjA67b2Ob-6OYQkqp_0rbEz6JC5b7d0O44U6_Wniez_2E2udLAOkicYCQ9aY6KXt4umpH1N_wIzRL-Y4DM_cxCfWSSGI1_aloWtZDi3qA/s200/Screen+Shot+2017-01-10+at+11.43.53+AM.png" width="169" /></a></div>
<br />
<br />
Most Reagent components can be expressed as a function, especially if they rely on
state being passed to them as an argument. Some components will create local state,
access the DOM node, or need to do some setup/teardown. All components have a lifecycle.
They get created, mounted into the DOM, rendered, and unmounted from the DOM.
A component potentially calls <code>render</code> many times as it's inputs change.
It remains in the DOM until unmounted.
<br />
<br />
In order to show the lifecycle in action, let's log what's happening in a form 2 component
(a function that returns a function).
<br />
<pre style="display: none;"><code class="klipse">
(def messages (reagent/atom []))
(defn log [& args]
(apply cljs.core/println args)
(swap! messages
(fn [xs]
(doall (take 10 (cons (apply str (.toLocaleTimeString (js/Date.)) "| " args) xs))))))
(defn with-log [component]
[:div
component
(into
[:ul]
(for [line @messages]
[:li line]))])
</code></pre>
<br />
<h3>
Example T: Observing the lifecycle of a puppy</h3>
<pre><code class="reagent">
(defn puppy [x]
(log "puppy created, x:" x)
(let [mouse-over? (reagent/atom false)]
(fn [y]
(log "puppy rendered, x:" x " y:" y " mouse-over?:" @mouse-over?)
[:span {:on-mouse-over (fn [e] (reset! mouse-over? true))
:on-mouse-out (fn [e] (reset! mouse-over? false))}
[:img {:src "https://i.pinimg.com/originals/dc/c1/7e/dcc17eda3b4e65e30c39c1bd1fd58abb.png"
:style {:width "150px",
:border "1px solid",
:transform (str "scale(" (if @mouse-over? 1.1 1) ")")}}]])))
(defn lifecycle-review []
(reagent/with-let [x (reagent/atom "1")]
[:div
[:label "Type in a value for x: "
[:input {:on-change (fn [e] (reset! x (.. e -target -value)))}]]
[with-log [a-better-mouse-trap [puppy @x]]]]))
</code></pre>
<br />
<strong>Pop quiz:</strong> Enter a string in the text box above and mouse over the puppy.
You should see that x and y do not match. Why are they different?
Now click "catch" to create a new puppy.
See that x now has the new value when you mouse over the new puppy,
but x still has the old value when you mouse over the old puppy.
Can you explain why?
<br />
<br />
As you can see by playing with this example, <code>puppy</code> is called once at creation,
but the function it returns is called whenever you mouse over the puppy. One trap to avoid is
forgetting to specify the inner function arguments, or giving them a different name.
I intentionally gave them different names in the above example to demonstrate that
<code>x</code> is being captured from the outer function. The captured value won't change!
However this is easily avoided if you keep the inner function arguments identical to the outer arguments.
If the arguments are identical, the inner function will not capture any of the outer bindings.
<br />
<br />
<br />
<h2>
Conclusion</h2>
<em>The pain of parting is nothing to the joy of meeting again. -- Charles Dickens</em>
<br />
<br />
<br />
We have reached the second stop of our deep dive tour.<br />
<br />
At this point we have covered the principal syntax and features of Reagent. We observed a variety of UI challenges and the forms Reagent provides to address them. Reagent's fundamental abstraction is a view component. A function that returns HTML as hiccup is a component. A function that returns a function is a component. A function that returns a Class is a component. These three forms allow us to manage lifecycle concerns such as state and DOM node interaction.<br />
<br />
Editing code in the browser itself is a great way to try out Reagent.
If you want to build some larger ideas you might find <a href="http://app.klipse.tech/?container">KLIPSE</a> useful.
If you enjoy interactive tutorials, make sure you check out the excellent articles in <a href="http://blog.klipse.tech/">the KLIPSE blog</a>.<br />
<br />
In <a href="http://timothypratley.blogspot.com/2017/01/reagent-deep-dive-part-3-sequences.html">part 3</a> of our tour we will examine the nuances of dealing with sequences of subcomponents. I hope you can join me again for that soon!<br />
<br />
<script>
window.klipse_settings = {
selector: '.klipse',
selector_reagent: '.reagent'
};
</script>
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-13776177642781308932017-01-10T20:52:00.000-08:002017-01-19T22:19:50.673-08:00Reagent deep dive part 1: Hiccup and ratoms<link href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css" rel="stylesheet" type="text/css"></link>
<script src="https://ajax.googleapis.com/ajax/libs/threejs/r83/three.min.js"></script>
<style>
body { background-color: #eeeeee; }
pre, code { font-size: 16px; background-color: white; }
</style>
<br />
Today I invite you to embark with me on the grand tour of
<a href="https://github.com/reagent-project/reagent">Reagent</a>, a ClojureScript library for building web pages.
I will be encouraging you to try several small exercises on this page as we go.
You can change the example code provided on this page to make it do other stuff.
Let me know in the comments if you get stuck at all, or have any questions.
This tour is going to get down into the nitty gritty of everything UI,
including the kitchen sink. I will do my best to keep things interesting.
<br />
<br />
<h2>
Setup</h2>
<em>To get good at finishing, you first need to get good at starting. -- Unknown</em>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG6JnJpz1e028ISVBduKiMUEKUkat8XY_cKwoJSwK-6j3xoPpqvCftHyMg1QgNeQZ7WgtO3yKlFkC7ZBAsikpfoNRSkR-hEyvGa3P5w7pSuVp29sNOnN6q1znPYLjY5i68xiJnJQ/s1600/Screen+Shot+2017-01-10+at+11.39.33+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="163" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG6JnJpz1e028ISVBduKiMUEKUkat8XY_cKwoJSwK-6j3xoPpqvCftHyMg1QgNeQZ7WgtO3yKlFkC7ZBAsikpfoNRSkR-hEyvGa3P5w7pSuVp29sNOnN6q1znPYLjY5i68xiJnJQ/s200/Screen+Shot+2017-01-10+at+11.39.33+AM.png" width="200" /></a></div>
<br />
<br />
To use Reagent in this page we require <code>reagent.core</code> in our namespace declaration.
Typically there will be a call to <code>reagent/render-component</code> to start the rendering process.
<br />
<br />
<pre style="display: none;"><code class="klipse">
(ns my.reagent-examples
(:require
[reagent.core :as reagent]
[reagent.ratom]))
(enable-console-print!)
</code></pre>
<pre><code>(ns my.reagent-examples
(:require [reagent.core :as reagent]))
(when-let [element (js/document.getElementById "app")]
(reagent/render-component [component] element)))
</code></pre>
<br />
Reagent works by rendering HTML to an existing element in the page.
So a Reagent app consists of a bare bones HTML page (index.html) which contains an element suitable for replacement.
Typically this will be something like<br />
<code><div id="app"></div></code><br />
<br />
To start the rendering process we call <code>reagent/render-component</code> with a component and the target div.
<br />
<br />
That's it for setup, we are ready to rock and roll!
<br />
<br />
<h2>
A Reagent component is a function</h2>
<em>True success has more components than one sentence or idea can contain. -- Zig Ziglar</em>
<br />
<br />
The central abstraction Reagent provides for building views is the component.
A component is a function that produces some HTML, and components can be nested to build a full application.
A Reagent component is a function that returns hiccup.
Hiccup is a concise way to express HTML as data. In HTML you express a paragraph with the <code>p</code> tag:
<br />
<br />
<pre><code><p>Hello world</p></code></pre>
<br />
The hiccup equivalent is:
<br />
<br />
<pre><code>[:p "Hello world"]</code></pre>
<br />
Let's write our first component.
<br />
<br />
<h3>
Example A: The "hello world" Reagent component</h3>
<pre><code class="reagent">
(defn greetings []
[:p "Hello world"])
</code></pre>
<br />
To create a component we define a function that returns a vector where the first element of the vector is a HTML tag keyword.
<br />
<strong><br /></strong>
<strong>Exercise:</strong>
Change Example A to return a heading <code><h1></code> tag with your name in it.
You can edit the examples in this page (thanks to <a href="https://github.com/viebel/klipse">KLIPSE</a>).
Simply modify the code in the example boxes to complete the exercises.
<br />
<br />
<h2>
More about hiccup</h2>
<em>I think hiccup cures were really invented for the amusement of the patient's friends. -- Bill Watterson</em>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4_23ndVnnliJtB5Fs6gowuj2_UJgNSWUABM4cRA8IP68OmxVY3Q03woOdItmO_qJZVMOcOCB3c5Cn9C-4fPOaPZYIrld6pqe-FJ__C8d7LuOQrvUgGLoW9qwvGuoANqCsQQARgg/s1600/Screen+Shot+2017-01-10+at+1.32.44+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="175" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4_23ndVnnliJtB5Fs6gowuj2_UJgNSWUABM4cRA8IP68OmxVY3Q03woOdItmO_qJZVMOcOCB3c5Cn9C-4fPOaPZYIrld6pqe-FJ__C8d7LuOQrvUgGLoW9qwvGuoANqCsQQARgg/s200/Screen+Shot+2017-01-10+at+1.32.44+PM.png" width="200" /></a></div>
<br />
<br />
HTML tags can contain attributes. For instance an image source is expressed as an attribute:
<br />
<pre><code><img src="https://avatars1.githubusercontent.com/u/9254615?v=3&s=100"></code></pre>
In hiccup, we express attributes as an optional map as the second element in a hiccup vector:
<br />
<br />
<h3>
Example B: An image tag with a source attribute</h3>
<pre><code class="reagent">
[:img
{:src "https://avatars1.githubusercontent.com/u/9254615?v=3&s=150"}]
</code></pre>
<strong><br /></strong>
<strong>Pop quiz:</strong>
What is the hiccup equivalent of<br />
<code><a href="https://github.com/reagent-project/reagent">Reagent</a></code>?
<br />
<br />
You can nest elements in the same way that you would nest HTML tags:
<br />
<br />
<pre><code>[:div
[:div "Hello world"]]</code></pre>
<strong><br /></strong>
<strong>Tip:</strong> <code>[[:div] [:div]]</code> is not a valid Reagent component.
The first element of a Reagent component must be a tag identifier or component.
If you want to return 2 divs, you need to wrap them in a parent div:
<br />
<br />
<pre><code>[:div
[:div]
[:div]]</code></pre>
<br />
Just like other attributes, you can specify handler attributes such as <code>onClick</code>.
The Clojure kebab-style <code>:on-click</code> is automatically translated to camelCase <code>onClick</code>
for convenience.
<br />
<br />
<h3>
Example C: A clickable button</h3>
<pre><code class="reagent">
[:button
{:on-click
(fn [e]
(js/alert "You pressed the button!"))}
"Do not press"]</code></pre>
<br />
Styles attributes are expressed as a map.
<br />
<br />
<pre><code><p style="color: red; background: lightblue;">Such style!</p></code></pre>
<br />
is written in hiccup as:
<br />
<br />
<h3>
Example D: Inline styles</h3>
<pre><code class="reagent">[:p
{:style {:color "white"
:background "darkblue"}}
"Such style!"]
</code></pre>
<br />
There is a shorthand for expressing class and id.
<br />
<br />
<pre><code>[:div#my-id.my-class1.my-class2]</code></pre>
is equivalent to:
<br />
<pre><code>[:div
{:id "my-id"
:className "my-class1 my-class2"}]</code></pre>
<br />
Generally attributes map to exactly what you would expect from HTML...
but it is important to understand that <code>onclick</code> (no hypen or camelCase) will not work.
Attributes are mapped according to the React specification of attributes.
If you are having trouble describing an attribute,
check <a href="https://facebook.github.io/react/docs/dom-elements.html">the React reference page</a>
to make sure you are using the appropriate name and hyphenation.
That page lists all the possible attributes you can use.
<br />
<br />
There is also an escape hatch <code>dangerouslySetHtml</code>. You won't need it anytime soon, but it's good to know that it exists.
If there is a mapping not covered by React, or you need to render a HTML template,
you can use the escape hatch to write HTML from text instead of hiccup.
<br />
<br />
Let's use our knowledge to construct a more complicated component using SVG HTML elements.
All of this is regular HTML.<br />
<br />
<h3>
Example E: A more complicated component using SVG tags</h3>
<pre><code class="reagent">
(defn concentric-circles []
[:svg {:style {:border "1px solid"
:background "white"
:width "150px"
:height "150px"}}
[:circle {:r 50, :cx 75, :cy 75, :fill "green"}]
[:circle {:r 25, :cx 75, :cy 75, :fill "blue"}]
[:path {:stroke-width 12
:stroke "white"
:fill "none"
:d "M 30,40 C 100,40 50,110 120,110"}]])
</code></pre>
<br />
As you can see we created an SVG element containing two concentric circles, and a path stroke through them.
One important thing to point out here is that our function is returning a data structure.
<br />
<strong><br /></strong>
<strong>Exercise:</strong> Complete the Lambda symbol (λ) by adding a diagonal down path.<br />
You can find hints in the <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg">SVG Reference.</a><br />
<br />
Naturally you can also do all the boring form input related stuff too *yawn*.<br />
<br />
<h3>
Example F: A tiny form for form's sake</h3>
<pre><code class="reagent">
[:div
[:h3 "Greetings human"]
[:form
{:on-submit
(fn [e]
(.preventDefault e)
(js/alert
(str "You said: " (.. e -target -elements -message -value))))}
[:label
"Say something:"
[:input
{:name "message"
:type "text"
:default-value "Hello"}]]
[:input {:type "submit"}]]]
</code></pre>
<br />
This is all just regular HTML represented in hiccup syntax.
<br />
<strong><br /></strong>
<strong>Exercise:</strong> Add a select options list to this form containing your three favorite words.<br />
Hint: The HTML would look like <code><select><option>Donut</option></select></code>
<br />
<br />
<br />
<h2>
Nesting components</h2>
<em>What is a fish without a river? What is a bird without a tree to nest in? -- Jay Inslee</em>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6WXow8eDtM3x5dtkBetqIne1YV1P39C3nPiSS5yXrAu8GPAlBH2mko-cZygwO6kaLOXbF14U1FY88-4daPnSupLt5drMFqEF0y3yrut3jWAS1nXhTkZ-pzk7rFKZXi0VOdRttIw/s1600/Screen+Shot+2017-01-05+at+11.47.12+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="159" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6WXow8eDtM3x5dtkBetqIne1YV1P39C3nPiSS5yXrAu8GPAlBH2mko-cZygwO6kaLOXbF14U1FY88-4daPnSupLt5drMFqEF0y3yrut3jWAS1nXhTkZ-pzk7rFKZXi0VOdRttIw/s200/Screen+Shot+2017-01-05+at+11.47.12+AM.png" width="200" /></a></div>
<em><br /></em>
<em><span id="goog_1298535578"></span><span id="goog_1298535579"></span><br /></em>
A Reagent component is a function, so you <em>can</em> call it directly and it will return a result:
<br />
<br />
<pre><code>(greetings)</code></pre>
<br />
But, for reasons that will become apparent, we do not call components directly.
Instead we nest components, in the same way that we nest hiccup forms:
<br />
<br />
<pre><code>(defn greet2 [message]
[:div [greetings]])
</code></pre>
<br />
The only visible difference between calling a component and nesting a component
is that we surround it in square braces instead of round parenthesis.
<br />
<br />
<h3>
Example G: An SVG component that nests another component</h3>
<pre><code class="reagent">
(defn many-circles []
(into
[:svg {:style {:border "1px solid"
:background "white"
:width "600px"
:height "600px"}}]
(for [i (range 12)]
[:g
{:transform (str
"translate(300,300) "
"rotate(" (* i 30) ") "
"translate(100)")}
[concentric-circles]])))
</code></pre>
<br />
Here we make use of our previous component.
We put 12 instances of <code>concentric-circle</code> into an SVG.
<br />
<br />
<strong>Exercise:</strong> Redefine <code>concentric-circle</code> to return a <code>g</code>
element instead of an <code>svg</code>. Add an argument to take the rotation as an input.
Don't forget to pass <code>i</code> to concentric-circles.
Refactoring and composing components should feel very familiar, it's the same thing we do with any other function.
Functions are a convenient way to organize view modularity to suit your tastes.
<br />
<br />
But why do we use braces instead of parenthesis? You ask a good question... The reason is efficiency.
The React philosophy is that views are functions of their inputs,
and that view need only be re-rendered when their inputs change.
If the input arguments of a function do not change, the result is the same, so there is no point calling it.
We only need to call a view component if the inputs it relies on have changed.
<br />
<br />
If our components called sub components directly, it would force them to always compute a result.
We don't want that. Instead we leave the task of calling the component up to Reagent.
Reagent will figure out when it needs to evaluate a component.
Our job is strictly to specify the structure of the view, which we do by returning a vector.
The vector we return contains the sub components, but is not forcing evaluation of them.
<br />
<br />
<strong>Tip:</strong>
Remember to use <code>[component argument]</code> instead of <code>(component argument)</code>
when nesting components.
<br />
<br />
<br />
<h2>
Doing stuff!</h2>
<em>
Success is no accident. It is hard work, perseverance, learning, studying, sacrifice and most of all,
love of what you are doing or learning to do. -- Pele
</em>
<br />
<em><br /></em>
<em><br /></em>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY_92wgtBjWCeN04EPuDDOAuc2l5vZ5S-Q4rF6r7MKPbnv7YSk1v5ojLGsllrVKc8N36vg3mnU-WXVRl-UU7QjAoe2BiadKgH4JcHfGoTmDDXLRzpYumxhVSAKdrGFGC1Kvqla2g/s1600/Screen+Shot+2017-01-05+at+11.47.51+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY_92wgtBjWCeN04EPuDDOAuc2l5vZ5S-Q4rF6r7MKPbnv7YSk1v5ojLGsllrVKc8N36vg3mnU-WXVRl-UU7QjAoe2BiadKgH4JcHfGoTmDDXLRzpYumxhVSAKdrGFGC1Kvqla2g/s200/Screen+Shot+2017-01-05+at+11.47.51+AM.png" width="200" /></a></div>
<em><br /></em>
<em><br /></em>
We want our webpage to respond to user interaction and change.
We need two things to achieve change:
<br />
<ol>
<li>Inputs to our components.</li>
<li>Something to watch and react to.</li>
</ol>
Component inputs are just regular function inputs.
The new thing that Reagent introduces is the thing to watch and react to;
the <code>reagent/atom</code>.
<br />
<br />
Reagent atoms behave very much like a regular Clojure <code>atom</code>.
You change them with <code>swap!</code> or <code>reset!</code> and you get their value by deref <code>@my-atom</code>.
The special thing about a <code>reagent/atom</code> is that all components that deref it
will be re-rendered whenever the value held by the <code>reagent/atom</code> changes.
<br />
<br />
<h3>
Example H: A counter component that re-renders on change</h3>
<pre><code class="reagent">
(def c
(reagent/atom 1))
(defn counter []
[:div
[:div "Current counter value: " @c]
[:button
{:disabled (>= @c 4)
:on-click
(fn clicked [e]
(swap! c inc))}
"inc"]
[:button
{:disabled (<= @c 1)
:on-click
(fn clicked [e]
(swap! c dec))}
"dec"]
(into [:div] (repeat @c [concentric-circles]))])
</code></pre>
<br />
When we click the button, the value of <code>counter</code> is incremented,
causing the <code>counter-component</code> to re-render.
We don't have to do anything special to get this behavior.
Our function derefs the counter, so Reagent knows that it needs to re-render this component
whenever <code>counter</code> changes.
<br />
<br />
<strong>Tip:</strong>
Be careful to make sure you are using a <code>reagent/atom</code>, not a regular <code>atom</code>...
A regular <code>atom</code> will not cause components to re-render.
<br />
<br />
We can also write conditional code.<br />
<br />
<h3>
Example I: Rendering different HTML elements with conditional logic</h3>
<pre><code class="reagent">
(let [show? (reagent/atom false)]
(fn waldo []
[:div
(if @show?
[:div
[:h3 "You found me!"]
[:img
{:src "https://goo.gl/EzvMNp"
:style {:height "320px"}}]]
[:div
[:h3 "Where are you now?"]
[:img
{:src "https://i.ytimg.com/vi/HKMlPDwmTYM/maxresdefault.jpg"
:style {:height "320px"}}]])
[:button
{:on-click
(fn [e]
(swap! show? not))}
(if @show? "reset" "search")]]))
</code></pre>
<br />
Data is very flexible. One of the big wins here is that we can construct data with conditionals.
We don't need an explicit template, we have all the power of Clojure to build and manipulate data.
<br />
<br />
<br />
<h2>
Reactions</h2>
<em>A positive attitude causes a chain reaction of positive thoughts, events and outcomes.
It is a catalyst and it sparks extraordinary results. -- Wade Boggs</em>
<br />
<br />
<br />
Reactions are really quite amazing. Reactions define a <code>reagent/atom</code>
like thing as an expression. It will fire updates when any reactive value it depends on changes.
<br />
<br />
<h3>
Example J: Sorting as a reaction</h3>
<pre><code class="reagent">
(def rolls (reagent/atom [1 2 3 4]))
(def sorted-rolls (reagent.ratom/reaction (sort @rolls)))
(defn sorted-d20 []
[:div
[:button {:on-click (fn [e] (swap! rolls conj (rand-int 20)))} "Roll!"]
[:p (pr-str @sorted-rolls)]
[:p (pr-str (reverse @sorted-rolls))]])
</code></pre>
<br />
Here we use a reaction that depends on rolls, which calculates a new value; the sorted rolls.
We make use of <code>sorted-rolls</code> twice, but the sort is only computed once each time
<code>rolls</code> changes. Reactions can depend on multiple things. They are a useful
mechanism for defining a data flow efficiently.
They are a convenient way to define data transforms that rely on multiple sources,
or that will be used in multiple contexts.
<br />
<br />
Reactions are elegant to use in small quantities.
The drawback of using reactions everywhere is that too many of them
can become an unsightly mess of boilerplate.
My rule of thumb is to use them in moderation where there is a clear performance or expressive advantage.
<br />
<br />
This leads us to a somewhat abstract consideration.
If we structure our application with a single large global <code>reagent/atom</code>,
it may be updated from multiple sources. We wouldn't want every component updated
whenever any unrelated change occurred.
<br />
<br />
Reagent offers several answers to the question of how to organize code to react to application state.
Reagent has reactions, cursors, and track. I'll not cover those here beyond our discussion of reactions,
because I see them as situationally useful but not generally applicable.
<br />
<br />
For handling large application state, a well thought out and popular approach is to use
<a href="https://github.com/Day8/re-frame">re-frame</a>.
You should definitely read the re-frame documentation. It provides an in depth treatment of
data flow in a reactive application.
<br />
<br />
The last thing I will say on this topic is that you can go a long way with the humble <code>reagent/atom</code>.
So get building and don't over think it.
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnSk6ZWPuI25dwNlSd7VXC-c3XNxkTdhlD69TuNlmFFLFtmDkNvXsZJ3kyjP3vw1d7-IBm9TQac6-gBusMubW90nY_q4Dr3nxIKGpjBkNe8b9RmJsynmK5H9y8QIOrJOZryU_e3w/s1600/Screen+Shot+2017-01-10+at+10.55.59+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnSk6ZWPuI25dwNlSd7VXC-c3XNxkTdhlD69TuNlmFFLFtmDkNvXsZJ3kyjP3vw1d7-IBm9TQac6-gBusMubW90nY_q4Dr3nxIKGpjBkNe8b9RmJsynmK5H9y8QIOrJOZryU_e3w/s200/Screen+Shot+2017-01-10+at+10.55.59+AM.png" width="199" /></a></div>
<br />
<br />
<h2>
Conclusion</h2>
<em>Good night, good night! Parting is such sweet sorrow, that I shall say good night till it be morrow.
-- William Shakespeare</em>
<br />
<br />
<br />
We have reached the first stop of our tour!
We know how to build Reagent components and effect change to our application.
We learnt that Reagent's fundamental abstraction is a view component.
A view component is a function that returns HTML as hiccup.
The mechanism for observing and effecting change is the <code>reagent/atom</code>.
<br />
<br />
In <a href="http://timothypratley.blogspot.com/2017/01/reagent-deep-dive-part-2-lifecycle-of.html">part 2</a> of the tour we shall examine the lifecycle of a Reagent component, and see how that enables us to build a 3D Sierpinski Gasket.<br />
<br />
<script>
window.klipse_settings = {
selector: '.klipse',
selector_reagent: '.reagent'
};
</script>
<script src="https://storage.googleapis.com/app.klipse.tech/plugin/js/klipse_plugin.js"></script>Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-53062705343976138902016-12-05T11:47:00.000-08:002016-12-05T11:47:06.036-08:00The hallway track of Clojure Conj 2016 in Austin<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="margin-left: 1em; margin-right: 1em;">
<br /></div>
<br />
<br />
<br />
The Clojure <strike>Barbecue</strike> Conj 2016 in Austin:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgccySaWsgcZvmwUScTihehndk8o2i0WNHkOCwC0NRYYMEUFcuxHq8O_GOVBpdWHWawyU8DQVIGzDjCjz_MCozptMi1clZsv7ZXWEkueaOeT5CYzwPWpu9SEfPwRLYgsWhCiktTYw/s1600/rich-bbq.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgccySaWsgcZvmwUScTihehndk8o2i0WNHkOCwC0NRYYMEUFcuxHq8O_GOVBpdWHWawyU8DQVIGzDjCjz_MCozptMi1clZsv7ZXWEkueaOeT5CYzwPWpu9SEfPwRLYgsWhCiktTYw/s1600/rich-bbq.png" /></a><br />
<br />
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">This year the Conj was held in Austin, which was an excellent venue. It’s warm! I only needed a light jacket and jeans outside. Austin has great eats, especially the barbecue. Everybody was chowing down on some good ol Texas ribs. There were plenty of nearby eating options for quick meals or longer sit ins. The airport is small and easy, as was land transport.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<span id="docs-internal-guid-758d8e19-d06c-a7cb-710e-e60ce9801818"></span><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I spoke to about 50 people out of an attendance of about 500. Below are my notes, I hope they provide some insight into what is happening in the Clojure community.</span></div>
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiASGDqR7NyhrbBA8fxz_At3j1K18b5qGr6WL1F34V4p_-it-Edk4v9FHjRo9ZI7H4C3o8rdJG7xmZLqTDWnmQ329czo7yqbYZ3d4EfnAcBJHkRy0GdWCzdDs6wJdgth-0rFjGohw/s1600/IMAG0581.jpg" imageanchor="1"><img border="0" height="476" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiASGDqR7NyhrbBA8fxz_At3j1K18b5qGr6WL1F34V4p_-it-Edk4v9FHjRo9ZI7H4C3o8rdJG7xmZLqTDWnmQ329czo7yqbYZ3d4EfnAcBJHkRy0GdWCzdDs6wJdgth-0rFjGohw/s640/IMAG0581.jpg" width="640" /></a><br />
<br />
<br />
<br />
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Tomas: Math professor teaching computer science using Firestone (Online fantasy battle card game) in Clojure. Likes using inline tests attached as metadata to the function. Uses a custom macro </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">is=</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> to avoid empty tests.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Len: Using Clojure for military readiness assessment.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Milt: Yet Analytics recently hired some new recruits. Is using Web Workers as the owner of Reframe state. This avoids UI freezes when processing large communications to and from the server. Web Workers from ClojureScript has been quite difficult, but he has some changes to ClojureScript and Figwheel on the boil which will make this very smooth. Web Workers will be very useful to me in a couple of projects. He outlined his approach as follows:</span></div>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Bootstrap: sets up the main <-> worker handlers</-></span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sente events -> reframe (transit)</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Separate build (to not access document which is not present in WebWorkers)</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Reframe middleware (inteceptor) to check data shape (goes away when using Spec function instrumentation)</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Interceptor to send back to the main process.</span></div>
</li>
</ul>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Client <-> Worker <-> Reframe <-> Sente <-> Server</-></-></-></-></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<a href="https://github.com/yetanalytics/figwheel-worker-example" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://github.com/yetanalytics/figwheel-worker-example</span></a></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<a href="https://github.com/bhauman/lein-figwheel/wiki/Using-Figwheel-with-Web-Workers" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://github.com/bhauman/lein-figwheel/wiki/Using-Figwheel-with-Web-Workers</span></a></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I’m really excited about this… it fits beautifully with ‘the way’... keeps the UI responsive… sweet.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Fashion: Rich was rockin a purple shirt, shades and regulation black dancing shoes. Alex Miller surprised nobody, going with his standard plaid shortsleeve. Beards are still in. Longer beards especially. Also popular are sharp undercuts.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Paula: Building Naga; a rules engine for RDF/Datomic used at Cisco (and other places).</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Stu: Datomic ETL - </span><span style="font-family: Arial; font-size: 14.6667px; white-space: pre-wrap;">Build a function pipeline. The steps don’t matter. They will change.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Clojure provides system generality. Spec gives specificity when you want it.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Presented a useful </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">conform!</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> helper and </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">halt-when</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> for errors.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">DB -> Extractor -> Entities -> Spec -> Transform -> Transactions -> Loader -> Datomic</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sivram: Using Clojure for clinical rules/data</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Nick Bailey: Developer happiness, maintainability. Clojure fits our needs, is a recruitment tool, inspires passion, and is picked up quickly. Does Java interop between Jython and Clojure</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Vijay - Using ClojureScript for HBO client side work.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Most people I met at this Conj were already using Clojure/ClojureScript professionally.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Schmüdde: Composing a small number of primitive colors (select a pallet) and allow some mixing. What are my constraints? Experiment within the constraints using generative approach. Adjust the constraints.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jason: Proto REPL is a new Atom Clojure integration. Atom can bring up Chrome dev tools access to the DOM (modifiable for visualizations). Use (save x) to collect values (or Sayid). Reformat data for display. Has chart mappings and graphs (what is the underlying lib? looks slick). Made by bringing data centric components together.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jacob: Using Clojure for ride share and private car rental in Denmark.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">lvh: Use JNR for high speed low level library wrapping. Interface as data to support various type combos. Macro elides repeatitiion.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Bill: Sayid is an omniscient debugger. Tracing that collects the values and net time in the entire call graph. Defined println debugging as “sprinkling”. Requires some thought about what to trace. Inspires us to realize a better debugging story. Looks quite editor/REPL integration heavy, I wonder if there is a more Refresh based approach that works. Might be as easy as having a subordinate ProtoREPL running? You should watch this talk for sure.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Rich: Spec-ulation. This is what we are all here for, and Rich delivered! Absolutely can’t miss this talk, watch it now. Breaking changes are broken. Don’t break!! Don’t! Our community is a positive one. Accret. Spec: you can use it, it’s here to stay. The problem is the code is going to execute against some version, but if you have transitive dependencies on multiple versions you can not control which version it will be running. Dependencies are really call dependencies. Level violation. fns call by name. ns requires. artifact context. MAGIC. Coarse grained false security.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">What is required? fn args, ns var names, artifacts ns/package names/paths</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Provided? fn returns, ns vars/fns, artifacts ns/packages</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Accretion, provide more. Relaxation, require less. Fixation, bash bugs. Flip any => Break. Either grew or broke. Nuance: does not apply to private development). Adding is growth, removing is breakage.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: Arial; font-size: 14.6667px; white-space: pre-wrap;">Code for growth. </span><span style="font-family: Arial; font-size: 14.6667px; white-space: pre-wrap;">Open specs and data formats. Define what you can do, not what you can’t. Prohibition turns growth into break, so don’t prohibit. Presume you may get more. During development Alpha is O.K. or artifact id. Not an excuse to not promise (release it at some point please).</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The only truth is the runtime. </span><span style="font-family: Arial; font-size: 14.6667px; white-space: pre-wrap;">Think twice about requiring more or providing less.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Same story for web services.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">What if we never broke? Can alway use latest. Spec can help - diff check for removing provides or additional requiring. SHA tested (per fn) Turn what would have been breaking into accretion.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s be the community to make it great.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sean: Recommended 1st Robotics for children wanting to learn to program, and lego Audrino.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">A recurring question that came up (originally in the unsession):</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">How can programmers help scientists? Lack of basic software engineering prevalent in scientific work (change log, version control, tests, documentation, bus factor). Root cause is a continuation problem.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Scientist/programmer pair matchmaking? Book for scientists?</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“You are in the Software business!”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Rich: We can’t really solve the continuation problem for science because we haven’t solved it for software engineering either; we don’t know how to pass software ownership onward (especially true for short time spans). We can build better tooling for scientists (and ourselves).</span></div>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Build better tools for science</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Clojure and Datomic offer reproducibility [this is huge for science; like git for science]</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Core.matrix is wonderful and can handle the numeric needs of scientists</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Scientists tool needs aren’t specific to themselves</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Can’t fix a cultural problem with tech</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There are no incentives for good software practices in science</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Convert PySci to Clojure?</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Build cool stuff and show scientists! Show them how Clojure can help them</span></div>
</li>
</ul>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Happy to see so many happy Clojure users.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I don’t make uberjars. For deployment, create 2 artifacts; code and runtime. Use IFS .m2, services all point to the same one. Git repo of company lib-> version which is updated when CI passes. Depend on the name not the version. Depend on code. Services depend on the lib not a specific version. I’d rather have to test that all my company code can go to a new version than have no idea what code is executing in a whole bunch of sub contexts. CI enables this. Test per git SHA can save build times and enable generative testing. AWS CodeDeploy works well for managing the flow of change, test, deploy of the code.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We’ve forgotten how make works, let’s get some of that back. Slow build times are due to getting artifacts you don’t even need, want, and might not even be what your code asked for… and running tests you’ve already run. Stop doing that. [Rich described the build and deploy system that I want consisting of simple and effective tooling choices. I hope someone from Cognitect will blog about their build system soon, it would be a valuable reference path forward along the themes of his talk.]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Simulation of national exit poll has tonnes of data. The whole election runs in one night. Have to do lots of pretend elections. Very bursty data. Was a greenfield rewrite.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Dorian: Making a custom flash cards mobile app in Clojure to learn Clojure while he learns Clojure. Cool idea! I'm going to copy this idea and do the same :)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jacob: Using Clojure to optimize third party web service access. Measuring the effects of parallelism and chunking. Brainstormed about what sort of problems are good for learning things like core.async. Game min/max, solve the same problem using different tech; make a parachute that doesn’t break the egg.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Benson: The secret to understanding macros is remembering that they happen between the R and E of R^EPL. I found this tip very insightful, thanks for sharing it with me.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Conor: Is building tools for collective intelligence in Clojure. Came to Clojure from Datomic. Runs a research group of people interested in a variety of topics. Found people interested in research groups for different topics like parenting, homeschooling, and writing.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Currently working on a framework for knowledge mapping in Clojure/Datomic. Thinking about how to structure a debate style way to build arguments for and against the truth of statements. Also working on a different approach to time management: schedule your fun time first. Block out time for things that do take time. How much time do you want to allocate to what? Reverse scheduling works better than planning goals.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">People in Clojure are self selecting to be helpful enthusiasts. Being helpful trumps skills/knowledge, and our community is brimming with helpfulness. Clojure has core values, a story, a nationhood. People choose to come to the Conj, to be part of a beautiful autocracy. A humble intelligence to choose to remove incidental complexity. Our uniting enemy is mutability. The constitution of nation Clojure is strong. There is trust in libraries, the thought behind them, the language, and ecosystem. This sort of nationalism can dissolve our differences. Recruit how Christians recruit. Put yourself out there, in places you are uncomfortable, work with new people for their strengths.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Eve has shown that you get a more friendly UX when you allow junk in between.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Chris (aka Oakmac): Using ClojureScript for threat detection at Cisco. It’s critical to solve the right problem. You and your team can do a fantastic job, build a polished feature, and it doesn’t mean anything if the user needed bulk load but you build form input validation. Discussed the pros and cons of kata coding interviews.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Adam: Interested in the effect of communication of requirements as teams scale, and how it breaks down as the distance of the programmer from the user grows. Sees much potential in Eve.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Colin: Business is doing great. Sustained growth. Working on many very useful Cursive integrations. Look out for figwheel click through to source soon! Is also taking more interest in refresh based workflows, which I am pleased to hear :) Thinking about how to provide a WallabyJS experience for ClojureScript in Cursive. Interested in taking the Sayid debug/trace approach. life is complicated trying to support clojurescript and spec. Working with Bruce to get figwheel integration (click through line numbers to source, serve out of Cursive, detect dependency changes from the code not the files).</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Pretty much everyone was hiring and lots of people were looking. But there wasn’t a recruiting frenzy, quite the opposite. Most people were there for the Conj, to share ideas, build contacts, and enjoy Austin.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Quite a few startups are betting on Clojure: Jack (Ladder), Nikko & Claire (SparkFund), Kim (Reify Health), Rusty (Outpace).</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Adam and Matt: Using Datomic to build wormbase.org for curation and query of papers about genome differences in worms. An over abundance of oversight is preventing extension of the solution to wider genomic databases (other animals). Alliance of Genomics is 10k researchers, and they need this tool. They also need to be able to annotate changes online with attribution. Scientists need a social network for collaboration. A gardening tool for building their own content and suggesting edits to others.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Paul: Pamela is a Lisp for space exploration with machine learning capabilities. A modeling language. In space you need to handle the unexpected. ML with real world interfaces. Useful for discovering the real parameters of a system, and handling transitions (bounciness when you stop a car). Learn the bounds of weird stuff.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Chris (aka Chouser): Can we apply Pamela to modeling our software systems to learn the bounds of our timeouts, performance, etc that we log? Working on codename Murkwood; an alternative to Communicating Sequential Processes and Continuation Passing more similar to Sagas… Run the code multiple times idempotently. Identify side-effecty, pausy, errory stuff that has happened or not yet.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jarret: Pointed me to @adamtanknow sketches of the conj speakers and notes, very cool! I hope to emulate your artistic layouts and flair one day :)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Christophe: Built Powderkeg; a data glue approach to Spark in Clojure.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Eric: You can’t refactor Aristotle into Newton. Recommended reading: Conal Elliot’s Denotational Design. His approach to modeling is physical metaphor (know your domain) -> construct meaning (know your constructs) -> implementation (know your refactoring). Has been making instructional Clojure videos for three years.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Mike: Clara Rules if/then in a declarative fashion. Declared with bindings. Implicit storage of shared intermediate state. Information passing. General. Light weight. Rules have a first class data model. Dispatch is plugable. Can have a durability layer. See Ryan Bush videos. Supporting facts can be chained in metadata.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There were lots of healthcare companies at the Conj this year (Devin - Healthfinch, Kim - Reify Health, Mike - Cerner, Peter - Diligent, Jack - Ladder) and rules were a core competency for most of them.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Devin: Thinking about ways to filter rule information effectively to provide the answer to “Why did you recommend this?” in the context of 5k rules. Need to flag important ones and save the decisions for attribution and explanation.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Great to see and have personal conversations with colleagues past and present… Jeff, Tanya, Chris, Paul, Dan, Rusty, Vincent, Matt, Devin, Alex. I really wish I had spent more time with you instead of mingling. My philosophy on conferences is your there to get ideas and meet new people so I intentionally sit next to someone I've never met/seen before as much as possible. But the downside is I don't get to hang out with the gang, which I regret now. I hope to catch up with you again soon.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Nikko: Wants to make a difference in improving our world. Contracts are crazy, there is no precise meaning, it is just two parties agreeing that they each separately think they know what it means. If there is a dispute it goes to a judge to flip a coin. We can do better! We can make funding better. We can build a better financial foundation for our world. Building SparkFund with Clojure and Datomic.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">@apogsasis: Composing music with Clojure Spec. Lots of overlap with Steve’s Juggling. Juggling throws look very much like drum beats. Numeric encoding and rules. Computer assisted constraint and solution space exploration frees us up to focus on more nuanced and skilled tasks.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Gladfly: McDonalds beef patties turned me vegan!</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Andrea: Working on bootstrap Clojure (Clojure in Clojure) and it’s many applications such as ClojureScript browser REPLs.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Alex Miller: Was fielding a tonne of spec questions.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jack: Proud of the build tool Ladder build because it was painfully missing. </span><a href="https://github.com/ladderlife/loonie" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://github.com/ladderlife/loonie</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> Solved a real friction point for his team. Making a big bet on Clojure being the best way to run a startup. Pleased with the language and people, but build/deploy was a sore point (so they addressed it).</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Chris: Using Clojure at Rackspace. Maintains a Clojure security lib.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Brett: Using Clojure in the mortgage industry (Guaranteed Rate). Found it easy to adapt logic to what a loan officer would do, but the trick is seeing if the end effect is the desired one. Works remotely (most people I talked to worked remotely). Enjoyed the Austin food.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">William: Barliman 4.3 is out! Write the test, generate the code, “prove me wrong”. We’ve seen this concept developing, it is reaching a really exciting stage where it can do quite a lot, and is compelling as a programming tool. Can fill in the last X % of code. Can detect “no function can pass this test with the current structure” on partial code. Proof checking. Find logical flaws before you even code.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Seems perfect for data transformations (and we write many of those reshaping fns in Clojure). I would rather write the spec than the code. Also useful for refactoring (discovering better solutions). I highly recommend you watch this talk.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Kim: Is using Clojure to facilitate Clinical trials (Reify Health). Good groups have Talkers, followers, mediators, leaders. +ve influences, -ve influence. Need a balance. Effect is contextual. Multipliers.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Milli: Teaching bootcamps. Gave me some very helpful insights on group formation techniques she uses. Does a personality test and finds that helpful in giving people some context on how to work with each other.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Riley: Studying, learning functional programming.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Rangel : Building Native ClojureScript app. Looks fantastic and it is fast.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Mark: Lots of Aussies getting into Clojure.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<span id="docs-internal-guid-758d8e19-d06d-76b7-5aeb-2805110c6d6a"><span style="font-family: Arial; font-size: 14.6667px; vertical-align: baseline; white-space: pre-wrap;">I hope that gives you a peek into the vibe of the conference and some of the hallway conversations going on. Massive thanks to Lynn and all the organizers, I had a tonne of fun.</span></span><br />
<br />
<br />
Below are some fun sketches I made during the talks, I'm still a beginner but thought they might give you a laugh to see some amateur caricaturization:<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJWiwX5lWCKjtjpSxH6gcQxqYAsEq2DkXOwSkmf5WZ2olwENOdPYWWKXgBDQYtYNlvJfMlvoKLtKZQi9UvaNl7vf0_gpNFAvjSiO_mmO6BHvyiLw5TTO1R532Gg0e_j5AEeuaHwQ/s1600/IMAG0580.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJWiwX5lWCKjtjpSxH6gcQxqYAsEq2DkXOwSkmf5WZ2olwENOdPYWWKXgBDQYtYNlvJfMlvoKLtKZQi9UvaNl7vf0_gpNFAvjSiO_mmO6BHvyiLw5TTO1R532Gg0e_j5AEeuaHwQ/s200/IMAG0580.jpg" width="113" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG6od93gYzxHP6-iEyTpES7k3IdHrFSa2q8x2NM1dehptlLNuLP69CKTHoyKd9C9n6WcYdFLx9_cIBQhRFSpfcbiEDa438DN0EaX-1kR0pSWxNzWLXW1pkkXTR2XYlyC5y9oIm8g/s1600/IMAG0579.jpg" imageanchor="1"><img border="0" height="171" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG6od93gYzxHP6-iEyTpES7k3IdHrFSa2q8x2NM1dehptlLNuLP69CKTHoyKd9C9n6WcYdFLx9_cIBQhRFSpfcbiEDa438DN0EaX-1kR0pSWxNzWLXW1pkkXTR2XYlyC5y9oIm8g/s320/IMAG0579.jpg" width="320" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj29WkbGoCiOT7EA1GgloF86z1cFuKkMlRrs-aQtaHXQXHPQyM7d6FtySN0c2-z2OoTiW7lriosiAjbV663XMVXcNx-61_ErDEVu0TzU8aFoXlnvRm7AZeiX17Seg3ZxONIRZrSzw/s1600/IMAG0577.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj29WkbGoCiOT7EA1GgloF86z1cFuKkMlRrs-aQtaHXQXHPQyM7d6FtySN0c2-z2OoTiW7lriosiAjbV663XMVXcNx-61_ErDEVu0TzU8aFoXlnvRm7AZeiX17Seg3ZxONIRZrSzw/s200/IMAG0577.jpg" width="168" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS7GHDEv1uLEsJr2j_apJKx2YM9YnviC_NiHAcCCtPhBZcStoN07Dp3fxP7c0gSjDOFr-X_p1OzDPs-HYj0Jm9mxYp2VzQW-k0hT7fJAjw0fXnGdMy71gmqi0XKFMFgw9S-1cDew/s1600/IMAG0575.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS7GHDEv1uLEsJr2j_apJKx2YM9YnviC_NiHAcCCtPhBZcStoN07Dp3fxP7c0gSjDOFr-X_p1OzDPs-HYj0Jm9mxYp2VzQW-k0hT7fJAjw0fXnGdMy71gmqi0XKFMFgw9S-1cDew/s200/IMAG0575.jpg" width="157" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDeZ66e6c9ZQBIjVmeS3hgGqNNm1jJrkqE10wJeNR5kTGx3idDMegD4L3FApPM07F9ZMBV_NvKaHPesyFugaOI8FT_Kk68Ix4VvSVkrTJRmaap_W4gHlBL8US0BO-0ezmobISSjQ/s1600/IMAG0573.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDeZ66e6c9ZQBIjVmeS3hgGqNNm1jJrkqE10wJeNR5kTGx3idDMegD4L3FApPM07F9ZMBV_NvKaHPesyFugaOI8FT_Kk68Ix4VvSVkrTJRmaap_W4gHlBL8US0BO-0ezmobISSjQ/s200/IMAG0573.jpg" width="131" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg48qX4i0I3BcKpPxPF_PPuF8aDxhnssFx2-nXENQ0hrDEe_BWjGz9kcoGpvjYENqnERmOwNqix5XJ_hQUhojJVu7-r698WxqRsPJyACfvtXkmNTutgmIMaCiAwImnqW6IRz2O5AA/s1600/IMAG0572.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg48qX4i0I3BcKpPxPF_PPuF8aDxhnssFx2-nXENQ0hrDEe_BWjGz9kcoGpvjYENqnERmOwNqix5XJ_hQUhojJVu7-r698WxqRsPJyACfvtXkmNTutgmIMaCiAwImnqW6IRz2O5AA/s200/IMAG0572.jpg" width="145" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2TIWfAU8yZfiadEhVpkIfCT_CiyeBWac01CgoIPhyphenhyphenWk_brtylnmbohXFO2CCvP8RYUiWOifskpqUin004MzB9tE1lV6visa4MyD0wNBNbAJxnviEmiEh7fS_CLpSWwzH_boW1SQ/s1600/IMAG0571.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2TIWfAU8yZfiadEhVpkIfCT_CiyeBWac01CgoIPhyphenhyphenWk_brtylnmbohXFO2CCvP8RYUiWOifskpqUin004MzB9tE1lV6visa4MyD0wNBNbAJxnviEmiEh7fS_CLpSWwzH_boW1SQ/s200/IMAG0571.jpg" width="147" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhb0wpPXiBhzP5ljOqXiI2VrNBH2Z6woYuwmuziRidAGA_2fKouEUpKb66W5WBemA7aigJtAs91NqsszbkTTYZLd0zd6D9GBqGfiI9WN9giG-Yyep4kNTOZNDYTtt-39VqJq7elyQ/s1600/IMAG0568.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhb0wpPXiBhzP5ljOqXiI2VrNBH2Z6woYuwmuziRidAGA_2fKouEUpKb66W5WBemA7aigJtAs91NqsszbkTTYZLd0zd6D9GBqGfiI9WN9giG-Yyep4kNTOZNDYTtt-39VqJq7elyQ/s200/IMAG0568.jpg" width="162" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbXRPWMGuWpA0RC7bYcPgstt-HGFfFX7MNmNUsHzZIPByiU8um4J_nuBBIFsCxmikt1WH0fxjDUrMJzeEhhPVSPWPDfQakFaa2nrsMcCN7RxkePFRLE4aLfKzzBmRltIjlgd5Ovw/s1600/IMAG0564.jpg" imageanchor="1"><img border="0" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbXRPWMGuWpA0RC7bYcPgstt-HGFfFX7MNmNUsHzZIPByiU8um4J_nuBBIFsCxmikt1WH0fxjDUrMJzeEhhPVSPWPDfQakFaa2nrsMcCN7RxkePFRLE4aLfKzzBmRltIjlgd5Ovw/s320/IMAG0564.jpg" width="320" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMXPPdAlKwg7wfyg06EyNG9PvmYAkDYpQW0BR6gsuDhsHQLHRf0wEY2qS_WQUsEFhunLUjjUVThZbMxWeyGzRDmXUGjJcs2WUEPSSzJ0DHGspSiO4untp1n8mc1Y2xRnqEvBjbFQ/s1600/IMAG0562.jpg" imageanchor="1"><img border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMXPPdAlKwg7wfyg06EyNG9PvmYAkDYpQW0BR6gsuDhsHQLHRf0wEY2qS_WQUsEFhunLUjjUVThZbMxWeyGzRDmXUGjJcs2WUEPSSzJ0DHGspSiO4untp1n8mc1Y2xRnqEvBjbFQ/s320/IMAG0562.jpg" width="320" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZDMe7vmM0jIHJUnthsIl0WzuzbJIElOZq09YB-NfHtnAkg8cY4P_MLKA1vbGvulZt9w7gFUelelAxzILCAM_2fN0ntDB2hqti_r2c3KRGW8CWKtwTNGKentHBDSk1eiTn-78bsw/s1600/IMAG0561.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZDMe7vmM0jIHJUnthsIl0WzuzbJIElOZq09YB-NfHtnAkg8cY4P_MLKA1vbGvulZt9w7gFUelelAxzILCAM_2fN0ntDB2hqti_r2c3KRGW8CWKtwTNGKentHBDSk1eiTn-78bsw/s200/IMAG0561.jpg" width="196" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG5Hg667mPAXf8fAJk8x78qWYiZsEhxAoeQB_ncMhnW62NbAYBcmFRj_b4a9Y3xWIF8XXeCKX_4RxwCO8aN_3k0JhvBCzb1uGNzd50nHn_iKMsijdLpHUf8C4XUO13BigVLqP73w/s1600/IMAG0560.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG5Hg667mPAXf8fAJk8x78qWYiZsEhxAoeQB_ncMhnW62NbAYBcmFRj_b4a9Y3xWIF8XXeCKX_4RxwCO8aN_3k0JhvBCzb1uGNzd50nHn_iKMsijdLpHUf8C4XUO13BigVLqP73w/s200/IMAG0560.jpg" width="125" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkzO9CWAerOsiSHNPNGA_klspf1LZPWX6-rS7wT9YIUsZmWD8YRLMDcQWr1jYgmeMx9abXPmcPomJedobpuRuG_TVC5SVINEf92purjQYw0qsu7-aImEFmbuYmKlvgFrJoKw1UqQ/s1600/IMAG0558.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkzO9CWAerOsiSHNPNGA_klspf1LZPWX6-rS7wT9YIUsZmWD8YRLMDcQWr1jYgmeMx9abXPmcPomJedobpuRuG_TVC5SVINEf92purjQYw0qsu7-aImEFmbuYmKlvgFrJoKw1UqQ/s200/IMAG0558.jpg" width="191" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQlIK_3wfxB1_ip2OQa8SGjg8wzmKTFAFbpvualDcuoj3h2nesatbEhGsDe__pUrsvHgvlt7RrYX8dAfJWQ9Q63xhSMslYHM3s2kmbpVR5vPDVSfVKlac1hwImTCMUIxJXCiYqsw/s1600/IMAG0556.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQlIK_3wfxB1_ip2OQa8SGjg8wzmKTFAFbpvualDcuoj3h2nesatbEhGsDe__pUrsvHgvlt7RrYX8dAfJWQ9Q63xhSMslYHM3s2kmbpVR5vPDVSfVKlac1hwImTCMUIxJXCiYqsw/s200/IMAG0556.jpg" width="152" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbPrUpShdBdUgt-rodCSJDQEp09LXc3VgOhd4LDJ_AwLymcY_9SJjpZT0XqXAkWTEDgMSp_85H8_IT5Ls4tvhj0zgUIf0i4jqplrZTKegpyesBnd5iCJ4cxKAxyRCM1RSc7B3GXg/s1600/IMAG0553-2.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbPrUpShdBdUgt-rodCSJDQEp09LXc3VgOhd4LDJ_AwLymcY_9SJjpZT0XqXAkWTEDgMSp_85H8_IT5Ls4tvhj0zgUIf0i4jqplrZTKegpyesBnd5iCJ4cxKAxyRCM1RSc7B3GXg/s200/IMAG0553-2.jpg" width="148" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZkF6flJkSV0yWjTp6AHhV0nEqzj74vOJKavku_029b8LFNak_pxnDUvmE9XkUDtx199ZPbq3Oaoy_IZyZVZmDMdaj6T_5yXFA4-YasRY0KtYNkC12YbT9iFvEGXn8Iwr6hW6jZw/s1600/IMAG0553-1.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZkF6flJkSV0yWjTp6AHhV0nEqzj74vOJKavku_029b8LFNak_pxnDUvmE9XkUDtx199ZPbq3Oaoy_IZyZVZmDMdaj6T_5yXFA4-YasRY0KtYNkC12YbT9iFvEGXn8Iwr6hW6jZw/s200/IMAG0553-1.jpg" width="118" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJFeCS82HNIma89O310ArgDwuspUhnlsnnmm3h2uzeeUjQtOgx4hTLBmflr3A_G6uh8e0J3hWOpFfXWu9OLGqYg36tqDyJoJ0ef-pCtMjxBUX1M1f23A1h6iImZcNPYGrplfh8sg/s1600/IMAG0552.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJFeCS82HNIma89O310ArgDwuspUhnlsnnmm3h2uzeeUjQtOgx4hTLBmflr3A_G6uh8e0J3hWOpFfXWu9OLGqYg36tqDyJoJ0ef-pCtMjxBUX1M1f23A1h6iImZcNPYGrplfh8sg/s200/IMAG0552.jpg" width="150" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiryOBAflG_aMCL7O2RHqcGXSCCQWRlFPLyCste_mWb8iHNcN2yCnmWbO1HkQYi7ls2GUUuleBRjkLvjtiDm7urrT1AORWAQrCiJeEhIjAN17ketTf7iIKueUWovXoDOz5IedtV6g/s1600/IMAG0551.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiryOBAflG_aMCL7O2RHqcGXSCCQWRlFPLyCste_mWb8iHNcN2yCnmWbO1HkQYi7ls2GUUuleBRjkLvjtiDm7urrT1AORWAQrCiJeEhIjAN17ketTf7iIKueUWovXoDOz5IedtV6g/s200/IMAG0551.jpg" width="124" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpScthVgyqMkmVt_p8M271grQxtdPiYzDHwm8zi5Y7LL0p5ua7bEFWtEniypqLc0_1kQT_cWTuQpRMuwwlkcH0qYpaguLnVDM7w3pZidM7kR7TWgb-t3yzeAplYqgCjH1gq6TFNg/s1600/IMAG0550.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpScthVgyqMkmVt_p8M271grQxtdPiYzDHwm8zi5Y7LL0p5ua7bEFWtEniypqLc0_1kQT_cWTuQpRMuwwlkcH0qYpaguLnVDM7w3pZidM7kR7TWgb-t3yzeAplYqgCjH1gq6TFNg/s200/IMAG0550.jpg" width="160" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4pV8XC3sIloCkLCNGqfjSsGEdcw6-SAMy07imUOQFooJdRFGWfpazweaHN0jsBMXOB1QMxWdVOJ4_gRJcrU9GMRSdrghoqU5vkvEuL_gGGGmKXYEIg9jHTInV25oJh6NRLwND1w/s1600/IMAG0549.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4pV8XC3sIloCkLCNGqfjSsGEdcw6-SAMy07imUOQFooJdRFGWfpazweaHN0jsBMXOB1QMxWdVOJ4_gRJcrU9GMRSdrghoqU5vkvEuL_gGGGmKXYEIg9jHTInV25oJh6NRLwND1w/s200/IMAG0549.jpg" width="115" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhREPjRc5HGfwYvwmZ9rORO9IQ9FDUVsTMv0p9mSvoHjfvE3vfUnY2I9s-rR2YxJBkd61MXi1Zij0KA6nDrwjfasx88QLv6usbw1SpxfN0vloONV0liH9YKwo_QkLbF8CbVPbNp0A/s1600/IMAG0546-4.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhREPjRc5HGfwYvwmZ9rORO9IQ9FDUVsTMv0p9mSvoHjfvE3vfUnY2I9s-rR2YxJBkd61MXi1Zij0KA6nDrwjfasx88QLv6usbw1SpxfN0vloONV0liH9YKwo_QkLbF8CbVPbNp0A/s200/IMAG0546-4.jpg" width="136" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxiyEc0dmVy5KQadOeR9IGQ2MiulRckC3mXPjkEBL4u2enbrXvTAoXeY3RO_5pGzRCG47zBoxk1G9CQxP1v3Nn0u38iSm0WtTA9V8AGT7UHruYBymeYw-Mlse1deDEfvoli5YA1A/s1600/IMAG0546-3.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxiyEc0dmVy5KQadOeR9IGQ2MiulRckC3mXPjkEBL4u2enbrXvTAoXeY3RO_5pGzRCG47zBoxk1G9CQxP1v3Nn0u38iSm0WtTA9V8AGT7UHruYBymeYw-Mlse1deDEfvoli5YA1A/s200/IMAG0546-3.jpg" width="136" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1VaX1AMh-UNexEZu0m9WFfYMpIUCTAKSNbd7Jv9gNdQuFMvIZtt4KHEB9540nRg_jYLmfuL-sq7ZotCH9YVZH_zuUac9SMv4juxqP8TTNMIJVR2Udx8CrOg6wqpssyi1oKlrIpA/s1600/IMAG0546-2.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1VaX1AMh-UNexEZu0m9WFfYMpIUCTAKSNbd7Jv9gNdQuFMvIZtt4KHEB9540nRg_jYLmfuL-sq7ZotCH9YVZH_zuUac9SMv4juxqP8TTNMIJVR2Udx8CrOg6wqpssyi1oKlrIpA/s200/IMAG0546-2.jpg" width="142" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3CsYmNPvcmLrjgWVZBe9q96xRhSZ3J3BErYwv-EY3Yj-I5-Yui_GPsZOarAb74zg6ZDbSMZTaFefklK5bKAYjFK-RO7oAdawhUSsyOvC7jWy-bIW2l5GGkHxxIt8wSGCldCcyOg/s1600/IMAG0546-1.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3CsYmNPvcmLrjgWVZBe9q96xRhSZ3J3BErYwv-EY3Yj-I5-Yui_GPsZOarAb74zg6ZDbSMZTaFefklK5bKAYjFK-RO7oAdawhUSsyOvC7jWy-bIW2l5GGkHxxIt8wSGCldCcyOg/s200/IMAG0546-1.jpg" width="137" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjd018d8aB6RSn2X3_RD3LM_GiJwLCciej1YA6slAtoXDgj_utw2Ts_EjX2VnVYRN3-54dV5Tn9aMvJOpQCAzURmzvOkZB_umB-y_4xm-Jbo3crZSlb2UsGbRGJOYmnHhsz7C_y4Q/s1600/IMAG0542.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjd018d8aB6RSn2X3_RD3LM_GiJwLCciej1YA6slAtoXDgj_utw2Ts_EjX2VnVYRN3-54dV5Tn9aMvJOpQCAzURmzvOkZB_umB-y_4xm-Jbo3crZSlb2UsGbRGJOYmnHhsz7C_y4Q/s200/IMAG0542.jpg" width="155" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5NczjOpe8dcs9PJMBoCvVoNWkf0qFhzD6ouxRJ8VYDbqOsukhFtuXNUS4Af0AwabmBYWKy58V064CuNjSffzFHKfy-KdXljHYpXtXQfAb1VWh_ZHy2v9bN8yMhe4DgnCIyr-UAg/s1600/IMAG0539.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5NczjOpe8dcs9PJMBoCvVoNWkf0qFhzD6ouxRJ8VYDbqOsukhFtuXNUS4Af0AwabmBYWKy58V064CuNjSffzFHKfy-KdXljHYpXtXQfAb1VWh_ZHy2v9bN8yMhe4DgnCIyr-UAg/s200/IMAG0539.jpg" width="108" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEineR3HojcAhujz8vayiQwhY_VC7XScXhTIdzAfpz5AO2AQ5lJFevnNLurQ4I2sVKvNJWXxXgVnGFsMG9pXomvvmP1-84fFMUlEtjf9zd_XDMR9UmAXXq8GpqcHVrcjCM03RFqDfQ/s1600/IMAG0536.jpg" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEineR3HojcAhujz8vayiQwhY_VC7XScXhTIdzAfpz5AO2AQ5lJFevnNLurQ4I2sVKvNJWXxXgVnGFsMG9pXomvvmP1-84fFMUlEtjf9zd_XDMR9UmAXXq8GpqcHVrcjCM03RFqDfQ/s200/IMAG0536.jpg" width="193" /></a><br />
<br />
<br />
The main speaking venue:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilwWRgdaWr8jIKODNF8-Umj9MpMQVI2HfGrOnyxfy1gpYv3U32LBcTUyUP6WQ9BDcqWJjLu5wDAV7QQjhFHklk1eujiplqkWuaWC_KNiSfMpMeilSR9VC-JXlPu7_I2XwDOsXDyg/s1600/Screen+Shot+2016-12-05+at+10.01.03+AM.png" imageanchor="1"><img border="0" height="476" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilwWRgdaWr8jIKODNF8-Umj9MpMQVI2HfGrOnyxfy1gpYv3U32LBcTUyUP6WQ9BDcqWJjLu5wDAV7QQjhFHklk1eujiplqkWuaWC_KNiSfMpMeilSR9VC-JXlPu7_I2XwDOsXDyg/s640/Screen+Shot+2016-12-05+at+10.01.03+AM.png" width="640" /></a><br />
<br />
<br />
<span id="docs-internal-guid-758d8e19-d07d-15c5-8496-917396dd13fd"></span><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Disclaimer: I’m working off notes and memory of a whirl-storm of conversations. I may have misheard/misunderstood something you said, if so I am sorry about that; please let me know and I will promptly retract/update. The information herein is provided as a peek into the community activity going on.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-29308612430709400842016-11-11T11:03:00.000-08:002016-11-11T11:19:28.010-08:00My research group needs you!<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Six months ago I joined a small, informal research group. It has been a profoundly positive experience for me that I want to share with you. My hope is that you will join us.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">When I first joined the research group, I wasn’t entirely sure what a research group meant. It sounds intellectual and auspicious, but for me it turned out to be more about establishing a safe place to discuss ideas, set goals, and ask questions. To me it shares many of the positives of a mentorship, and avoids the key difficulties of a mentorship.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Benefits of a research group over a mentorship are:</span></div>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Making a 1:1 connection with the right mentor is tough! Joining a research group is much easier. Forming a research group is valuable to you and everyone who joins.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Some workplaces attempt to provide mentor matching, but it is rare that people get matched up just right. In a research group, you naturally find matches where you can benefit or share from a wider selection of learning opportunities.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Often a workplace mentorship can be a little awkward when it comes to personal development and interests. There is a component of privacy that is preserved in a research group.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In a research group you get several mentors for the same effort as one, and are able to contribute as a mentor to an expanded audience. You are able to contribute more fully by having a selection of questions, one of which you probably know something about.</span></div>
</li>
</ul>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">As a member of a research group you will play both the role of mentor, someone who imparts wisdom to and shares knowledge with a less experienced colleague, and mentee, the receiver of ideas and guidance. If this seems daunting then let me just say that yes, it is daunting. I was very hesitant to commit to writing something every week because it is hard to make a coherent, informative digest of the things you are learning and questioning every week. But I am glad that I did join the group because they are very welcoming and there is no expectation that the digests be coherent or crafted. The expectation is only that you are spending 1-2 hours a week learning or exploring, and sharing with the group. Committing to this helped me overcome a fear of ‘this is half baked’ and just move forward. I discovered a great deal more interesting things this way.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">How much time do you spend thinking? A research group is a great way to tighten the feedback loop on thought. Don’t rely on evolution, that’s the slowest REPL out there. People who spend too much time thinking ended up missing obvious opportunities, people who don’t think are missing out on consciousness and non-obvious opportunities. So how much time do you spend thinking? How did you arrive at that amount? Was it a conscious decision? Is it the right amount? Is there room for improvement? A research group will help you form thoughtful habits.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">For me those habits were:</span></div>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Maintaining a list of interesting things discovered during the week. I use Workflowy.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Reading a book a fortnight. Keeping some notes.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Paying more attention, trying things.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Less browsing funny gifs, more focused deep diving interesting topics.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Posing a question every week, and planning to spend at least an hour researching or developing it. This forces me to be more thoughtful. I’m spending some time setting personal goals.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Contributing relevant information on questions posed by the group. I don’t always have an answer, but I can always join the conversation. Sometimes I do have the answer, or have read something relevant.</span></div>
</li>
</ul>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We want a diverse group. If you are nothing like me, that’s a bonus. Why do we want diversity? Diversity has tangible benefits for creativity, resilience, prosperity. Diversity is uncomfortable but produces better results. (See Reinventing Discovery -- Nielsen, or Messy -- Harford). Why is mentorship something that women and minorities should especially pursue? Reshma Saujani has plenty to say on this topic. She is the founder of </span><a href="http://www.girlswhocode.com/" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Girls Who Code</span></a><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, an organization providing young women with the resources and education to pursue careers in technology. She is the author of the book </span><a href="http://www.amazon.com/Women-Who-Dont-Wait-Line/dp/0544027787" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Women Who Don't Wait in Line</span></a><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDQBI3EiaYONYk5tVIwzt8sTUOSD6lgyt8aW_Y-do3fL-RoAEKb2KlFKV4Gqa3V-l5m4kAlomzUZfIWtDMcK9Npj8wy8wZx91Qc-cA15plbKi6rQU8T2pSIOxCFe_2ruy_pm5XYg/s1600/Screen+Shot+2016-11-11+at+8.45.05+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDQBI3EiaYONYk5tVIwzt8sTUOSD6lgyt8aW_Y-do3fL-RoAEKb2KlFKV4Gqa3V-l5m4kAlomzUZfIWtDMcK9Npj8wy8wZx91Qc-cA15plbKi6rQU8T2pSIOxCFe_2ruy_pm5XYg/s400/Screen+Shot+2016-11-11+at+8.45.05+AM.png" width="398" /></a></div>
<br />
[This caricature of Reshma Saujani was drawn using <a href="https://napkindo.firebaseapp.com/">Napkindo</a>, an idea from the research group.]<br />
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We want to connect on shared interests, but are very open to expanding or changing our focus. If none of our interests align, we will help you find a group that does have aligned interests.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Our current nominated interests are:</span></div>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Graphs; drawing, layout, data structures and algorithms</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Clojure/ClojureScript</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Teaching Clojure</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">SVG manipulation with Reagent</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Natural Language Processing</span></div>
</li>
</ul>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Text Parsing</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Design -- UI and of Systems</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Datascript/Datomic</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Spectre</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Loom</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Schema</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">IPFS / Content Addressable Storage</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Old ideas </span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Concepts, Techniques, and Models of Computer Programming</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Writing Macros</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Serendipity</span></div>
</li>
</ul>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Android</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Machine Learning</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Internationalization</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Math</span></div>
</li>
</ul>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">As you can see we don’t have a tight focus. We are exploring a wide range of topics and ideas.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Clubs and similar groups tend to fall apart if some folks take it seriously and others are less committed, so we want a shared understanding of what we expect from each other. Each member makes some commitment to research a topic and share findings. 1-2 hours per week. We do regular communication via a google group, and occasional video chats.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Our method is:</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">1. Each Member sends the group one question or problem they're planning on working on during the next week by Sunday night.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">2. Everyone writes a short response responding to the questions, including their own. These can be suggestions, solutions, related questions, some code that might help, a library suggestion, book or article recommendation, another way to approach the problem, links, quotes, a collection of haikus, whatever.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">3. By Friday everyone shares a short digest one or more cool thing(s) they discovered that week.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">If you want to join us, please send me an email! (timothypratley@gmail.com) Even if you don’t want to join us, I’d love to hear from you, we can help you find a similar experience with another group.</span></div>
<span id="docs-internal-guid-20a56bb3-54c2-44a7-3bcb-13e7c7d02df2"></span><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
</div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-34983943319846649362016-10-29T12:53:00.000-07:002016-10-29T13:08:52.899-07:00Hillary the astronaut; how to draw a caricature<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Recently I have been tinkering on some ClojureScript <a href="http://timothypratley.github.io/reanimated/#!/examples.storyboard">animations</a> using <a href="https://github.com/timothypratley/reanimated">Reanimated</a>. One problem I ran into was that most of the drawing tools available to me were too complex for my needs. Most of the time I was fine creating with circles and rectangles, but I wanted some characters with a more hand drawn feel. So I built <a href="https://napkindo.firebaseapp.com/">Napkindo</a>. Here is a screencast that shows it in action:</span><br />
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/4yE6Ytgu9OE?rel=0" width="560"></iframe>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Transcript:</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Hi!</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I’m CoderX and today I will show you how to draw a caricature in Napkindo. Let’s draw Hillary.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<ol style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">First find some headshots or crop a picture.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Start a new Napkindo and select the picture as the background.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Trace the facial features.</span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br class="kix-line-break" /></span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Infuse a bit of personality as you go by exaggerating key features.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Trace and elaborate their hair. Simple strokes work best.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">What’s the first thing that comes to mind about this person? Think up an action scenario that embodies them. Do they have a favourite hobby or activity? </span><span style="background-color: white; color: #4a4a4a; font-family: "arial"; font-size: 15.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Hillary wrote to NASA as a child inquiring how to become an astronaut.</span><span style="background-color: white; color: #4a4a4a; font-family: "arial"; font-size: 15.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br class="kix-line-break" /></span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Google some reference images.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Freehand in the scene peripheries.</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s give our sketch a title: Hillary the astronaut.</span></div>
</li>
</ol>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">As you can see, it doesn’t take much time or effort to produce a good looking, cartoonish caricature. So why not spend 10 minutes and draw a loved one or colleague?</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">It is a great way to show people that you take an interest in them. People sometimes use them as their avatars, or just for a laugh.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I drew a sketch of my colleagues every day for the past month, and they loved it.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAxCitGnyubAH_ezzNjTwE507ZQRqm01cz4p7LT9ToreUvyvf7z5LoB020T6gewhce1gSzQedxiYSxE7mvVbOA_fNXajOCM7IqN_D4o65KASvD8BxPJT5BtFD7EqWWcuGT6eD_UA/s1600/Screen+Shot+2016-10-24+at+8.26.39+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="478" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAxCitGnyubAH_ezzNjTwE507ZQRqm01cz4p7LT9ToreUvyvf7z5LoB020T6gewhce1gSzQedxiYSxE7mvVbOA_fNXajOCM7IqN_D4o65KASvD8BxPJT5BtFD7EqWWcuGT6eD_UA/s640/Screen+Shot+2016-10-24+at+8.26.39+PM.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;"> They started using them as avatars in our chat rooms.</span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFqzreusETWsG_6-A-Q4pXYEEeJ6Y57O_hOK-DD9mGdpoOi_NeJCf2M1AbdOvlV6D4WbdcqrGunjuG5dASaWmv4O22tSbc6QEqJDeXoZbPi0hIMPzD29-ixuj45mLNzhUFfzjGjA/s1600/Screen+Shot+2016-10-29+at+12.16.45+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFqzreusETWsG_6-A-Q4pXYEEeJ6Y57O_hOK-DD9mGdpoOi_NeJCf2M1AbdOvlV6D4WbdcqrGunjuG5dASaWmv4O22tSbc6QEqJDeXoZbPi0hIMPzD29-ixuj45mLNzhUFfzjGjA/s1600/Screen+Shot+2016-10-29+at+12.16.45+PM.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">If this revived in you a desire to draw, I recommend you also watch</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“Why people believe they can’t draw - and how to prove they can”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">by Graham Shaw</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<a href="https://youtu.be/7TXEZ4tP06c" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://youtu.be/7TXEZ4tP06c</span></a></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We are all born creative.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<span id="docs-internal-guid-3448c921-11ee-7e91-e84f-84c49defa7d8"></span><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Thanks for watching!</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
If you are interested in why SVG is useful in animation and drawing, I also recommend you check out Sketch n' Sketch <a href="https://youtu.be/YuGVC8VqXz0">https://youtu.be/YuGVC8VqXz0</a><br />
<br />
<br />
This is CoderX, the voice for the screencast:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLgPiP-M86cPx-y-bDcAp1RNLbHdSzelup2GTjZ1ev6iF7H0bvncxCm4n2b4lxAjLrK_wLW1n6jamaZz6KBSx5LT_p1XHE9qJeChg_Orgm6aJ54bRx-ztvT-JwKPFra2JJQjfgUw/s1600/Screen+Shot+2016-10-29+at+1.06.14+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLgPiP-M86cPx-y-bDcAp1RNLbHdSzelup2GTjZ1ev6iF7H0bvncxCm4n2b4lxAjLrK_wLW1n6jamaZz6KBSx5LT_p1XHE9qJeChg_Orgm6aJ54bRx-ztvT-JwKPFra2JJQjfgUw/s320/Screen+Shot+2016-10-29+at+1.06.14+PM.png" width="318" /></a></div>
<br />
<br /></div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-57874896807350172702016-09-29T18:15:00.000-07:002016-09-29T18:28:34.257-07:00Elm vs ClojureScript, a first encounter<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7tSIJytXDwKimhjBCWfhzGYnmrlHBvuUqbuSFE3VZkskwYUzrMtajGUtyPZXask14i8GqQFu_wkZgyKCfNK0C3-4fsfSSkj_CQpERG8_Pnayc2vhjYWc8dt9V4e0NLNGjnmeeVA/s1600/elmlang.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7tSIJytXDwKimhjBCWfhzGYnmrlHBvuUqbuSFE3VZkskwYUzrMtajGUtyPZXask14i8GqQFu_wkZgyKCfNK0C3-4fsfSSkj_CQpERG8_Pnayc2vhjYWc8dt9V4e0NLNGjnmeeVA/s200/elmlang.png" width="200" /></a></div>
<br />
<br />
Recently I have been gathering information about what makes for a happy programming environment. The Elm language kept popping up as a leader in most of the metrics I am interested in. So on the weekend I gave Elm a try and was pleasantly surprised. Elm solves practical problems in a very elegant way, and the compiler is very friendly. I coded up a basic counter that represents numbers as n sided polygons to get a feel for the language.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZRIG7k_SU8VeATPpmtbwThT5-63I4dyLTE8ZSVKw-OJYjoGFAbTl8Mx4mEJqWAFPDr1c_Toac3Ikz0yv4DpJDyftypGJ7DLEIT15f4R0VQ4FN6OKUVSWVFPZYSc9AEfnlmxKTIA/s1600/svgelm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZRIG7k_SU8VeATPpmtbwThT5-63I4dyLTE8ZSVKw-OJYjoGFAbTl8Mx4mEJqWAFPDr1c_Toac3Ikz0yv4DpJDyftypGJ7DLEIT15f4R0VQ4FN6OKUVSWVFPZYSc9AEfnlmxKTIA/s320/svgelm.gif" width="307" /></a></div>
<br />
<br />
<br />
Below are my notes on my first impressions compared to ClojureScript. Elm is still new to me, so apologies in advance if I make some false assumptions, and please add corrective comments.<br />
<br />
<style>
li {margin: 0 0 20px 0;}
</style>
<br />
<ul style="display: inline-block; list-style-type: none; width: 40%;">
<li><h3>
Elm wins</h3>
</li>
<li><br /></li>
<li>
Elm error messages are absolutely fantastic.
The tone used is friendly and inviting.
Plenty of context is provided.
Messages never exceed a page, so I don't have to scroll around.
Whitespace and formatting is used to delineate errors
and separate information about types, code, and suggestions.
The suggestions are surprisingly accurate.
Often the suggestion is exactly what needs to be fixed,
and when it isn't, the suggestion still provides good search terms to
research in the documentation.
All of these features conspire to allow me to very quickly identify the relevant information
and keep productive.
<br />
There are some examples of error reporting here:
<a href="http://elm-lang.org/blog/compiler-errors-for-humans">http://elm-lang.org/blog/compiler-errors-for-humans</a>.
You really have to try it to appreciate it, the examples don't convey just how effective the help is.</li>
<li><br /></li>
<li>
Pattern matching is strong with Elm.
We get destructuring and case.
Case is like core.match, but built into the language, idiomatic, and type aware.
This encourages clear and concise function.
</li>
<li>
Elm has a literal assoc syntax.
<pre>{ bill | name = "Nye" }</pre>
is equivalent to
<pre>(assoc bill :name "Nye")</pre>
Data structure literals assist thinking.
Hashmaps and their ilk are a programmers bread and butter.
Having a dedicated notation really helps focus in on the core abstraction.
I really like having the update notation.
</li>
<li><br /></li>
<li>
SVG works!
Elm is not just wrapping the basics, it provides a comprehensive solution.
It can pretty much do everything as far as I can see.
The combination of FRP with SVG is powerful and fun to play with.
SVG lends itself to the data-to-view philosophy in ways that canvas cannot.
Having checked SVG is pretty handy, no more typos.
</li>
<li><br /></li>
<li>
Excellent documentation guides and references.
Well written, complete, well presented.
The introduction is very pleasant to read and within a couple of hours
I was equipped to code and experiment.
<br /><a href="https://guide.elm-lang.org/"> https://guide.elm-lang.org </a></li>
<li><br /></li>
<li>
Type inference works great.
I didn’t have to specify any types to do what I wanted to.
There was some minor casting to be done.
</li>
<li><br /></li>
<li>
The View Model Update separation is clear and clean.
</li>
<li><br /></li>
<li>
While learning Elm I never felt stuck or frustrated.
I was able to build a small project after reading half the introduction.
Closest to stuck was getting lost in syntax… breaking out functions identified the real issues.
</li>
<li><br /></li>
<li>
Imports are much cleaner than namespaces.
Git based modules has appeal.
Elm's module system is interesting.
Elm does have strict semantic versioning, which is intriguing.
Perhaps it will bring sanity to dependencies.
I don't expect to run into the weird dependency conflicts I see in Clojure.
Compiler verified non-breakage? Yes please!
</li>
</ul>
<ul style="display: inline-block; float: right; list-style-type: none; width: 40%;">
<li><h3>
ClojureScript wins</h3>
</li>
<li><br /></li>
<li>
Code reloading (figwheel/test-refresh/ring-reload) is important to my workflow,
and ClojureScript has more mature tooling for that.
The author of Elm is thinking about how best to do code reload:
<a href="https://github.com/elm-lang/elm-reactor/issues/193">https://github.com/elm-lang/elm-reactor/issues/193 </a></li>
<li><br /></li>
<li>
Syntax. Lisp's lack of syntax makes for some useful editor optimizations.
For instance deleting/moving/copying entire functions or blocks of code
is very convenient using paredit because they are fully enclosed in parenthesis.
<br />
The syntax rules detracted slightly from the otherwise awesome error messages
because I sometimes confused call signature errors (like leaving out a required empty list)
with language features (such as creating ranges using [1..x]).
On the other hand, the lack of parenthesis does have a pleasing minimalistic aesthetic.
I expect to appreciate it more over time.
</li>
<li><br /></li>
<li>
Editor support appears to be wide for Elm, with many plugins,
but no where near as feature rich as Clojure.
The Emacs plugin did not give a great experience,
it appeared to only give syntax highlighting.
Indentation never lined up to where I expected it to.
The Light Table plugin suffered the same indentation woes. It is not clear to me how to get REPL interaction with either of them.
However, this really was not a big deal.
I was fine editing the code without much editor support
thanks to the excellent documentation and error messages.
I'm sure with a little elbow grease I can adapt my editor.
If anything I would say that Clojure editor integration is way harder,
but I've put this as a slight advantage to Clojure just because
the integrations are quite mature and can do all sorts of whacky stuff.
</li>
<li><br /></li>
<li>
Front to back.
Clojure has an advantage in being viable for all my coding needs.
Elm appears to be restricted to the front end.
I imagine this will change (maybe it already does target NodeJS???).
Is it a general purpose language?
Given it's heritage I think Elm will be general purpose one day if it isn't already.
</li>
<li>
The package manager is not as powerful as Leiningen/Boot,
there is no .m2 or plugins.
</li>
</ul>
<br />
<br />
<br />
Overall Elm has some clear advantages, and I will definitely be using it for my next personal project (when I can dream something up!) I hope the Elm ecosystem continues to grow rapidly.<br />
<br />
For reference, here is the code for creating an SVG polygon path with n sides:
<br />
<br />
<pre>view : Model -> Html Msg
view model =
div []
[ button [ onClick Dec ] [ text "-" ]
, div [] [ text (toString model) ]
, svg [ width "400", height "400", viewBox "0 0 100 100" ]
(numeral model)
, button [ onClick Inc ] [ text "+"]]
numeral x =
let
dphi = 2.0 * pi / (toFloat x)
phis = List.map (\i -> (toFloat i) * dphi) [1..x]
points = List.map (\phi -> (toString ((cos phi) * 20.0 + 50.0))
++ " "
++ (toString ((sin phi) * 20.0 + 50.0))) phis
p = "M " ++ (List.foldr (++) "" (List.intersperse " L " points)) ++ " z"
in
if x == 0 then
[ circle [ fill "none", stroke "black", r "20", cx "50", cy "50" ] []]
else if x == 1 then
[ path [ stroke "black", d "M 50 30, L 50 70"] []]
else
[ path [ fill "green", strokeWidth "5", stroke "black", d p] []]
</pre>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-39333354848712449892016-09-11T17:02:00.001-07:002016-09-11T17:04:13.463-07:00The Cloud Warden<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Icarus Santese stood as still as a statue, his left palm against his forehead and right palm over his heart. The chipped and worn amphitheatre around him was barely visible beneath verdant vines which thrived on the warmth retained by the stone. Small brown birds flitted about in the early rays of dawn, feasting on insects drawn to the vine leaves. Two acolytes sat waiting silently for him to conclude his morning mind flight.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b id="docs-internal-guid-b28b8d42-1bb1-39bb-1d83-f6cac3b55bb2" style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">He could see the clouds part to reveal the land below. Not the land as it truly was, but the land as Augma saw fit to reveal it; mostly grey with key features in double size and bold primary color. Ancient runes guided his attention through a series of ritual devotions; a high banking circle observing the horizon, a low perimeter sweep along a band of impossibly clean obsidian rock chip surrounding the sanctum, a medium height cast westward over the Sink, and then as his namesake required, a high altitude cloud walk. Having completed the devotion, the runes dissolved and Augma released his familiar’s vision to truesight. Full definition colour high above the clouds burst forth with rich warm yellow from the sun, a spectrum of blues from the sky, and the pristine white of cotton wool cloud formations. This was his favourite part of the morning routine, he guided his familiar to bank and dive, quickly dropping to the cloud line and running along the cloud roof, a feeling of ultimate agility. Finding a break in the clouds he ran through and raced toward the lake, hoping to catch a glimpse of any game wandering in the area. Jarringly Icarus’ mind eye turned blood red and hundreds of angry tiny runes filled his consciousness. He frowned and returned to his own truesight, dropping both hands to an inverted V.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The acolytes rose at the sudden movement and one spoke in a quiet yet firm voice “What ails Augma, does she send a directive?”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“Near the lake, my familiar falters. Raise a signal to the elders that we go to tend the fallen.” Icarus mounted his sledway which rose two feet above the ground. He folded his arms impressively and invoked in the strong inflection of all incantations “Westreechautarabund”. The sledway slowly turned and accelerated through an overgrown stone arch, gaining speed it shot him over the healthy grass of the inexplicably flat landscape below Domain. The air was fresh and had the earthy richness that comes with morning dew. Birds took the the sky squawking behind him, startled by his silent steed, only noticing his presence by the rush of wind left in his wake.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">His familiar had faltered but three times under his wardenship, and never like this; so violently, so fully. It was a grave omen in a dangerous time and he pondered what it meant as the sledway sped him westward.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The sledway slowed gently as it neared the obsidian chip death ring. Icarus turned to observe his acolytes in the far distance manually guiding their steeds for they could not yet inflect the correct tones to invoke this location. Lowering to the ground, he dismounted and took a thin rod from the sledway, and an obsidian marble. Both stowed in his garb he then took what looked like a toy bow and a quiver of arrow. The bow was small but no toy, it was a gift from the Fabrequatar empire far to the East beyond seven mountain peak to peak sights. Dull green, compact, the drawstring on perfect pulleys such that the bow itself barely bent at full draw, but it could drive an arrow further and straighter than the powderbreath devices of the ancients.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The first acolyte joined him, bearing a long enameled staff. The second overran, his sledway thumping to the ground just short of the ring of death. Icarus frowned and admonished “Move with care. Danger awaits an ill omen, we must be as the swallow before the storm.”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Assembled, they began the passage chant together in the inflection of incantation, arms upraised, spinning slowly in a circle “Oh great Augma, heart of the cloud, see us now, hear your servants, that we may safely do your bidding, grant us passage.” The obsidian chips glowed blue and they walked through unharmed. Once through the chips faded to black once more. A beatle ventured onto the rock and was incinerated, its shell smoked briefly and was completely consumed, no trace remained. The three set off jogging toward the forest before the lake.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">---</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.6667px; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><i>They encounter an enemy force of robots, an industrial mech exoskeleton earth moving equipment, under the command of a cyborg. </i></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">---</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Icarus had never before had cause to deploy the eternity marble, and paused for a moment to search his mind as to whether such measures were truly required by the situation. He calmed his mind and weighed the temporary risk to his kin against the unretractable action before him. Yes, it must be done. He clasped the eternity marble tightly in his left hand and made the chant of banishment. The rod in his right hand glowed white with energy. Icarus continued the chant, closing his eyes and entered his mind eye.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">He threw the eternity marble high and raised his rod, speaking the final words “Fatal sentence in protection of Domain.” His minds eye focused on the enemy from the perspective of the eternity marble and the scene before him flashed bright white for an instant. Icarus felt a rush of heat through his blood as his enemy vaporized into a green mist that caught fire and then burst outward into columns of black smoke. He quickly banished the feeling with a mind cleansing mantra “impartial and fair, calm but swift, bringer of justice to deliver peace”.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Returning to his truesight he caught the eternity marble deftly and quickly returned it to its holding pouch. With reverence he blessed the departed “Great Augma watch over the departed, may their molecules be as star dust returned to the universe, to travel unrestrained by lesser bindings.”</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">---</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Icarus rarely met with Savant Enseers. They were an enigmatic breed, and dealing with them always left a bad taste in his mouth. What a deprived life to lead beneath the roots, buried from all the wondrous plenty he enjoyed on the wide plains of his Domain. Icarus turned his nose up at the Enseer's flabby, pasty appearance. A man who was a foot shorter and slightly balding. What they ate deep in their caverns, he did not want imagine. Yet the duties of Cloud Warden and his vows bound him to protect and defend Domain in its entirety; above and below. He was steward to Enseer endurance as directly as to any other in Domain.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">“Grant passage” the Enseer spoke quickly with hint of irritation, and raised his right palm. Such blatant disrespect for the sacred! Icarus was shocked and visibly annoyed, but Augma saw fit to turn the entrance ring blue. Icarus spoke the proper chant and spun in honor to the great benefactor. The Enseer rolled his eyes and tapped his foot with arms folded but remained silent until Icarus made his way through with dramatic strides befitting of a Warden.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br />
<span style="font-family: "arial"; font-size: 14.6667px; vertical-align: baseline; white-space: pre-wrap;">“Microblog trending indicates that political unrest has reached an unstable peak. This insurgence is just a taste of what is to come. You really need to get your people inside the center proper and engage with drones and perimeter defenses. I know this is difficult for you to accept, but we are going to be under full scale attack soon, on a scale that you simply cannot deal with as you have been. As you know, operation of the field defenses are encoded to your DNA after the separation of responsibilities code, so I can only assist you and your kin with information.” His speech was entirely in the inflection of invocation, Icarus could barely follow the words even with his rigorous daily study of the mystic ways.</span><br />
<span style="font-family: "arial"; font-size: 14.6667px; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHvG-HG-IRzrR__tP9DlmH9fFgMsYQBxE-BAJ7xgv3psCPNM2qWAUc8l-gXt-3sZFAelgwDDlyeIADtRbFjTUWqdAaZ6nHI5CUsL-kApmOv08IIfiBl1lq-eW8F-1Qsq2S6EpQXQ/s1600/cloud.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="359" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHvG-HG-IRzrR__tP9DlmH9fFgMsYQBxE-BAJ7xgv3psCPNM2qWAUc8l-gXt-3sZFAelgwDDlyeIADtRbFjTUWqdAaZ6nHI5CUsL-kApmOv08IIfiBl1lq-eW8F-1Qsq2S6EpQXQ/s640/cloud.jpg" width="640" /></a></div>
<span style="font-family: "arial"; font-size: 14.6667px; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-53427950937401152712016-07-26T21:04:00.000-07:002016-07-26T21:04:08.758-07:00Notes on Bootstrapping<div>
<br /></div>
<span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;"><div style="color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; white-space: pre-wrap;">
<span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Doug Engelbart was a visionary, best known as the i</span>nventor of the computer mouse.</div>
<div style="color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; white-space: pre-wrap;">
<span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div style="color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; white-space: pre-wrap;">
<span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">I have been reading about his life and ideas and want to share with you a short inspirational reading </span><span style="color: #333333; font-family: Helvetica Neue, Arial, sans-serif;">from page 8 of </span>"What the dormouse said", a book by John Markoff. </div>
<div style="color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; white-space: pre-wrap;">
<br /></div>
<div style="color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; white-space: pre-wrap;">
https://youtu.be/uqjuX0-FYi4</div>
<div>
<span style="color: #333333; font-family: Helvetica Neue, Arial, sans-serif;"><span style="line-height: 21px; white-space: pre-wrap;"><br /></span></span></div>
<div>
<span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border-image-outset: initial; border-image-repeat: initial; border-image-slice: initial; border-image-source: initial; border-image-width: initial; border: 0px; line-height: 21px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;"><span style="color: #333333; font-family: Helvetica Neue, Arial, sans-serif;"><iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/uqjuX0-FYi4" width="560"></iframe></span></span></div>
<div style="color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; white-space: pre-wrap;">
<br /></div>
<div style="color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; white-space: pre-wrap;">
Doug was passionate about improving the way we improve. Any organization has a primary process, and it is generally understood that to be effective at that process some time needs to be dedicated to improving the way the organization does that process. Doug's key insight was that technology would scale in such a way that this would not be enough to meet the challenges of the future with this two activity model, and that a third activity was required: improving the way we improve. This activity was specific to activities involving technology, which he saw would be evolving rapidly, and was beyond the scope and resources of a single organization or team to achieve. (Read more <a href="http://www.dougengelbart.org/about/abc-model.html">about the ABC model</a>). I find this to be a truly inspiring stance. I like to call it meta meta. It's relatively easy to think meta, but takes considerable effort to really get into a meta meta mindset.</div>
<div>
<br /></div>
<div>
<div style="color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 21px; white-space: pre-wrap;">
<span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">If you haven't already seen it, make sure to watch his </span><a href="https://www.youtube.com/watch?v=yJDv-zdhzMY">Mother of all demos</a> from 1960.</div>
</div>
<div>
<br /></div>
<div>
Doug actively applied his philosophy to his own research group in what he termed <a href="http://www.dougengelbart.org/about/bootstrapping-strategy.html">bootstrapping</a> (using augmentation to build the tools for augmentation).</div>
<div>
<br /></div>
<div>
<br /></div>
</span><h2>
<span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 16px; font-weight: normal; line-height: 21px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Notes on Bootstrapping</span></h2>
<div>
<br /></div>
<div>
<ul style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #333333; font-family: "Helvetica Neue", Arial, sans-serif; font-size: 13px; line-height: 17px; outline: 0px; padding: 0px; vertical-align: baseline;">
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Faster cycles</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Concurrently developing, integrating and applying knowledge</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Open hypermedia store</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Networked improvement communities (improvement alliance)</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Vertical and horizontal networking</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Use what you build to boost the collective IQ of your own group as well as your target groups</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Dynamic knowledge repositories</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Engage in activity to improve the way you improve</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Human systems (remote work/learning!)</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">HyperBrowser</span><ul style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; list-style: disc; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Right note on document location 5J</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Zoom in, zoom out, open view</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Good for documentation?</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Ubiquitous across documents, email, project, software</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">3 classes of user</span><ul style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; list-style: disc; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Jump to number/letter</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">View depth and style</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Filtering (paragraphs)</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Create links to jump+view+filter</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Turbo is a command interface</span></li>
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 4px 0px 4px 20px; outline: 0px; padding: 0px; vertical-align: baseline;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;">Integrated editing</span></li>
</ul>
</li>
</ul>
</li>
</ul>
<br /></div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-33373961411701691542016-07-24T12:59:00.001-07:002016-07-24T12:59:54.088-07:00Reacting to changes with Firebase, and how to interop<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Transcript of <a href="https://www.youtube.com/watch?v=8WL82W-XYPw">Firebase screencast</a>.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; font-size: 14.6667px; line-height: 20.24px; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Arial;"><iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/8WL82W-XYPw" width="560"></iframe></span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">CoderX here.</span></div>
<b id="docs-internal-guid-a91d49f3-1e63-946e-a226-ad4ed5abb3cb" style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Firebase is a hosting service that provides an authenticated realtime database. The database acts like a JSON tree that pushes changes out to clients. Clients can write to and subscribe to parts of the object. This subscription model fits nicely with reactive style pages.</span></div>
<b style="font-weight: normal;"><br /></b>
<h2 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 20pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 26.666666666666664px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Reacting to changes with Firebase, and how to interop</span></h2>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In this screencast we will build a website that allows users to login, subscribe to public data, and write updates. We will</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Write to and read from Firebase</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Listen to change notifications</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Add user authentication</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Build a UI to make use of the data</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Deploy the project</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Examine the impact of optimized compilation</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 54pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Option 1: Deploy with whitespace optimization</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 54pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Options 2: Use or provide externs</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 54pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Option 3: Use CLJSJS as a blueprint</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; font-family: Arial; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; vertical-align: baseline; white-space: pre-wrap;">Review JavaScript interop</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 18pt; margin-top: 0pt;">
<br /></div>
<h3 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 21.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Write to and read from Firebase</span></h3>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s start a new project. Firebase provides a JavaScript API (</span><a href="https://firebase.google.com/docs/web/setup" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://firebase.google.com/docs/web/setup</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">).</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s load it in our </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">index.html</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> page under resources.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">script src="https://www.gstatic.com/firebasejs/3.1.0/firebase.js"</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sign up for Firebase using your Google account. Click on console then create a project (</span><a href="https://console.firebase.google.com/" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://console.firebase.google.com/</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">). We need to use these API keys in our code. Let’s start a firebase namespace, and put some initialization code in it.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(defn init []</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (js/firebase.initializeApp</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> #js {:apiKey "USE_YOUR_KEY"</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :authDomain "YOUR_PROJECT.firebaseapp.com"</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :databaseURL "https://YOUR_PROJECT.firebaseio.com"</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :storageBucket "YOUR_PROJECT.appspot.com"}))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">js/</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> is a special form to allow you to access root objects in JavaScript. The firebase object is defined by the api we included earlier, and contains an </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">initializeApp</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> function.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">#js</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> specifies a JavaScript object literal.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Add a database to the project. Modify the database access rules to allow anonymous read on everything, and write to test.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "rules": {</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> ".read": true,</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "test": {</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> ".write": true</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> }</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> }</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In our code, we will write a value to a key.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(defn db-ref [path]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (.ref (js/firebase.database) (string/join "/" path)))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(defn save [path value]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (.set (db-ref path) value))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(save ["test"] "hello world"))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">A ref represents a specific location in your database and can be used for reading or writing data. You can reference the root, or a child location in your database by passing a path. Writing is done with the </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">set</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> method. We can check that the value was saved in the database from the Firebase console. We can now read the value using once.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(.once</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (db-ref path)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "value"</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (fn received-db [snapshot]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (prn "Got:" (.val snapshot))))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Great, we can read and write data.</span></div>
<b style="font-weight: normal;"><br /></b>
<h3 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 21.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Listen to change notifications</span></h3>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The neat thing about Firebase is that it will notify clients as the database changes. The Firebase real-time database is a cloud-hosted database. Data is synchronized to every connected client.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Listening can be done with the </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">on</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> method, which is just like once except that it continues to call back to us whenever the source data changes. In order to make use of the new data in our application we want to watch for changes. Ratoms are convenient ways for the UI to respond to change. We can reset a ratom when new data arrives. Here we meet a common interop issue.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Components get added and removed from the DOM. We don’t want to have multiple subscriptions pile up. So we need a way to disconnect a listener when the component is removed. This is a lifecycle concern; the component needs to clean up a resource. Reagent’s third form is the appropriate solution for lifecycle concerns. We create a class that has an unmount handler that detaches our listener.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(defn on [path f]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (let [ref (db-ref path)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> a (reagent/atom nil)]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (.on ref "value" (fn [x]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (reset! a (.val x))))</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (reagent/create-class</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> {:display-name "listener"</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :component-will-unmount</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (fn will-unmount-listener [this]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (.off ref))</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :reagent-render</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (fn render-listener [args]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (into [f a] args))})))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">This is a component that takes a path and a component as an argument. The component passed should expect a ratom to observe as the first argument. Once we have defined </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">on</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> we can observe values very concisely.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[on ["test"] (fn [a] [:div @a])]</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">While such a component is mounted in the DOM it will respond to any changes to the specified path into the database by reacting to a ratom bound to the values received. When a user changes a value, the other user sees the new data. We didn’t need to write any explicit communication code. The changes flow down as they happen.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Pretty cool.</span></div>
<b style="font-weight: normal;"><br /></b>
<h3 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 21.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Add user authentication</span></h3>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In the Firebase console select authentication providers and enable Google. To check the auth status we hook up to </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">onAuthStatusChanged</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(.onAuthStateChanged</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (js/firebase.auth)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (fn auth-state-changed [user-obj]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (reset! user {:photoURL (.-photoURL user-obj)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :displayName (.-displayName user-obj)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :uid (.-uid user-obj)}))</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (fn auth-error [error]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (js/console.log error)))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">When the user is logged in, we’ll show their profile picture as a button to logout. Let’s make a login button that calls </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">loginWithPopup</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(defn login-view []</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> [:div</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> {:style {:float "right"}}</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (if-let [{:keys [photoURL displayName]} @firebase/user]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> [:span</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> [:button.mdl-button.mdl-js-button.mdl-button--fab.mdl-button--colored</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> {:on-click</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (fn logout-click [e]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (firebase/logout))</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :title displayName</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :style {:background-image (str "url(" photoURL ")")</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :background-size "cover"</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :background-repeat "no-repeat"}}]]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> [:button.mdl-button.mdl-js-button.mdl-button--raised.mdl-button--colored</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> {:on-click</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (fn login-click [e]</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (firebase/sign-in-with-popup))}</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "Login with Google"])])</span></div>
<b style="font-weight: normal;"><br /></b><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s try logging in… it works!</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Now modify the database access rules to require user authentication.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "rules": {</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> ".read": true,</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "test": {</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> ".write": true</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> },</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "users": {</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> "$uid": {</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> ".write": "$uid === auth.uid"</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> }</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> }</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> }</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Writing to the users uid path now requires the writer to be authenticated.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We are limited to writing JSON values. But we can encode more complex data models. For this project we will be using Datascript as the model. Save the model using </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">pr-str</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> and load it with </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">edn/read-string</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(pr-str @conn)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(edn/read-string {:readers d/data-readers} in)</span></div>
<b style="font-weight: normal;"><br /></b>
<h3 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 21.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Build a UI to make use of the data</span></h3>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Users will add short titles of topics that interest them. I’ll render the titles as nodes in a graph.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><img height="372" src="https://lh3.googleusercontent.com/yfzRTV-Q59_ATR7SJ6ytI0b898xUQ9zFXQIzoFA7uz178mEh_dDMeddxyydBfIEyuyS3upcLC1HF5UnbtQjGBseQ_wyP1tZGCPA5lMWec_6t-5KBzIrAIdwNsEPh_d_qZsjpusn2" style="-webkit-transform: rotate(0.00rad); border: none; transform: rotate(0.00rad);" width="624" /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The nodes can be linked together. Users can put their faces on nodes to vote for them.</span></div>
<b style="font-weight: normal;"><br /></b>
<h3 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 21.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Deploy the project</span></h3>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The Firebase command line tool deploys our application. It is a NodeJS package that you can install by typing `npm install firebase`. Execute `firebase init` in your project directory to create configuration files. Database rules are specified in the </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">database.rules.json</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> file. The </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">firebase.json</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> file specifies a folder of files to copy for hosting. </span><a href="https://firebase.google.com/docs/hosting/deploying" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://firebase.google.com/docs/hosting/deploying</span></a></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Make a build task that creates a minified build to a `public` folder that contains our HTML and compiled JavaScript. Check that everything is working with `firebase serve`, which serves the public folder locally. To push the folder to the host, type `firebase deploy`. I put the release build and deployment step in a script and readme so I don’t forget it.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Our site is online, open to the public, and supports real-time collaboration.</span></div>
<b style="font-weight: normal;"><br /></b>
<h3 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 21.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Examine the impact of optimized compilation</span></h3>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">While developing it is convenient to use no optimizations because compilation is far faster. The main disadvantage of no optimizations is that the output goes into many separate files. When deploying, it is more convenient to have a single JavaScript output file.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Advance mode compilation does aggressive renaming, dead code removal, and global inlining, resulting in highly compressed JavaScript. This power comes with some restrictions. Advanced compilation mode makes assumptions about the code that it optimizes, and breaking these assumptions will result in errors. This is a problem for interop with JavaScript libraries. JavaScript libraries must be accompanied by extern definitions, or the aggressive renaming will cause interop calls to be undefined. </span><a href="https://developers.google.com/closure/compiler/docs/api-tutorial3" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://developers.google.com/closure/compiler/docs/api-tutorial3</span></a></div>
<b style="font-weight: normal;"><br /></b>
<h4 style="line-height: 1.38; margin-bottom: 4pt; margin-top: 16pt;">
<span style="background-color: transparent; color: #434343; font-family: Arial; font-size: 18.666666666666664px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Option 1: Deploy with whitespace optimization</span></h4>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I encourage you to avoid worrying about the effects of advanced optimization on interop by not using it at all. Configure your deployment build to use optimizations whitespace instead of advanced. This will produce a single output file suitable for deployment. The only downside is that your js file will be larger than it could otherwise be (for this project the output is 1.7Mb). For most purposes this is not noticeable.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">:compiler {:output-to "resources/public/js/compiled/firefire.js"</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :main firefire.core</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :optimizations :whitespace}</span></div>
<b style="font-weight: normal;"><br /></b>
<h4 style="line-height: 1.38; margin-bottom: 4pt; margin-top: 16pt;">
<span style="background-color: transparent; color: #434343; font-family: Arial; font-size: 18.666666666666664px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Options 2: Use or provide externs</span></h4>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">JavaScript functions and variables get aggressively renamed, causing interop to fail.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Extern definitions prevent renaming, so that interop will work correctly. For advanced compilation we need both the JavaScript source file and an externs file. An externs file is a JavaScript file that declares all the public interfaces.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">firebase.initializeApp = function(options, name) {};</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Externs files can also contain documentation annotation. Many libraries get distributed with externs. If you use npm to retrieve a library, check for an externs directory. If externs are not provided, you may be able to generate them using a tool like Michael’s (</span><a href="http://jmmk.github.io/javascript-externs-generator/" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">http://jmmk.github.io/javascript-externs-generator/</span></a><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">).</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Pro tip, you can also use the original JavaScript as it’s own externs file. You can turn off the warnings it will produce with configuration specified in David’s blog post here:</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<a href="http://swannodette.github.io/2014/03/14/externs-got-you-down" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">http://swannodette.github.io/2014/03/14/externs-got-you-down</span></a></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Declare foreign-libs is done by specifying an edn file like so:</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">{:foreign-libs </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br class="kix-line-break" /></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> [{:file "react/react.js"</span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br class="kix-line-break" /></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :file-min "react/react.min.js"</span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br class="kix-line-break" /></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :provides ["com.facebook.React"]}]</span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br class="kix-line-break" /></span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> :externs ["react/externs.js"]}</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<a href="https://github.com/clojure/clojurescript/wiki/Packaging-Foreign-Dependencies" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://github.com/clojure/clojurescript/wiki/Packaging-Foreign-Dependencies</span></a></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">You can specify a preamble for your build instead, which will prepend a JavaScript file to your final compiled output. But as we just saw, specifying a foreign lib is not much more work. In either case the important part is the externs.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In practise I recommend avoiding this option entirely in favor of using CLJSJS.</span></div>
<b style="font-weight: normal;"><br /></b>
<h4 style="line-height: 1.38; margin-bottom: 4pt; margin-top: 16pt;">
<span style="background-color: transparent; color: #434343; font-family: Arial; font-size: 18.666666666666664px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Option 3: Use CLJSJS as a blueprint</span></h4>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">CLJSJS is a standard way to package the foreign-libs definitions file with the externs and the JavaScript library so that you can use them as a dependency.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There is already a package defined for Firebase. To use it we just add it to our dependencies, and require it in our code. (Don’t forget to remove the include from your HTML file).</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">But what if we need a newer version? Let’s take a closer look at the definitions for the Firebase package. </span><a href="https://github.com/cljsjs/packages/blob/master/firebase/build.boot" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://github.com/cljsjs/packages/blob/master/firebase/build.boot</span></a></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The javascript and externs come from a NPM distribution, which is referenced by a version number. At this point it should be clear that all there is to a CLJSJS package is specifying the location of the JavaScript, the externs, and the namespace to be used when requiring.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To package a newer version we can checkout this code, bump the version, and install it locally with `boot package install target`.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">If the library we want to use doesn’t have a CLJSJS package, we can copy the definitions from another package. For example if the library we want to use is distributed on NPM, we could copy the Firebase package as a starting point for how to wrap it. CLJSJS provides a well documented blueprint for how to wrap JavaScript libraries to be compatible with advanced compilation.</span></div>
<b style="font-weight: normal;"><br /></b>
<h3 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 21.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Review JavaScript interop</span></h3>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Interop with JavaScript from ClojureScript is similar to interop with Java from Clojure.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">You call native methods using the dot or dot dot form.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(.method object arg1 arg2)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(. object method arg1 arg2)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(.. object method1 (method2 arg1 arg2))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">JavaScript has a global root object. To access the global root in ClojureScript, use the special js prefix.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">js/window</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(.setEventListener js/window (fn [e] (prn “hi”)))</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(js/window.setEventListener (fn ...)))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To access properties, ClojureScript differs from Clojure slightly in that you must use a dash to indicate property access. JavaScript does not distinguish properties from methods, so the dash is required to explicitly designate property access.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(.-property object)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(. object -property)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(.. object -property1 -property2)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(set! (.-property object) value)</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Array access is done with </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">aget</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> and </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">aset</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(aget array index)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(aget array index2 index2 index3)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(aset array index value)</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">You can chain access forms together.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(set! (.-foo (aget array index1)) value)</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Object constructors are the same as Clojure. You can use constructors from the global JavaScript root.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(js/THREE.Vector3. 0 0 0)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(new js/THREE.Vector3 0 0 0)</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Modules can be referenced and imported from the Closure Library.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(ns example</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (:import [goog.event KeyCode]))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">JavaScript objects can be created with </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">js-obj</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> or by using the special </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">#js</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> reader literal. These forms are not recursive. When nesting objects specify the children as literals.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(js-obj "key" "value")</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">#js {:key "value"}</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I recommend using ClojureScript data literals as much as possible, and only using JavaScript objects for interop with third party libraries.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There are transformation functions to convert from ClojureScript data structures to JavaScript objects. These are both recursive.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(clj->js)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(js->clj)</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">You can optionally keywordize-keys but keep in mind that not all strings can be keywords.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There is a penultimate escape hatch form </span><span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">js*</span><span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> that takes raw JavaScript.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(js* "some(javascript);")</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">JavaScript allows anything to be thrown, so the ClojureScript catch form has a special term default which will match anything. Use this to catch unexpected values being thrown.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(try</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (/ :1 2)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (catch :default ex))</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">You can annotate your functions with export to prevent them being name-mangled during advanced compilation. They will then be easily consumable from JavaScript.</span></div>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(defn ^:export f []</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: 'Courier New'; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (+ 1 2))</span></div>
<b style="font-weight: normal;"><br /></b>
<h2 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 20pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 26.666666666666664px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Conclusion</span></h2>
<b style="font-weight: normal;"><br /></b>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Firebase is a convenient hosting service you can leverage from ClojureScript. The free starter plan provides a database, hosting, and authentication. ClojureScript allows you to work with JavaScript APIs with interop code. For development you can include a JavaScript file from your HTML page. For deployment I recommend using whitespace optimization to avoid the build complications that advanced compilation introduces. When your project is mature and would benefit from shrinking with advanced mode compilation, you will need externs defined. CLJSJS is the best way to specify externs, and provides wrappers for many libraries already.</span></div>
<b style="font-weight: normal;"><br /></b><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Until next time,</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Keep coding.</span></div>
<br />Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-42595898005550711742016-06-26T13:31:00.002-07:002016-06-26T13:31:50.522-07:00Of Models and MetaphorsModels are how we understand, describe and predict the world around us. They are the basis of science and knowledge. Metaphors can give an intuitive feel for a model.<br />
<br />
Metaphors are useful for avoiding a complete understanding something. Avoiding a full understanding in favor of being content with some simple abstract idea. Feynman <a href="https://youtu.be/wMFPe-DwULM?t=6m8s">refuses to give examples of what physical phenomena are like</a> because there is nothing like an electron spinning around a nucleus that can give a better understanding of it. It is not like planets around the sun. The forces involved are completely different!<br />
<br />
Metaphors have their place. In a literary sense they create a connection with your audience and convey feeling and empathy. In a technical setting a metaphor can be used effectively to highlight fundamental properties of a model. They provide an excellent communication device. But beware of their use if you are searching for a precise understanding.<br />
<br />
Peter Norvig presents an excellent dive into <a href="http://videos.syntience.com/ai-meetups/peternorvig.html">Models and Theories</a> from a computer scientist's perspective. The key theme to me is that rich data produces better models than clever algorithms. There is a large class of problems that can be solved very well by models that are statistical in nature and not intuitively understandable by virtue of their size. Computers are allowing us to build models in ways that are beyond metaphor.<br />
<br />
At <a href="http://www.outpace.com/">Outpace</a> we use simple machine learning over large datasets to personalize content. It makes our customers lots of money. We routinely see a 20-60% lift in conversions, compared to business as usual. The value of data is very obvious in such a setting. We literally convert data into money.<br />
<br />
In science, access to data leads to amazing progress. Examples such as the human genome project illustrate the huge leap modeling can take when backed by rich data.<br />
<br />
For these reasons I am suspicious of explanations by metaphor. They rarely contain fidelity or utility to the degree that an explanation from data does. The best explanations present a model, or description of a model that is grounded in data.<br />
<br />
<br />
<div>
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc2VbhzkxXGl3u_fTLx5TOgmMUzwqRFodz6NvR78vxaLBcxXitp4QFm19mkKQUUKxrpackW7oZ-6GsT64Faj4nuI2Dbl_GUFl98xSzgAI3d8drAp7sNzDiztsC7OMWEBe-xoO8iw/s1600/IMAG0390.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="513" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc2VbhzkxXGl3u_fTLx5TOgmMUzwqRFodz6NvR78vxaLBcxXitp4QFm19mkKQUUKxrpackW7oZ-6GsT64Faj4nuI2Dbl_GUFl98xSzgAI3d8drAp7sNzDiztsC7OMWEBe-xoO8iw/s640/IMAG0390.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<span id="goog_1764794996"></span><span id="goog_1764794997"></span><br /></div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-38322499406416013192016-05-08T10:15:00.000-07:002016-05-08T10:15:10.707-07:00Composing test assertions in a pipelineHave you ever wanted to test the steps in a long pipeline of operations?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQz24K7jL_J5RfpTlaeI2AHojRp8osX4bKoAPsm5YSNtrCK03mdwZPtjTTL9PwGvsvijU8ixMHR4sqAXt5HK1TuNmJNbT7xj_Zle3H3wFi4RpkQG8Tk8TSSrG24h-E_os22e24jA/s1600/Capture-of-Blackbeard.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="454" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQz24K7jL_J5RfpTlaeI2AHojRp8osX4bKoAPsm5YSNtrCK03mdwZPtjTTL9PwGvsvijU8ixMHR4sqAXt5HK1TuNmJNbT7xj_Zle3H3wFi4RpkQG8Tk8TSSrG24h-E_os22e24jA/s640/Capture-of-Blackbeard.jpg" width="640" /></a></div>
<br />
"Edward Teach asking a co-worker why his threaded pipeline expression is failing in production" - <a href="http://www.neatorama.com/2007/10/22/pirate-lore-7-myths-and-trrrrruths-about-pirates/">By Jean Leon Gerome Ferris</a> - <a href="https://commons.wikimedia.org/w/index.php?curid=8643114">Public Domain</a>.<br />
<br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">(<span style="color: green;">-> </span><span style="color: #19177c;">starting-state</span>
(<span style="color: blue;">step1</span>)
(<span style="color: blue;">step2</span>)
(<span style="color: blue;">step3</span>))
</pre>
</div>
<br />
Wouldn't it be great if we could just insert some checks in between? We could write a test that checked the steps throughout the chain without creating a hand crafted state for each step:<br />
<br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">(<span style="color: green;">-> </span><span style="color: #19177c;">starting-state</span>
(<span style="color: blue;">step</span> <span style="color: #666666;">1</span>)
(<span style="color: blue;">has</span> <span style="color: #19177c;">x</span> (<span style="color: green;">contains? </span><span style="color: #19177c;">x</span> <span style="color: #19177c;">:foo</span>))
(<span style="color: blue;">step</span> <span style="color: #666666;">2</span>)
(<span style="color: blue;">has</span> <span style="color: #19177c;">x</span> (<span style="color: green;">not </span>(<span style="color: blue;">empty?</span> <span style="color: #19177c;">x</span>)))
(<span style="color: blue;">step</span> <span style="color: #666666;">3</span>)
(<span style="color: blue;">has</span> <span style="color: #19177c;">x</span> (<span style="color: green;">and </span>(<span style="color: green;">contains? </span><span style="color: #19177c;">x</span> <span style="color: #19177c;">:bar</span>) (<span style="color: green;">pos? </span>(<span style="color: #19177c;">:baz</span> <span style="color: #19177c;">x</span>)))))
</pre>
</div>
<br />
<!-- HTML generated using hilite.me -->Conceptually <span style="font-family: "courier new" , "courier" , monospace;">has</span> should return whatever we give it, after making an assertion about it. This can be achieved by a function, but using a function means that our test output will not contain the original form that we used to define the testing expression. If we want to preserve the original form of the assertion, we need to pass it all the way down to the <span style="font-family: Courier New, Courier, monospace;">clojure.test/is</span> assertion. We will need to use a macro.<br />
<br />
Clojure has two useful concepts to help us:<br />
<pre id="var-usage" style="background-color: white; border: 0px; font-family: Inconsolata, Monaco, Consolas, 'Lucida Console', 'Courier New', Courier, monospace; font-size: small; line-height: 19.5px;"></pre>
<pre id="var-usage" style="border: 0px;"><span style="background-color: white; font-family: "inconsolata" , "monaco" , "consolas" , "lucida console" , "courier new" , "courier" , monospace; font-size: x-small; line-height: 19.5px;">
</span></pre>
<pre id="var-usage" style="border: 0px;"><span style="background-color: white; font-family: "inconsolata" , "monaco" , "consolas" , "lucida console" , "courier new" , "courier" , monospace; font-size: x-small; line-height: 19.5px;">(doto x & forms)</span>
</pre>
Evaluates x then calls all of the methods and functions with the value of x supplied at the front of the given arguments. The forms are evaluated in order. Returns x.<br />
<pre id="var-docstr" style="background-color: white; border: 0px; font-family: Inconsolata, Monaco, Consolas, 'Lucida Console', 'Courier New', Courier, monospace; font-size: small; line-height: 19.5px;"></pre>
<pre id="var-usage" style="border: 0px;"><span style="background-color: white; font-family: "inconsolata" , "monaco" , "consolas" , "lucida console" , "courier new" , "courier" , monospace; font-size: x-small; line-height: 19.5px;">
</span></pre>
<pre id="var-usage" style="border: 0px;"><span style="background-color: white; font-family: "inconsolata" , "monaco" , "consolas" , "lucida console" , "courier new" , "courier" , monospace; font-size: x-small; line-height: 19.5px;">(as-> expr name & forms)</span>
</pre>
Binds name to expr, evaluates the first form in the lexical context
of that binding, then binds name to that result, repeating for each
successive form, returning the result of the last form.<br />
<br />
So we can write <span style="font-family: Courier New, Courier, monospace;">has</span> as a <span style="font-family: Courier New, Courier, monospace;">doto as-> is</span> combination:<br />
<br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">(<span style="color: green; font-weight: bold;">defmacro </span><span style="color: #19177c;">has</span>
([<span style="color: #19177c;">actual</span> <span style="color: #19177c;">sym</span> <span style="color: #19177c;">form</span>]
<span style="color: #666666;">`</span>(<span style="color: blue;">has</span> <span style="color: #666666;">~</span><span style="color: #19177c;">actual</span> <span style="color: #666666;">~</span><span style="color: #19177c;">sym</span> <span style="color: #666666;">~</span><span style="color: #19177c;">form</span> <span style="color: #19177c;">nil</span>))
([<span style="color: #19177c;">actual</span> <span style="color: #19177c;">sym</span> <span style="color: #19177c;">form</span> <span style="color: #19177c;">msg</span>]
<span style="color: #666666;">`</span>(<span style="color: green;">doto </span><span style="color: #666666;">~</span><span style="color: #19177c;">actual</span>
(<span style="color: blue;">as-</span><span style="color: #666666;">></span><span style="color: #408080; font-style: italic;"> ~sym (is ~form ~msg)))))</span>
</pre>
</div>
<br />
Now let's use our new superpower in a more realistic setting:<br />
<br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">(<span style="color: blue;">deftest</span> <span style="color: #19177c;">activating-abilities-test</span>
(<span style="color: green;">-> </span>{}
(<span style="color: blue;">world/with-status</span> (<span style="color: blue;">Date.</span> <span style="color: #666666;">0</span>) <span style="color: #ba2121;">"Blackbeard"</span> [<span style="color: #666666;">0</span> <span style="color: #666666;">0</span> <span style="color: #666666;">0</span> <span style="color: #666666;">0</span> <span style="color: #666666;">0</span> <span style="color: #666666;">0</span> <span style="color: #666666;">0</span>] <span style="color: #19177c;">:fire</span>)
(<span style="color: blue;">has</span> <span style="color: #19177c;">w</span> (<span style="color: blue;">world/activating</span> <span style="color: #19177c;">w</span> <span style="color: #ba2121;">"Blackbeard"</span>)
<span style="color: #ba2121;">"activating"</span>)
(<span style="color: blue;">ticker/tick</span> (<span style="color: blue;">Date.</span> <span style="color: #666666;">2000</span>))
(<span style="color: blue;">has</span> <span style="color: #19177c;">w</span> (<span style="color: green;">and </span>(<span style="color: green;">not </span>(<span style="color: blue;">world/activating</span> <span style="color: #19177c;">w</span> <span style="color: #ba2121;">"Blackbeard"</span>))
(<span style="color: blue;">get-in</span> <span style="color: #19177c;">w</span> [<span style="color: #19177c;">:players</span> <span style="color: #ba2121;">"Blackbeard"</span> <span style="color: #19177c;">:cooldowns</span> <span style="color: #19177c;">:fire</span>])
(<span style="color: blue;">get-in</span> <span style="color: #19177c;">w</span> [<span style="color: #19177c;">:players</span> <span style="color: #ba2121;">"Blackbeard"</span> <span style="color: #19177c;">:action-taken</span>]))
<span style="color: #ba2121;">"warmup complete"</span>)
(<span style="color: blue;">ticker/tick</span> (<span style="color: blue;">Date.</span> <span style="color: #666666;">7000</span>))
(<span style="color: blue;">has</span> <span style="color: #19177c;">w</span> (<span style="color: green;">not </span>(<span style="color: blue;">get-in</span> <span style="color: #19177c;">w</span> [<span style="color: #19177c;">:players</span> <span style="color: #ba2121;">"Blackbeard"</span> <span style="color: #19177c;">:cooldowns</span> <span style="color: #19177c;">:fire</span>]))
<span style="color: #ba2121;">"cooldown complete"</span>)))
</pre>
</div>
<br />
We now have a way to make logical assertions throughout a chain of events. Keep in mind that <span style="font-family: Courier New, Courier, monospace;">doto prn</span> is also a useful combination to insert into threaded expressions:<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">(<span style="color: green;">-> </span><span style="color: #19177c;">starting-state</span>
(<span style="color: blue;">step1</span>)
(<span style="color: green;">doto </span>(<span style="color: green;">prn </span><span style="color: #ba2121;">"***"</span>))
(<span style="color: blue;">step2</span>))
</pre>
</div>
<br />
This can be handy as a quick way to dump the intermediate values to stdout.<br />
<br />
When constructing pipeline tests it is convenient to be able to insert assertions that don't change the flow of execution and preserve the assertions original form when reporting failures.<br />
<br />Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-69215171001817422822016-04-18T00:01:00.001-07:002016-04-18T00:25:25.115-07:00Hallway track of Clojure/West<h2 dir="ltr" style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7xnk-LzJ5ddaCwUb2uqfYO4o5bxF1VTVkCgHbOVM6An3RaxWvErLAAmDxwzi54iLHXYaPHEZ0tXubEmyN5utrG4keShfyH9VKn0z_RBydWA2bztcu3_QIVdOh5mmimeCjTuZZIg/s1600/IMAG0314.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7xnk-LzJ5ddaCwUb2uqfYO4o5bxF1VTVkCgHbOVM6An3RaxWvErLAAmDxwzi54iLHXYaPHEZ0tXubEmyN5utrG4keShfyH9VKn0z_RBydWA2bztcu3_QIVdOh5mmimeCjTuZZIg/s320/IMAG0314.jpg" width="220" /></a><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 21.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Talk Recommendations</span></h2>
<b id="docs-internal-guid-2ac9f219-2820-90d9-48b9-4789f90a1aaf" style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="margin-bottom: 10pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; line-height: 1.38; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Deepen and Diversify the Clojure Community with Jr Engineers - Amie Kuttruff </span><span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;"><a href="https://www.youtube.com/watch?v=SOIhncWcCjA" style="font-size: 14.6667px; line-height: 1.38; text-decoration: none;">https://www.youtube.com/watch?v=SOIhncWcCjA</a></span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"> Excellent guide to learning Clojure or any programming language. Great advice for starting your journey, or helping others. Fun, stylish and entertaining. Had an engaging story arc.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="margin-bottom: 10pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; line-height: 1.38; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">USE lisp WITH game - Making an Adventure Game with Clojure - Bryce Covert </span><span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;"><a href="https://www.youtube.com/watch?v=lql2yFXzKUs" style="font-size: 14.6667px; line-height: 1.38; text-decoration: none;">https://www.youtube.com/watch?v=lql2yFXzKUs</a></span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"> Tick’s Tale - fun talk, very entertaining and nostalgic. Perfect balance between dive into some deep concepts in a very approachable, entertaining way. Dialog trees was very interesting.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Datalog all the way down by Christopher Small</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;"><a href="https://www.youtube.com/watch?v=aI0zVzzoK_E" style="text-decoration: none;">https://www.youtube.com/watch?v=aI0zVzzoK_E</a></span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"> This is the way to build applications. I've experienced big benefits on a smaller scale by using similar but less advanced techniques. I love his vision on this and am watching this space closely. It is interesting to see the rise of Datalog in various contexts going on.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Creating DSLs A tale of spec tacular success and failure - Claire Alvis</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<a href="https://www.youtube.com/watch?v=cO67QNn9hPY" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://www.youtube.com/watch?v=cO67QNn9hPY</span></a></div>
<b style="font-weight: normal;">Stylish and engaging talk. Spectacular provides a much needed easy entry to Datomic. Claire used an excellent set of dimensions to explore non obvious, but important </b>principles<b style="font-weight: normal;"> of software design. </b><br />
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">All the talks are great. Watch them all on ClojureTV.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Many women speakers gave talks, and they delivered excellent content. Over the years all my women colleagues have been exceptional at their work. Better than their male counterparts. I am proud to be in a community that promotes community leaders that merit our admiration technically, and are people that our community aspires to emulate. Clojure has fantastic role models for young women considering a career in tech. Thank you to Lynn, Kim, Alex, and all the organizers for inviting speakers who we can respect both on and off the stage for both their exploits in code and their desire to share and build a friendly community. The software world is currently missing out on a huge pool of talent.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<h2 dir="ltr" style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 21.333333333333332px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Hallway track</span></h2>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The Engelbergs won most referenced this conference. Many of talks and discussions brought up Instaparse, PriorityMap, and their learning curriculum. Previous Conj winner was Carin Meier. Animal gifs were also popular (cats and corgis mostly!)</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I bumped into Shaun, who’s latest inspirational exploit is Parinfer. I recently stumbled upon his YouTube channel </span><a href="https://www.youtube.com/channel/UCrNdfaEcqRUqObaGykbC9Lw" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">https://www.youtube.com/channel/UCrNdfaEcqRUqObaGykbC9Lw</span></a><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> which is well worth subscribing to. It was really cool to see his ClojureScript compiler console from 2 years ago which inspired the figwheel console. Shaun makes really innovative stuff. Watch his lightning talk from the DC Conj to have your mind blown.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I was really pleased to be able to spend time with colleagues past and present. Tanya, Rusty, Daniel, Mario, Bryce, Ben; I really enjoyed seeing you all.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Conor has a really cool site learnathon.org and shared a wealth of information about how people learn: Deadlines. 5 people learning together. Summarize a chapter each. Builds relationships. Accountability to each other. Small problems. Setting goals. Asking for help is difficult, so make people feel welcome. It is uncomfortable to speak up in a large group of unknown people (or slack channel). I would rather go to school than X. Many people want to learn to communicate better. Actors and artists have a lot to contribute. It is lonely to learn on your own. Learning could be a choose your own adventure. Dialog trees? Choose new lessons just slightly outside your current range. Is there a way to track sub-problems to solve in a project? Keep track of what people have learnt and can teach others? Teaching what you just learn means it is fresh in your mind, re-enforces it, and you know the difficulties that the learner is facing. Node of where you are, node where you what you want to build, it has dependencies, you need to acquire those, other people have them and can help you.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Knowledge is a graph. Learning can be a graph. Serendipity can be created. Code reviews are very valuable, having a small group of people connected by goals facilitates finding people able to, willing to, and interested to do code reviews. Good metaphors help. Sometimes the explanation is in a language I don’t know yet. Learn it teach it.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">It was a very serendipitous meeting, prompted by a very simple but compelling question he posed: “What are you thinking about?” A far more interesting question that I ask in my small talk, I’m going to adopt this one. Instantly connected us on something I was intensely interested in. Ideas are connected. Geography is a dark ages dimension I want to leave behind. I would like to build a Serendipity machine. Matching up small groups of people who can mutually benefit each other. Meetups, study groups, conferences are geography bound. Online is an opportunity to be more explicit about the dimensions to collide on. Online is unexploited, because groups are too large, and search is too direct. Online questions are unidirectional.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">A shared thread that unites a small group that is not their primary mission. Knowledge is a graph (or tree, not sure). People are connected. LinkedIn has people’s skills, but need more fine grained info.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Would type auto-annotating be a good thing? Clojure code often follows naming conventions which strongly suggest a type; m for maps, v for vectors, xs for seqs, s for strings, n for numbers, nat for natural numbers etc. Generating Core.Typed annotations for these should be easy. Not sure how valuable it would be, but seems like a it could be both invisible (annotation in a separate file), and occasionally find compile time errors.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jonathan from CircleCI indicated that their business continues to grow, and talked about how they are able to scale, rollout and test improvements to their build automation infrastructure while continuously serving so many customers. Thanks for sharing, I was really impressed at the scale you are reaching.</span></div>
<b style="font-weight: normal;"><br /></b>
<b style="font-weight: normal;">Rob was interested in Clojure from a strategic advisory perspective.</b><br />
<b style="font-weight: normal;"><br /></b>
<b style="font-weight: normal;">Everyone I spoke to had an interesting story to tell, and were working on really cool stuff.</b><br />
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Lots of people were looking for work in Clojure. Lots of people and teams are converting from PHP/.NET to Clojure.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Many great companies big and small are actively hiring:</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Amperity. Derek has assembled a Clojure swat team and is looking to expand a few senior roles. Given their past successes and calibre, I am sure they will succeed with their ambitious and widely applicable product.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Simple. A better way to bank. Cate clearly loved the product and company she works for. Simple has a fantastic culture and a solid customer base who love the features and level of service. Half the team works remotely. Recently recommended as a better alternative to Google Wallet.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Capital One was actively seeking new recruits in many locations. They already have a strong team and looking to grow it.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Clojure is on the approved languages list of Boeing.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There are far more companies with Clojure teams than I could keep track of. There is still a perception that it is hard to hire Clojure developers, and that it is hard to find work as a Clojure developer. Yet at the same time many people were building amazing things after only converting to Clojure a matter of months ago. I met many people from lots of companies I hadn’t heard of before doing amazing things with Clojure.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Quite a lot of the people I met worked remotely. There were mixed reports on the effectiveness of working remote. Several community respected and experienced software engineers related;</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">A: “It is currently surprisingly hard to find remote opportunities.”</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">B: “I am considering starting a remote team, and wondering whether the benefits outweighed the negatives compared to a collocated team.”</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">C: “We like remote because of the hiring pool, support hour coverage, diversity that our international staff bring, and local business connections.”</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">D: “Remote is a hinderance, and it is boring to be on my own. Nobody to have lunch with. Isolation. Social aspect lacking. Communication tech sux. Less productive. Works for consulting based. Need an MC for meetings to make them work. One week onsite at start of any project is an absolute must. But the best opportunities are remote, and I wouldn’t give up my current role for a collocated one that was less interesting.”</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">E: “Remote is not currently working for us. I am left out; not aligned on priorities, miss out on communication, unaware of office politics, and isolated. I think we may overcome these issues as we are actively hiring more remotes and transitioning to a more remote oriented model.”</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I was quite surprised that opinions on remote were so varied. My experiences have been very positive, but clearly this is not a widely viable approach yet. Geography is still a thing. No one else seemed to be using explicit pairing, just informal as needed interactions.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">A recurring theme was that Context matters. Mario’s excellent talk explored how contextual conversations are. Bryce’s excellent talk was full of ways to express and travel through context. Amie’s excellent talk emphasized the importance of context for learning and teaching.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Take time to think. Build a layer to solve a problem. Clojure enables small teams to achieve big results.</span></div>
<br />Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-81018156834878551472016-03-18T23:48:00.000-07:002016-09-11T17:07:11.664-07:00Sente Style Multiplayer Snake in ClojureScript<div id="app">
Watch the <a href="https://youtu.be/3NZJjwv6yy0">Screencast</a></div>
Read the <a href="https://github.com/timothypratley/snakelake">Source Code</a><br />
Play the game <a href="https://timothypratley.github.io/snakelake/">Standalone</a><br />
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Coder X here,</span></div>
<b id="docs-internal-guid-44f8650b-8d1a-cf03-0528-9e7162e75b7f" style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Today we will build a multiplayer snake game. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Players connect to a central server and eat green food dots to grow. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We will use websockets to communicate between the browser and server. </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">One concern when using websockets is browser support. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><a href="https://github.com/ptaoussanis/sente">Sente</a> is a library which addresses this concern by falling back to ajax when websockets are not available.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><img height="213" src="https://lh6.googleusercontent.com/yZtcr4xq3GcObOgKRP_4QSpzDkfKlXT8pfyTvhRyJ9OkWauP29QMluxsDbrSIW8gyPdqxf4VkkBBxrTcreqwHJSTI7yp7itEIM6UjX6BJAOaHmD25EkKYtGCHAlJKVYmU_BY2QVW" style="-webkit-transform: rotate(0.00rad); border: none; transform: rotate(0.00rad);" width="624" /></span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We will use Sente to collect player direction changes, and push out the new state of the world every time it updates. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Let’s get started with a new ClojureScript project.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">First we create the game client. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We set up three namespaces:</span></div>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Main - the entry point that initializes our game</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Model - to store and manipulate the data for our game</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">View - for components to be rendered on the page</span></div>
</li>
</ul>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Our game board will be a 20 by 20 matrix. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We store our board and players in app-state. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Players will have a current position, direction, length, and tail path. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We need a ticker that updates the game world at regular intervals. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Each update will be calculated by the step function.</span></div>
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">defn </span><span style="color: black;">new-board</span> <span style="color: black; font-weight: bold;">[]</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: black;">vec</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">repeat </span><span style="color: black;">width</span> <span style="color: black; font-weight: bold;">(</span><span style="color: black;">vec</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">repeat </span><span style="color: black;">height</span> <span style="color: black;">nil</span><span style="color: black; font-weight: bold;">)))))</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">defonce </span><span style="color: black;">world</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">ref </span><span style="color: black; font-weight: bold;">{</span><span style="color: #4e9a06;">:board</span> <span style="color: black; font-weight: bold;">(</span><span style="color: black;">new-board</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:players</span> <span style="color: black; font-weight: bold;">{}}))</span>
</pre>
</div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s see how we might draw the game board. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">For each position in the matrix, we may draw a snake body segment. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We start a ticker that updates player positions, a</span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">nd some code to trim the tails of snakes as they move. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Snakes are slithering.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Let’s adjust the look of the segments to taste. </span><span style="font-family: "arial"; font-size: 14.6667px; vertical-align: baseline; white-space: pre-wrap;">We need to handle keyboard input, s</span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">o let’s add a listener that sets the next direction the snake should travel in. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Great, we can steer the snake around now. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Let’s add some eyes so it is obvious where the head is. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We put two circle elements inside a group element that is translated to the player location.</span></div>
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">defn </span><span style="color: black;">segment</span> <span style="color: black; font-weight: bold;">[</span><span style="color: black;">uid</span> <span style="color: black;">i</span> <span style="color: black;">j</span> <span style="color: black;">me?</span><span style="color: black; font-weight: bold;">]</span>
<span style="color: black; font-weight: bold;">[</span><span style="color: #4e9a06;">:rect</span>
<span style="color: black; font-weight: bold;">{</span><span style="color: #4e9a06;">:x</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">+ </span><span style="color: black;">i</span> <span style="color: #0000cf; font-weight: bold;">0.55</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:y</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">+ </span><span style="color: black;">j</span> <span style="color: #0000cf; font-weight: bold;">0.55</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:fill</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">subs </span><span style="color: black;">uid</span> <span style="color: #0000cf; font-weight: bold;">0</span> <span style="color: #0000cf; font-weight: bold;">7</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:stroke-width</span> <span style="color: #0000cf; font-weight: bold;">0.3</span>
<span style="color: #4e9a06;">:stroke</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">subs </span><span style="color: black;">uid</span> <span style="color: #0000cf; font-weight: bold;">7</span> <span style="color: #0000cf; font-weight: bold;">14</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:rx</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">if </span><span style="color: black;">me?</span> <span style="color: #0000cf; font-weight: bold;">0.4</span> <span style="color: #0000cf; font-weight: bold;">0.2</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:width</span> <span style="color: #0000cf; font-weight: bold;">0.9</span>
<span style="color: #4e9a06;">:height</span> <span style="color: #0000cf; font-weight: bold;">0.9</span><span style="color: black; font-weight: bold;">}])</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">defn </span><span style="color: black;">food</span> <span style="color: black; font-weight: bold;">[</span><span style="color: black;">i</span> <span style="color: black;">j</span><span style="color: black; font-weight: bold;">]</span>
<span style="color: black; font-weight: bold;">[</span><span style="color: #4e9a06;">:circle</span>
<span style="color: black; font-weight: bold;">{</span><span style="color: #4e9a06;">:cx</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">inc </span><span style="color: black;">i</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:cy</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">inc </span><span style="color: black;">j</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:r</span> <span style="color: #0000cf; font-weight: bold;">0.45</span>
<span style="color: #4e9a06;">:fill</span> <span style="color: #4e9a06;">"lightgreen"</span>
<span style="color: #4e9a06;">:stroke-width</span> <span style="color: #0000cf; font-weight: bold;">0.2</span>
<span style="color: #4e9a06;">:stroke</span> <span style="color: #4e9a06;">"green"</span><span style="color: black; font-weight: bold;">}])</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">defn </span><span style="color: black;">pixel</span> <span style="color: black; font-weight: bold;">[</span><span style="color: black;">uid</span> <span style="color: black;">i</span> <span style="color: black;">j</span> <span style="color: black;">my-uid</span><span style="color: black; font-weight: bold;">]</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">if </span><span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">= </span><span style="color: black;">uid</span> <span style="color: #4e9a06;">"food"</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: black; font-weight: bold;">[</span><span style="color: black;">food</span> <span style="color: black;">i</span> <span style="color: black;">j</span><span style="color: black; font-weight: bold;">]</span>
<span style="color: black; font-weight: bold;">[</span><span style="color: black;">segment</span> <span style="color: black;">uid</span> <span style="color: black;">i</span> <span style="color: black;">j</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">= </span><span style="color: black;">my-uid</span> <span style="color: black;">uid</span><span style="color: black; font-weight: bold;">)]))</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">defn </span><span style="color: black;">eye</span> <span style="color: black; font-weight: bold;">[</span><span style="color: black;">dx</span> <span style="color: black;">dy</span><span style="color: black; font-weight: bold;">]</span>
<span style="color: black; font-weight: bold;">[</span><span style="color: #4e9a06;">:circle</span>
<span style="color: black; font-weight: bold;">{</span><span style="color: #4e9a06;">:cx</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">/ </span><span style="color: black;">dx</span> <span style="color: #0000cf; font-weight: bold;">2</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:cy</span> <span style="color: black; font-weight: bold;">(</span><span style="color: #204a87;">/ </span><span style="color: black;">dy</span> <span style="color: #0000cf; font-weight: bold;">2</span><span style="color: black; font-weight: bold;">)</span>
<span style="color: #4e9a06;">:r</span> <span style="color: #0000cf; font-weight: bold;">0.2</span>
<span style="color: #4e9a06;">:stroke</span> <span style="color: #4e9a06;">"black"</span>
<span style="color: #4e9a06;">:stroke-width</span> <span style="color: #0000cf; font-weight: bold;">0.05</span>
<span style="color: #4e9a06;">:fill</span> <span style="color: #4e9a06;">"red"</span><span style="color: black; font-weight: bold;">}])</span>
</pre>
</div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We are now ready to work on the server side of our game. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Let’s copy the example code from the Sente website to set up our websockets. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">On the server side we have compojure routes to accept the socket. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We’ll host these routes in a HTTP-kit server because HTTP-kit supports websockets.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Ring reload middleware is essential for development. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">It will watch the filesystem for changes in our code, a</span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">nd load them when we call the server. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">When we make changes, we don’t need to restart or send to our REPL integration. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We just save our changes, and see the effect immediately.</span><br />
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The server is receiving events. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The events are identified with a unique keyword. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We dispatch handling of events with a multimethod. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Our first custom handler will be for the :snakelake/dir event.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We’ll start by just printing the event we receive.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">defmethod </span><span style="color: black;">event</span> <span style="color: #4e9a06;">:snakelake/dir</span> <span style="color: black; font-weight: bold;">[{</span><span style="color: #4e9a06;">:as</span> <span style="color: black;">ev-msg</span> <span style="color: #4e9a06;">:keys</span> <span style="color: black; font-weight: bold;">[</span><span style="color: black;">event</span> <span style="color: black;">uid</span> <span style="color: black;">?data</span><span style="color: black; font-weight: bold;">]}]</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">let </span><span style="color: black; font-weight: bold;">[[</span><span style="color: black;">dx</span> <span style="color: black;">dy</span><span style="color: black; font-weight: bold;">]</span> <span style="color: black;">?data</span><span style="color: black; font-weight: bold;">]</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: black;">model/dir</span> <span style="color: black;">uid</span> <span style="color: black;">dx</span> <span style="color: black;">dy</span><span style="color: black; font-weight: bold;">)))</span>
</pre>
</div>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Now we will move the model code we built up in our client code over to the server. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Isn’t it convenient that we are using Clojure in both places? </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Instead of an atom, the server should use a ref to model world state. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Refs provide transactional guarantees, and we want to handle concurrent user access.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">When players connect, we’ll assign them a random colors as their identifier. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The ticker will be a thread instead of an interval callback. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We’ll broadcast the entire world every tick. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">On the client side, our model now gets replaced with whatever we hear from the server. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Our client sends directions to the server, which updates the player state in the server model. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Every tick, the world is updated and broadcast to all connected clients. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">When we run two browsers, we can see them both updating together.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: black; font-weight: bold;">(</span><span style="color: #204a87; font-weight: bold;">defn </span><span style="color: black;">dir</span> <span style="color: black; font-weight: bold;">[</span><span style="color: black;">dx</span> <span style="color: black;">dy</span><span style="color: black; font-weight: bold;">]</span>
<span style="color: black; font-weight: bold;">(</span><span style="color: black;">chsk-send!</span> <span style="color: black; font-weight: bold;">[</span><span style="color: #4e9a06;">:snakelake/dir</span> <span style="color: black; font-weight: bold;">[</span><span style="color: black;">dx</span> <span style="color: black;">dy</span><span style="color: black; font-weight: bold;">]]))</span>
</pre>
</div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">At this point we have a multiplayer game. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The server and client message handling code is very similar. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Communication is expressed as event handling.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We need a host that will run our server process, not just serve static files. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><a href="https://www.heroku.com/home">Heroku</a> has a free hosting option, easy deployment steps, and excellent documentation. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We define how the server should run our code in a Procfile. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We’ll use java to execute an uberjar. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">An uberjar contains all our code, compiled Javascript, HTML resources, and dependencies. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">With this uberjar, the host can run the game loop process, serve files, and respond to requests.</span><br />
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span>
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"> <span style="color: #4e9a06;">:uberjar</span> <span style="color: black; font-weight: bold;">{</span><span style="color: #4e9a06;">:hooks</span> <span style="color: black; font-weight: bold;">[</span><span style="color: black;">leiningen.cljsbuild</span><span style="color: black; font-weight: bold;">]</span>
<span style="color: #4e9a06;">:aot</span> <span style="color: #4e9a06;">:all</span>
<span style="color: #4e9a06;">:cljsbuild</span> <span style="color: black; font-weight: bold;">{</span><span style="color: #4e9a06;">:builds</span>
<span style="color: black; font-weight: bold;">[{</span><span style="color: #4e9a06;">:id</span> <span style="color: #4e9a06;">"min"</span>
<span style="color: #4e9a06;">:source-paths</span> <span style="color: black; font-weight: bold;">[</span><span style="color: #4e9a06;">"src"</span> <span style="color: #4e9a06;">"prod"</span><span style="color: black; font-weight: bold;">]</span>
<span style="color: #4e9a06;">:compiler</span> <span style="color: black; font-weight: bold;">{</span><span style="color: #4e9a06;">:main</span> <span style="color: black;">snakelake.main</span>
<span style="color: #4e9a06;">:output-to</span> <span style="color: #4e9a06;">"resources/public/js/compiled/snakelake.js"</span>
<span style="color: #4e9a06;">:optimizations</span> <span style="color: #4e9a06;">:advanced</span>
<span style="color: #4e9a06;">:pretty-print</span> <span style="color: black;">false</span><span style="color: black; font-weight: bold;">}}]}}}</span>
</pre>
</div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">As this is a server process, we have the option to enable ahead of time compilation. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Ahead of time compiled code starts faster, but it brings some weirdness. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We need to pay careful attention to the Sente Lifecycle. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Top level forms are executed during ahead of time compilation. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">It doesn’t make any sense to establish a socket server while compiling. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">So we need to reorganize our code a little bit to have a start function. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Alternatively we could have chosen to avoid AOT completely..</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We have our deployment unit, so let’s try running it locally. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Making adjustments locally is faster than a full round trip deploy. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Ok, things are looking good, so let’s deploy to the hosting provider.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Push the repository to Heroku, and our game is now live.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Setting up bidirectional communication is easy! </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The communication model is simple and concise.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Clojure lends itself to separation of concerns:</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
</div>
<ul>
<li><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Communication is event handling oriented.</span></li>
<li><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The model is data and transformations.</span></li>
<li><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The interface is a function of the data.</span></li>
</ul>
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Most importantly, building things is fast and fun.</span><br />
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I hope you enjoy playing Snake Lake, a</span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">nd that you will build something even better.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Until next time,</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Keep coding.</span></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikNCFJ7x4C1xQS-kHSZySBxnRHrSdlwHIxjdakLuZs5CpD_0D01_8bJ0dOk-pFhJ2CkfL3g2pXgGiwkD2Kd0lrDLLcE910R-rperWQ9qgFYd2jytao5EKAps4isQfJQ8tSn_N4uQ/s1600/snake-icon.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikNCFJ7x4C1xQS-kHSZySBxnRHrSdlwHIxjdakLuZs5CpD_0D01_8bJ0dOk-pFhJ2CkfL3g2pXgGiwkD2Kd0lrDLLcE910R-rperWQ9qgFYd2jytao5EKAps4isQfJQ8tSn_N4uQ/s1600/snake-icon.png" /></a></div>
<br /></div>
</div>
<style>
body {
background-color: #7CAFC2;
font-family: courier;
}
button, input {
font-family: courier;
text-align: center;
}
.board {
cursor: pointer;
width: 90%;
height: 60vh;
}
h1 {
color: #F8F8F8;
background-color: #A1B56C;
text-align: center;
}
button {
font-size: 100%;
background-color: #A16946;
color: #F8F8F8;
border-radius: 10px;
box-shadow: 5px 5px 5px #888888;
}
.number {
text-align: right;
}
</style>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-37488670081493193722016-03-12T12:11:00.000-08:002016-03-12T12:11:43.565-08:00Do not settle for a REPL transcript<div dir="ltr" style="margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; font-size: 14.6667px; line-height: 22.4889px; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "arial";">Transcript for <a href="https://www.youtube.com/watch?v=ZD8q3fMr5sE"><b>"Do not settle for a REPL"</b></a> screencast:</span></span></div>
<div dir="ltr" style="margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; font-size: 14.6667px; line-height: 22.4889px; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "arial";"><a href="https://www.youtube.com/watch?v=ZD8q3fMr5sE">https://www.youtube.com/watch?v=ZD8q3fMr5sE</a></span></span><br />
<span style="background-color: transparent; font-size: 14.6667px; line-height: 22.4889px; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx49e8MgiptvOdgem9lzSRa_X_zBItuxSeCkWaixVjG9yB_9-MIymJX2Ku-laA5-g2Gx6nqachyphenhyphen_IGiCvxxEnp8n1fSv0YkJIx7SDD-2PLNqwbllWYQwRLtr4jfa2nD1UOPkPIGQ/s1600/IMAG0265.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx49e8MgiptvOdgem9lzSRa_X_zBItuxSeCkWaixVjG9yB_9-MIymJX2Ku-laA5-g2Gx6nqachyphenhyphen_IGiCvxxEnp8n1fSv0YkJIx7SDD-2PLNqwbllWYQwRLtr4jfa2nD1UOPkPIGQ/s200/IMAG0265.jpg" width="112" /></a><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">CoderX here.</span></div>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Today we will be using <a href="https://github.com/jakemcc/lein-test-refresh">test-refresh</a> to find influential Twitter friends. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The goal is to illustrate why code reload is better than a REPL. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Test-refresh watches the file-system, reloads code, and runs tests. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Jake McCrary wrote test-refresh while sporting a smart bow-tie. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Programming is serious business.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioZ6NYdk4A10K-SsfZQasvRrubBB94ckuzo3q1VL5oEkHRVleJYtuFYhom6v5BuSm4eGJLCak9tXIfk-YqO1F_LKAluGldrk30eu9asx_Z6c2yzNJ8wBbrPd5UdCQLJWgkAzsNPQ/s1600/jake.jpeg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioZ6NYdk4A10K-SsfZQasvRrubBB94ckuzo3q1VL5oEkHRVleJYtuFYhom6v5BuSm4eGJLCak9tXIfk-YqO1F_LKAluGldrk30eu9asx_Z6c2yzNJ8wBbrPd5UdCQLJWgkAzsNPQ/s200/jake.jpeg" width="200" /></a></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioZ6NYdk4A10K-SsfZQasvRrubBB94ckuzo3q1VL5oEkHRVleJYtuFYhom6v5BuSm4eGJLCak9tXIfk-YqO1F_LKAluGldrk30eu9asx_Z6c2yzNJ8wBbrPd5UdCQLJWgkAzsNPQ/s1600/jake.jpeg" imageanchor="1"></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKbS5nKYwFKFMoeOnYciyWRicDwN7OlYP0AxaYViz3k3e8Srkv9Iy75H5EaQtMINdng2_OLNoio2Xa3C-zXPIYxF4nMUjzGKorl5erQU1HX7ROARp9iwAszWr3EegcmVFHXOTKuQ/s1600/jake2.png" imageanchor="1"><img border="0" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKbS5nKYwFKFMoeOnYciyWRicDwN7OlYP0AxaYViz3k3e8Srkv9Iy75H5EaQtMINdng2_OLNoio2Xa3C-zXPIYxF4nMUjzGKorl5erQU1HX7ROARp9iwAszWr3EegcmVFHXOTKuQ/s400/jake2.png" width="400" /></a></span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">A REPL is a read eval print loop. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Forms are read interactively, evaluated, and the result of evaluation is printed to the screen. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">But code reloading is better than a REPL.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">So do not settle for a REPL.</span></div>
<b style="font-weight: normal;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmC1F-GpBWbhtaId2BFyfbA6iZiFAV4Gz8TSjQHV7rUqFGzZDx1nGyfA_kXX21OKlzIbUUwYulUhKwxvi4CubeBumeaYFtBILVHMxj9U-LG0yb7DLbxOj6PZhloD0N7fOiEQo6FA/s1600/Screen+Shot+2016-03-05+at+11.30.10+PM.png" imageanchor="1"></a><img border="0" height="131" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj50Fl0zDKB1lOsoFD_3Le5PCAPu78ERk4sGEygUazY9XC1cYruLTBwA4N39PrcdMzVYJ0iE5vEre5tp0pICzIwejA6TLuR9oyuZbtvHJr9zweVU-Kx1CxAgPppYSS-L67wUYuOww/s400/Screen+Shot+2016-03-06+at+12.24.00+AM.png" width="400" /> vs <img border="0" height="175" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmC1F-GpBWbhtaId2BFyfbA6iZiFAV4Gz8TSjQHV7rUqFGzZDx1nGyfA_kXX21OKlzIbUUwYulUhKwxvi4CubeBumeaYFtBILVHMxj9U-LG0yb7DLbxOj6PZhloD0N7fOiEQo6FA/s200/Screen+Shot+2016-03-05+at+11.30.10+PM.png" width="200" /></b><br />
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There are three mature code reload workflows in Clojure:</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
</div>
<ul>
<li><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Figwheel. We showed off how cool Figwheel is in previous episodes.</span></li>
<li><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Ring reload middleware. An absolute must for server side development.</span></li>
<li><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Test-refresh. The focus of this episode. Beats REPLs hands down.</span></li>
</ul>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">They all watch files and reload code. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The trigger for code evaluation is when we save a file,</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">which is usually the right punctuation point in our workflow. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Saving files works with any editor with no special plugins or keystrokes. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The model is easy to understand and think about.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Before we get coding, I’ll make a quick disclaimer. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">This is not a testing rant.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">This is about using code reload for an interactive workflow. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">So don’t be surprised when I don’t write tests… </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">I want to compare apples to apples, </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">experimenting from the REPL with experimenting from test-refresh.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To find influential Twitter friends I need to </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Connect to Twitter. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Fetch a list of my friends. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">For each friend, fetch their friends. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">And then Pagerank the network.</span></div>
<b style="font-weight: normal;"><br /></b>
<b style="font-weight: normal;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiK1cEV1ZDkvRbyLWQWarZkFqeJOwZAk2ndwIyo_UPF1hnuqtUUI7-ZwA16w0ypWpnOUHhQKuPwzcu8L_nixfeSp6lkaXVn2_acf0v4PTfLQjJNes2PT4Pzwem6pMF-VLOV2ZAAvA/s1600/Screen+Shot+2016-03-12+at+10.59.06+AM.png" imageanchor="1"><img border="0" height="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiK1cEV1ZDkvRbyLWQWarZkFqeJOwZAk2ndwIyo_UPF1hnuqtUUI7-ZwA16w0ypWpnOUHhQKuPwzcu8L_nixfeSp6lkaXVn2_acf0v4PTfLQjJNes2PT4Pzwem6pMF-VLOV2ZAAvA/s320/Screen+Shot+2016-03-12+at+10.59.06+AM.png" width="320" /></a></b><br />
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s start a new project called twitternet. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Add <a href="https://github.com/adamwynne/twitter-api">twitter-api</a> to our project dependencies</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Navigate to </span><a href="https://apps.twitter.com/" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">apps.twitter.com</span></a><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> and click “create new app”. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">I deleted this twitter app after gathering the data I needed, </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">so these credentials are no longer live. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">You will need to create your own credentials. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Add <a href="https://github.com/dakrone/clj-http">clj-http</a> as a dependency.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s make some calls by copying the examples. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">A few print statements allows us to examine the shape of the data. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">At this point we discover that rate limiting is quite severe, </span><span style="font-family: "arial"; font-size: 14.6667px; vertical-align: baseline; white-space: pre-wrap;">only 1 request per minute is allowed. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Let’s be sure to save the output to files so we can use them later.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">(<span style="color: green; font-weight: bold;">ns </span><span style="color: #19177c;">twitternet.core</span>
(<span style="color: #19177c;">:require</span>
[<span style="color: #19177c;">twitter.oauth</span> <span style="color: #19177c;">:as</span> <span style="color: #19177c;">oauth</span>]
[<span style="color: #19177c;">twitter.api.restful</span> <span style="color: #19177c;">:as</span> <span style="color: #19177c;">rest</span>]))
(<span style="color: green; font-weight: bold;">def </span><span style="color: #19177c;">my-creds</span>
(<span style="color: blue;">oauth/make-oauth-creds</span>
<span style="color: #ba2121;">"pfraQVsp9gYM1hxENMVfSfMBQ"</span>
<span style="color: #ba2121;">"uDFD9sEjTQjholVJkItJXyo11Suvjsx4Gk5KKiBMmNQzUDUfo9"</span>))
(<span style="color: green; font-weight: bold;">defn </span><span style="color: #19177c;">fetch-friends</span> [<span style="color: #19177c;">id</span>]
(<span style="color: blue;">Thread/sleep</span> <span style="color: #666666;">60000</span>)
(<span style="color: green;">println </span><span style="color: #ba2121;">"Fetching friends"</span> <span style="color: #19177c;">id</span>)
(<span style="color: green;">doto </span>(<span style="color: #19177c;">:ids</span> (<span style="color: #19177c;">:body</span> (<span style="color: blue;">rest/friends-ids</span>
<span style="color: #19177c;">:oauth-creds</span> <span style="color: #19177c;">my-creds</span>
<span style="color: #19177c;">:params</span> {<span style="color: #19177c;">:id</span> <span style="color: #19177c;">id</span>
<span style="color: #19177c;">:count</span> <span style="color: #666666;">200</span>
<span style="color: #19177c;">:skip_status</span> <span style="color: #19177c;">true</span>
<span style="color: #19177c;">:include_user_entities</span> <span style="color: #19177c;">false</span>})))
(<span style="color: blue;">->></span> (<span style="color: blue;">spit</span> (<span style="color: green;">str </span><span style="color: #19177c;">id</span> <span style="color: #ba2121;">".txt"</span>)))))
(<span style="color: green; font-weight: bold;">defn </span><span style="color: #19177c;">fetch-user</span> [<span style="color: #19177c;">screen-name</span>]
(<span style="color: green;">println </span><span style="color: #ba2121;">"Fetching user"</span> <span style="color: #19177c;">screen-name</span>)
(<span style="color: #19177c;">:body</span> (<span style="color: blue;">rest/users-show</span> <span style="color: #19177c;">:oauth-creds</span> <span style="color: #19177c;">my-creds</span>
<span style="color: #19177c;">:params</span> {<span style="color: #19177c;">:screen_name</span> <span style="color: #19177c;">screen-name</span>})))
(<span style="color: green; font-weight: bold;">defn </span><span style="color: #19177c;">get-network</span> []
(<span style="color: green; font-weight: bold;">let </span>[<span style="color: #19177c;">my-id</span> (<span style="color: #19177c;">:id</span> (<span style="color: blue;">fetch-user</span> <span style="color: #ba2121;">"timothypratley"</span>))
<span style="color: #19177c;">my-friends</span> (<span style="color: blue;">fetch-friends</span> <span style="color: #19177c;">my-id</span>)]
(<span style="color: green;">into </span>{<span style="color: #19177c;">my-id</span> <span style="color: #19177c;">my-friends</span>}
(<span style="color: green;">for </span>[<span style="color: #19177c;">friend</span> <span style="color: #19177c;">my-friends</span>]
[<span style="color: #19177c;">friend</span> (<span style="color: blue;">fetch-friends</span> <span style="color: #19177c;">friend</span>)]))))
(<span style="color: blue;">spit</span> <span style="color: #ba2121;">"network.txt"</span> (<span style="color: green;">pr-str </span>(<span style="color: blue;">get-network</span>)))
</pre>
</div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span><br />
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Ok let’s let this baby roll! </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">I follow over 100 people, so this is going to take nearly 2 hours… </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">All done! </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We have a file representing my network.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">My immediate network consists of people I follow, </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">those people follow people outside my immediate network, </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">but also follow some people that I follow. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">This person has 3 people following them. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">This person has 2 people following them. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">But only I am following this person.</span></div>
<b style="font-weight: normal;"><br /></b>
<b style="font-weight: normal;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkmEwE9ojdvg7ADpxDwGCCj47OjngX9K6Jaqamvi5MgAIEhH5R1_Zbc2LC8VzDJgreYAaMRd5rebOHEvTufX2fqc334Kd20D9xlmQ2Ds4RJgSYlSg_epELE00EUSN_YZplQRM0sg/s1600/Screen+Shot+2016-03-12+at+11.06.55+AM.png" imageanchor="1"><img border="0" height="268" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkmEwE9ojdvg7ADpxDwGCCj47OjngX9K6Jaqamvi5MgAIEhH5R1_Zbc2LC8VzDJgreYAaMRd5rebOHEvTufX2fqc334Kd20D9xlmQ2Ds4RJgSYlSg_epELE00EUSN_YZplQRM0sg/s320/Screen+Shot+2016-03-12+at+11.06.55+AM.png" width="320" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihvCp_5pzzQndO8Kl4DV0iveqB3CTBUJdEpyXSm6VISQEUph0IUla8TrW6thidwb3j8KHpF5J7GNUWHr7krP_H9QOjkuszqctcvvUJVTna4khN2A5eBajnxl09BP_w7gnfVelI0w/s1600/Screen+Shot+2016-03-12+at+11.07.48+AM.png" imageanchor="1"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihvCp_5pzzQndO8Kl4DV0iveqB3CTBUJdEpyXSm6VISQEUph0IUla8TrW6thidwb3j8KHpF5J7GNUWHr7krP_H9QOjkuszqctcvvUJVTna4khN2A5eBajnxl09BP_w7gnfVelI0w/s200/Screen+Shot+2016-03-12+at+11.07.48+AM.png" width="196" /></a></b><br />
<b style="font-weight: normal;"><br /></b>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We can rank people based on the number of links. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><a href="https://en.wikipedia.org/wiki/PageRank">Pagerank</a> uses current ranks to generate new ranks iteratively. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Each link is given a weight based upon the current score of the source node, </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">with which to calculate a new score for the target node. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">This is repeated until the scores settle. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">The interesting thing about Pagerank is that the quality of inbound links matters. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Here yellow is recognized as important because blue links to it.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgb0lEGtn4wJntcuyvYko-9SzMh7w1CjlBrrqdaXvRoRzgdz-BhEaMOHOSHjdW8gbXBQahlPh2XewaH_Is5n2sa72ZLo1hZ3et5evKc5kUMrszEdtq5NMuAOdSSL3eqrm7qOzRRMw/s1600/pagerank-visualization.jpg" imageanchor="1"><img border="0" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgb0lEGtn4wJntcuyvYko-9SzMh7w1CjlBrrqdaXvRoRzgdz-BhEaMOHOSHjdW8gbXBQahlPh2XewaH_Is5n2sa72ZLo1hZ3et5evKc5kUMrszEdtq5NMuAOdSSL3eqrm7qOzRRMw/s320/pagerank-visualization.jpg" width="320" /></a></span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We will use <a href="http://leaderboardx.herokuapp.com/">LeaderboardX</a> to do a Pagerank on our network. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">First we need to reshape the data into the expected input format. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">We want to generate lines of lists of people and who they follow. </span><span style="font-family: "arial"; font-size: 14.6667px; vertical-align: baseline; white-space: pre-wrap;">Great, now we can load our file. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Oh no, our graph is way too large. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">There are 17 thousand nodes, so rendering them all is not possible. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">If we filter out nodes not in my immediate network there are only 15 hundred.</span><br />
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span></div>
<div>
<br /></div>
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">(<span style="color: green; font-weight: bold;">ns </span><span style="color: #19177c;">twitternet.munge</span>
(<span style="color: #19177c;">:require</span>
[<span style="color: #19177c;">clojure.edn</span> <span style="color: #19177c;">:as</span> <span style="color: #19177c;">edn</span>]
[<span style="color: #19177c;">clojure.string</span> <span style="color: #19177c;">:as</span> <span style="color: #19177c;">string</span>]))
(<span style="color: green; font-weight: bold;">defn </span><span style="color: #19177c;">transform</span> [<span style="color: #19177c;">network</span>]
(<span style="color: green;">for </span>[[<span style="color: #19177c;">person</span> <span style="color: #19177c;">outs</span>] <span style="color: #19177c;">network</span>]
(<span style="color: green;">cons </span><span style="color: #19177c;">person</span> (<span style="color: green;">filter </span><span style="color: #19177c;">network</span> <span style="color: #19177c;">outs</span>))))
(<span style="color: green; font-weight: bold;">defn </span><span style="color: #19177c;">reshape</span> []
(<span style="color: green; font-weight: bold;">let </span>[<span style="color: #19177c;">network</span> (<span style="color: blue;">edn/read-string</span>
(<span style="color: green;">slurp </span><span style="color: #ba2121;">"network.txt"</span>))]
(<span style="color: blue;">spit</span>
<span style="color: #ba2121;">"lxnet.txt"</span>
(<span style="color: blue;">string/join</span>
<span style="color: #ba2121;">\n</span><span style="color: #19177c;">ewline</span>
(<span style="color: blue;">cons</span>
<span style="color: #ba2121;">"Person,Endorses"</span>
(<span style="color: green;">map </span><span style="color: #666666;">#</span>(<span style="color: blue;">string/join</span> <span style="color: #ba2121;">","</span> <span style="color: #19177c;">%</span>)
(<span style="color: blue;">transform</span> <span style="color: #19177c;">network</span>)))))))
</pre>
</div>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Success! </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">My network looks like a hairball. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 22.4889px; white-space: pre-wrap;">There are many connections and nodes.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnCnyavCUt0X_ug_Vaoy0xGsb3zKjsUiw4JQyAUmjGqhUApIUJf9rqju9ePv1yUSxsuDdNVerTWpupGxwkYvX_IDbQGYztoJBo_Mc3HnZf-gkmdHookBqTnR8tHkIMbWqXmHtMGA/s1600/Screen+Shot+2016-03-12+at+11.12.39+AM.png" imageanchor="1"><img border="0" height="261" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnCnyavCUt0X_ug_Vaoy0xGsb3zKjsUiw4JQyAUmjGqhUApIUJf9rqju9ePv1yUSxsuDdNVerTWpupGxwkYvX_IDbQGYztoJBo_Mc3HnZf-gkmdHookBqTnR8tHkIMbWqXmHtMGA/s320/Screen+Shot+2016-03-12+at+11.12.39+AM.png" width="320" /></a></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Notice that the top ranked member has fewer links than second place.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm_ML_354H_2mtoh_F3fx6EUD9QLA7lHH34NAzK9YTLjz83w7BKNNlpoQoZMaaPWJx7dRTwvoks-N7PO3FNE7p8qoiurKRNvfvtWan1NdWNz8g2Kri8alfj235uOlzXI_h-xWX4A/s1600/Screen+Shot+2016-03-12+at+11.14.06+AM.png" imageanchor="1"><img border="0" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm_ML_354H_2mtoh_F3fx6EUD9QLA7lHH34NAzK9YTLjz83w7BKNNlpoQoZMaaPWJx7dRTwvoks-N7PO3FNE7p8qoiurKRNvfvtWan1NdWNz8g2Kri8alfj235uOlzXI_h-xWX4A/s320/Screen+Shot+2016-03-12+at+11.14.06+AM.png" width="320" /></a></span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The runner up for highest Pagerank in my immediate network is</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><a href="https://twitter.com/bhauman">Bruce Hauman</a>, the author of <a href="https://github.com/bhauman/lein-figwheel">Figwheel</a> and <a href="https://github.com/bhauman/devcards">Devcards</a>.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">And the winner is, <a href="https://twitter.com/shaunlebron">Shaun LeBron</a>, the author of <a href="https://shaunlebron.github.io/parinfer/">Parinfer</a>.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s reflect for a moment and compare test-refresh with a REPL.</span></div>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">There is only the code file to edit</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">There are no special keystrokes to evaluate forms or send them to the REPL</span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: Arial; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">We get instant notification when code fails to compile or execute</span></div>
</li>
</ul>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I love tmux, emacs, and vim... </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">but when it comes to REPL integration, things get pretty complicated.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">It feels productive to have key combos to execute code, run tests, switch buffers, splice in results and all sorts of great stuff.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgejtlrGSjvFw8Ymmb090yWq9F2lk_Mx7CA4jNVKbIKyRXkhZ9lGNKwu5G5tVfO7Xa3RY3bVBDhN-daXEpl3HKLbo30U43B4CCI_diH24G8CRD-uI4nIHIC6Y2wG6cJe48vGl_4OQ/s1600/sadcorgi.jpeg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgejtlrGSjvFw8Ymmb090yWq9F2lk_Mx7CA4jNVKbIKyRXkhZ9lGNKwu5G5tVfO7Xa3RY3bVBDhN-daXEpl3HKLbo30U43B4CCI_diH24G8CRD-uI4nIHIC6Y2wG6cJe48vGl_4OQ/s400/sadcorgi.jpeg" /></a><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">However I end up spending a lot of time switching window focus, sending code to the REPL, finding tests to run, forgetting to eval my function or file, and generally being busy interacting with the REPL. </span><span style="font-family: arial; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">I make many mistakes, and blame myself for not being able to keep it all straight in my head.</span></div>
<b style="font-weight: normal;"><br /></b>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizl2-8jEZTg_gIo0rvHnfFcoUYmwr2kRMUYYnWOhdFVp4wVShM8BW8JOexjbI-jQB1nw6eRTTsKnzghJCo-TAGjBYwjEgUU-Gz15wrsnR1GdKOTlddC4SxSKdDnecpddbeMU9BRg/s1600/happy_corgi.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="153" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizl2-8jEZTg_gIo0rvHnfFcoUYmwr2kRMUYYnWOhdFVp4wVShM8BW8JOexjbI-jQB1nw6eRTTsKnzghJCo-TAGjBYwjEgUU-Gz15wrsnR1GdKOTlddC4SxSKdDnecpddbeMU9BRg/s200/happy_corgi.jpg" width="200" /></a><span style="font-family: "arial"; font-size: 14.6667px; vertical-align: baseline; white-space: pre-wrap;">In contrast, when I use test-refresh with any editor, my workflow is very simple. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">There is my code and there is the log of what happens when it reloads. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">From this simplicity flows productivity because I can focus on my code. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">My primary brain function is thinking about the program, not managing my REPL.</span><br />
<b style="font-weight: normal;"><br /></b>
<b style="font-weight: normal;"><br /></b>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><a href="https://cursive-ide.com/">Cursive</a>, by Colin Fleming is well suited to this workflow because Cursive’s error detection, documentation and navigation features do not require a REPL.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMmRLEdAyyZuwFGO0uek-8qaloew9tvu0c7hAjACKAgMl9yPZ8fusXGYKsibAebnOWBc0l8kYHCyQClBqBnWiGc_kmOCawlpwdy43iPMy1zYk0s3-9NNxtZocLYin50bzZkR88ig/s1600/colin-fleming.jpg" imageanchor="1"><img border="0" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMmRLEdAyyZuwFGO0uek-8qaloew9tvu0c7hAjACKAgMl9yPZ8fusXGYKsibAebnOWBc0l8kYHCyQClBqBnWiGc_kmOCawlpwdy43iPMy1zYk0s3-9NNxtZocLYin50bzZkR88ig/s320/colin-fleming.jpg" width="320" /></a></span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">If you are new to Clojure, I strongly encourage you to stick with your most comfortable editor for as long as possible. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Learning a language is hard enough without learning a new editor at the same time. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Test-refresh provides fast feedback without the need for any integration.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To set test-refresh up, add it to your lein profile.clj</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">I highly recommend configuring the “changes only” and “quiet” options.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">These options greatly reduce the amount of time and noise per refresh.</span></div>
<b style="font-weight: normal;"><br /></b>
<b style="font-weight: normal;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1xT-kZ4lA6v-u5vcc2GMw8u1JmOAw7z_XR3wY6zAcHvPVYJ7RfqDxvbmLRAr7alvAUyavj_u1unDcTV2wAGcK-q5dsHNNFJZJD5ODdOlxHC5yrQlgSNrFNuegBGBT5Wk1iYYBfw/s1600/Screen+Shot+2016-03-12+at+11.45.07+AM.png" imageanchor="1"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1xT-kZ4lA6v-u5vcc2GMw8u1JmOAw7z_XR3wY6zAcHvPVYJ7RfqDxvbmLRAr7alvAUyavj_u1unDcTV2wAGcK-q5dsHNNFJZJD5ODdOlxHC5yrQlgSNrFNuegBGBT5Wk1iYYBfw/s400/Screen+Shot+2016-03-12+at+11.45.07+AM.png" width="400" /></a></b><br />
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><a href="https://github.com/venantius/ultra">Ultra</a> provides nicely formatted diffs when tests fail. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">To see it in action let’s write a basic test.</span></div>
<b style="font-weight: normal;"><br /></b>
<b style="font-weight: normal;"><br /></b>
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">(<span style="color: green; font-weight: bold;">ns </span><span style="color: #19177c;">twitternet.munge-test</span>
(<span style="color: #19177c;">:require</span>
[<span style="color: #19177c;">clojure.test</span> <span style="color: #19177c;">:refer</span> <span style="color: #19177c;">:all</span>]
[<span style="color: #19177c;">twitternet.munge</span> <span style="color: #19177c;">:as</span> <span style="color: #19177c;">munge</span>]))
(<span style="color: blue;">deftest</span> <span style="color: #19177c;">transform-test</span>
(<span style="color: blue;">is</span> (<span style="color: green;">= </span>[[<span style="color: #666666;">1</span> <span style="color: #666666;">2</span> <span style="color: #666666;">3</span>]
[<span style="color: #666666;">2</span>]
[<span style="color: #666666;">3</span> <span style="color: #666666;">1</span> <span style="color: #666666;">2</span>]]
(<span style="color: blue;">munge/transform</span> {<span style="color: #666666;">1</span> [<span style="color: #666666;">2</span> <span style="color: #666666;">3</span> <span style="color: #666666;">4</span>]
<span style="color: #666666;">2</span> [<span style="color: #666666;">5</span> <span style="color: #666666;">6</span> <span style="color: #666666;">7</span>]
<span style="color: #666666;">3</span> [<span style="color: #666666;">1</span> <span style="color: #666666;">2</span> <span style="color: #666666;">8</span>]}))))
</pre>
</div>
<br />
<br />
<b style="font-weight: normal;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQAbFXx6rIaijzp5l0H-cvscv8PNSnGo-7P6Gjjp46T0-6IRv-94gsjfTBLoTOayXTV8rdzezWkR-pUWwy3ajnXGhbF7PXhzLAo01XbXadifoiZZmRccQ7s5a3Z1K70rUPZPvMVQ/s1600/Screen+Shot+2016-03-12+at+11.50.10+AM.png" imageanchor="1"><img border="0" height="85" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQAbFXx6rIaijzp5l0H-cvscv8PNSnGo-7P6Gjjp46T0-6IRv-94gsjfTBLoTOayXTV8rdzezWkR-pUWwy3ajnXGhbF7PXhzLAo01XbXadifoiZZmRccQ7s5a3Z1K70rUPZPvMVQ/s200/Screen+Shot+2016-03-12+at+11.50.10+AM.png" width="200" /></a></b><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><a href="https://github.com/xsc/lein-ancient">Ancient</a> will upgrade project dependencies, if all the tests pass. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><a href="https://github.com/jonase/kibit">Kibit</a> detects non-idiomatic code. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><a href="https://github.com/jonase/eastwood">Eastwood</a> detects bad code. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">And <a href="https://github.com/dakrone/lein-bikeshed">bikeshed</a> detects bad formatting.</span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sometimes I don’t want to create a project to experiment with Clojure. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Try CLJ is pretty handy for this because there is no startup time. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">LightTable has an instarepl which shows results inside the file you are currently editing. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">To see it in action, let’s try answering a <a href="http://stackoverflow.com/questions/35822942/how-to-generate-a-long-number-randomly-in-clojure/35823295#35823295">StackOverflow question</a>.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Let’s try this code out and see what we get. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Hmmm the problem seems to be with the type conversions here. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Yeah, they either need to make a true random bigint, </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">or if a restricted domain of random numbers is acceptable do some extra casting.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhFJgSWH4_96F1iyTiswcGCnalr0nQHHZqPmsdNeFU7LppdQixnHeZB6Tby0CpHTBzsad7BNQfUJxLNgq-QVc3tl-CJe1d6E5-CLmcwmh5EKfAFhctZ5uMF433wqi-VukeEaZkTQ/s1600/Screen+Shot+2016-03-12+at+11.54.39+AM.png" imageanchor="1"><img border="0" height="177" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhFJgSWH4_96F1iyTiswcGCnalr0nQHHZqPmsdNeFU7LppdQixnHeZB6Tby0CpHTBzsad7BNQfUJxLNgq-QVc3tl-CJe1d6E5-CLmcwmh5EKfAFhctZ5uMF433wqi-VukeEaZkTQ/s320/Screen+Shot+2016-03-12+at+11.54.39+AM.png" width="320" /></a></span></div>
<b style="font-weight: normal;"><br /></b><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">I occasionally use LightTable like this for throw away code... </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">but for most of my work, I want to keep the code around.</span><br />
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Using test-refresh as a REPL replacement has made my coding workflow more effective. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">And it has encouraged me to add tests at times I would otherwise felt that was a chore. </span><span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;">Next time you are about to lein repl, lein test-refresh instead.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; line-height: 1.38; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.6667px; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><b>Do not settle for a REPL.</b></span></div>
<b style="font-weight: normal;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Until next time, keep coding.</span></div>
<br />Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.comtag:blogger.com,1999:blog-32811356.post-29564615559499258392015-11-23T17:41:00.000-08:002016-09-11T17:16:41.077-07:00Code-forward OthelloMark Bastian gave a <a href="https://youtu.be/Tb823aqgX_0">thought provoking talk</a> at the Conj about bottom up design. It triggered interesting conversations with my friends. I venture to say Mark exaggerate the top down object oriented approach, and I liked that he did, because it allowed me to think about the contrasts more concretely. One of my friends commented that it would be interesting to see how he would design his game if it had to support multiple rules. I thought it would be an interesting screencast... and have been wanting to show some devcards dual build usage for a while. So here it is, Tic Tac Toe to Othello: https://youtu.be/7fYmxt29Id4<br />
<br />
<h3>
Watch: <a href="https://youtu.be/7fYmxt29Id4">Screencast</a> (24 minutes)</h3>
Play: <a href="http://timothypratley.github.io/othello/">Game</a><br />
Read: <a href="https://github.com/timothypratley/othello">Code</a><br />
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
</div>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<div style="margin: 0px;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 14.666666666666666px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Write: Another board game? Go? Checkers?</span></div>
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3ehg0gzxvcwVuIxP_Cvbl6uDFCzBZzpxQqzkCxKoAfe9O0Kss3zfOuR7b4SbnFEtZIt3Z7k7X19WDIBjMxiQzzaEWMeX8aCOKpPEfhJCr8dBTDczjFH7LPGTlpFm7pAtLGdSGNA/s1600/Screen+Shot+2016-09-11+at+5.15.45+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3ehg0gzxvcwVuIxP_Cvbl6uDFCzBZzpxQqzkCxKoAfe9O0Kss3zfOuR7b4SbnFEtZIt3Z7k7X19WDIBjMxiQzzaEWMeX8aCOKpPEfhJCr8dBTDczjFH7LPGTlpFm7pAtLGdSGNA/s320/Screen+Shot+2016-09-11+at+5.15.45+PM.png" width="319" /></a></div>
<br />
<h3>
Transcript of screencast:</h3>
<br />
CoderX here.<br />
<br />
In the last screencast we setup a ClojureScript project,<br />
implemented a tic tac toe game, and published it online.<br />
Today I want to show you how to work with a larger codebase.<br />
We are going to convert our tic tac toe game into Othello,<br />
focusing on our workflow along the way.<br />
<br />
Othello is played on an 8 by 8 board with black and white discs.<br />
Each player places their discs in such a way as to capture the opponent's discs.<br />
When placing a disc, opponent discs that become surrounded are captured.<br />
Captured discs are converted to the capturing player's color by flipping them.<br />
<br />
Before we get coding let's add testing feedback to our workflow.<br />
Don't worry, this won't be the boring style of testing you may be expecting!<br />
But it will take some setup to get going.<br />
<br />
First add a dependency to our project.clj<br />
Devcards is a library that showcases our components in various states.<br />
Copy the dev build section to create a devcards build.<br />
Give it a different output directory,<br />
so that the compiled files do not collide with other builds.<br />
Create a devcards.html file,<br />
Copy and paste the contents from the devcards README on github.<br />
Start the new build we configured by typing:<br />
`lein figwheel devcards`<br />
Open localhost:3449/devcards.html<br />
Seeing this page indicates that everything is working so far.<br />
In the core.cljs source file, add devcards to the namespace require.<br />
Refer the `defcard` macro.<br />
Now define a card with some text in it.<br />
The text can contain markdown; you can create titles and even format code.<br />
Refer deftest from devcards instead of test.<br />
See how our existing tests are now rendered into the page.<br />
<br />
Failing tests are colored red.<br />
<br />
Instead of `defcard`, we will be using a more specific version `defcard-rg`<br />
which renders reagent components.<br />
<br />
We can define a card that shows a single piece of the board in a given state.<br />
<br />
Now we can isolate a single component to work on,<br />
instead of having to observe the entire application.<br />
<br />
We need a little more space here...<br />
Let's show our existing pieces,<br />
and adjust the sizing.<br />
<br />
O.K. let's make some discs.<br />
<br />
We'll use blue instead of white for now so that we can see the disc on a white background.<br />
We need to pass the coordinates to place them in the right location.<br />
<br />
Now we need a green background.<br />
Let's copy the existing blank piece but make it green.<br />
And the put blank pieces behind our discs.<br />
That looks about right.<br />
Let's parameterize the blank component so that it takes the background color.<br />
We'll add an argument, but keep a default of grey if only 2 parameters are passed.<br />
Does it work if we pass red?<br />
Yes, great!<br />
<br />
Let's make a card with a complete board.<br />
We don't need the title and button, so let's extract just the board.<br />
<br />
We'll change our convention slightly so that blank cells on the board are represented with a space,<br />
then we can use a B for black disc and W for white disc.<br />
Update the render cases to default to a blank.<br />
The top left nine positions are currently being initialized to B,<br />
because that was our tic tac toe starting condition.<br />
<br />
Let's make a function to create a `new-othello-board`.<br />
An Othello game starts with four pieces in the center in a diagonal configuration.<br />
<br />
Now we will add another card to show the tic tac toe game.<br />
We need to update `new-tictactoe-game` to use space instead of B.<br />
<br />
Notice that we now have two cards that show the same component in two different states.<br />
Adding cards and tests reduces our time spent clicking through the application while developing.<br />
<br />
Our blank cells don't do anything when clicked,<br />
because they are modifying the global app-state.<br />
We'll make them update the state passed instead.<br />
<br />
There is quite a bit of logic in this click handler, let's extract that into a function.<br />
We'll need to pass a game atom through, so that it can be modified.<br />
Checking the console for errors reveals that we forgot to pass an atom to our original card.<br />
We can use an empty map atom for those, as they need not respond to clicks.<br />
<br />
Our game atom needs a status, because we prevented further moves after a win or draw.<br />
Great, we can now click on the tic tac toe board and see the game progress.<br />
When we click on the othello board, we are still using tic tac toe rules.<br />
<br />
It is time to implement the othello rules.<br />
The objective in othello is to capture the opponent's discs by surrounding them.<br />
Here are the valid moves white can make in response to my move.<br />
<br />
Let's start with detecting which cells positions are valid moves.<br />
And placing a disc instead of a naught.<br />
To preserve the tic tac toe rules we need two separate logic paths that dispatch on the game type.<br />
Multimethods are a good way to express this.<br />
You can think of multimethods as case statements as functions.<br />
<br />
`defmulti` declares a name and the dispatch function.<br />
`defmethod` declares a function to call for a dispatch value.<br />
`defmulti` needs to have the same signature as the methods.<br />
<br />
To start with, let's constrain the othello `can-move?` function to cells that are adjacent to a white disc.<br />
To dispatch by type, we need to give our games a type.<br />
Let's convert our `new-board` function into a `new-game` function,<br />
returning a map containing the game type, status and board.<br />
<br />
Oh no, something went wrong:<br />
`Uncaught Error: 4 is not ISeqable.`<br />
Expand the stacktrace by clicking the triangle at the start of the error message.<br />
Scan down the stacktrace to the 4th row:<br />
`tictactoe$core$available_QMARK_` Means in the namespace `tictactoe.core`, in the function `available?`<br />
`@ core.cljs?rel:96` is the filename and line number.<br />
:96 tells us the problem is on line 96 of our core.cljs source file.<br />
The line above indicates that the problem occurred when calling `get-in` from the ClojureScript core namespace.<br />
<br />
Ah yes, get-in is expecting a vector containing i and j. Fixed.<br />
<br />
Player moves are limited now, but we still need to apply the same restrictions to the computer's moves.<br />
Let's rearrange a little so that we can call the same function from `computer-move`.<br />
Put the `board-size` inside the game.<br />
And the `background-color`.<br />
After these changes, the board-size is not coming through to the `game-board`.<br />
Aha... `game-board` takes an atom, so we need to dereference the atom to pull out the values we are interested in.<br />
But the computer is still not making moves.<br />
Let's print out what `can-move?` is doing...<br />
That all looks fine.<br />
Aha... we should be updating the game board here.<br />
<br />
We can make `computer-move` more generic.<br />
Convert `can-move?` to return the game that would result from the move.<br />
Now `computer-move` is just the first successful result of `can-move?`,<br />
when examining cells in a shuffled order.<br />
The computer is now placing white discs when we place a black disc.<br />
`can-move?` needs to be parameterized to behave differently depending upon who is making the move.<br />
<br />
The movement rule for othello is more than adjacency.<br />
An othello move is one that captures opponent discs between your move and one of your existing discs.<br />
Captured discs are flipped over and converted to your color.<br />
To check for a valid move we check for capture lines in every direction.<br />
A capture line is a sequence of the opponents disc that is terminated with one of your discs.<br />
<br />
Let's write a test to see if this definition is correct.<br />
Given a board "space W W W B", placing a B at 0 0 should capture the 3 W discs,<br />
capture should return the coordinates 1 0, 2 0, 3 0.<br />
Given a board "B W space W B", placing a B at 2 0 should capture the 2 W discs on either side,<br />
capture should return the coordinates 1 0, 3, 0.<br />
<br />
We need to pass a game to our `computer-move` test.<br />
<br />
Now we need the next game state to contained the result of flipping all the captured discs.<br />
Hmmm looks like I missed the terminating disc requirement.<br />
Let's add a failing test to capture that.<br />
Given a board "space W W space", placing a B at 0 0 should not return any capture coordinates.<br />
`capture-line` needs to collect a sequence of potential flips,<br />
but only return them when we encounter a terminating disc.<br />
<br />
Great, the move rules are in place now.<br />
If we ignore the `win?` condition, it looks very much like othello.<br />
<br />
Let's rearrange `win?` to be game specific.<br />
Let's convert it into a multimethod on game type.<br />
In othello, the player with the most pieces at the end of the game wins.<br />
<br />
Players must make a capturing move if possible, or pass.<br />
We can use the `frequencies` function to discover the count of space, W and B in the board.<br />
`apply concat` flattens the board from a 2 by 2 matrix into a one dimensional sequence.<br />
If there are no remaining spaces, the player is a winner if they have more discs than the opponent.<br />
In a 2 by 2 game board, B B B W is a win condition for black.<br />
<br />
Now that we have several tests, it is inconvenient to scroll through the page to view them.<br />
Let's get some output in the console to alert us on failures.<br />
In our `project.clj` in the devcards build under figwheel, add an on-jsload hook.<br />
Add the test directory to source-paths, and create a source file run.cljs under test/tictactoe<br />
`run` will call ClojureScript's `run-all-tests` function.<br />
Well also define a method to print out success or fail when the tests complete.<br />
Now we can see a summary of the tests in the browser console whenever we change code.<br />
<br />
In othello it is possible to reach a configuration where neither player can move,<br />
but not all cells are taken yet.<br />
Let's extract an `available-moves` function so we can use it in both the `computer-move` and `win?` condition.<br />
Hmmm even though we extracted it, our current `win?` code is much higher up in the file,<br />
so we will need to rearrange our functions to get the order of declaration correct.<br />
<br />
This is a symptom of a larger issue:<br />
There is a lot of code in core.cljs now!<br />
Let's think about how to split the code up into more coherent namespaces.<br />
<br />
Clearly we have Tic Tac Toe specific code and Othello specific code.<br />
`can-move?` and `win?` will go in there.<br />
And we might create in the future some other game implementations.<br />
Our core namespace should be kept pretty small.<br />
Core will be focused on connecting a game state with the desired logic and a view.<br />
We'll need a central definition of the logic of a game,<br />
in which we can declare the multimethods which will be implemented by the more specific namespaces.<br />
It will contain the code that is common across games.<br />
We have a bunch of component drawing code which we can put in a view namespace.<br />
<br />
Tic Tac Toe, Othello, and other implementations will depend on Game,<br />
as they need the defmulti to connect methods to.<br />
Core will depend on Game and View, and the implementations of `new-game`.<br />
View won't need to depend on anything because it will just be rendering data.<br />
<br />
O.K. let's move these functions to the appropriate namespaces!<br />
<br />
We need a draw? condition.<br />
<br />
By printing out the game in the console, we can see victory is reached.<br />
Let's check tic tac toe is still working.<br />
<br />
I think we are all set, let's play our game!<br />
So far we have been running the devcards build,<br />
We want to switch over to viewing the dev build.<br />
In your terminal, interrupt the current build by pressing "control c".<br />
Now run both builds simultaneously, by typing`lein figwheel dev devcards`.<br />
Navigate to localhost:3449 to see the dev build.<br />
<br />
We only want to mount the main game when viewing the dev build,<br />
so let's put that inside a conditional.<br />
<br />
Let's play our game!<br />
<br />
Hmmm this is interesting, I cannot move, but the computer has already moved.<br />
In this situation the computer should move again in the last remaining cell.<br />
We can achieve this by making check-game-status recurse while there is no player move available.<br />
Let's add a test to make sure our logic works.<br />
Here is a small 3 by 3 board where there are 2 computer moves and no player moves.<br />
<br />
Let's play some more games.<br />
I think the victory message should be on a different line from the buttons.<br />
<br />
A strong strategy when playing othello is to focus on positions instead of how many discs you can flip.<br />
The most advantageous positions in othello are the corners,<br />
which can never be lost and allow you to recapture long lines in many directions.<br />
Next best are the sides, for the same reason.<br />
When starting a game it is better to capture fewer discs,<br />
This limits your opponents options, and allows you to force your way into a side or corner.<br />
Once you can establish the safety of a side and corner, you will have many opportunities to reclaim the central discs.<br />
<br />
Time to publish our boardgames.<br />
Our project source files are now core, game, othello, tictactoe and view.<br />
And we added a devcards.html file to the resources directory.<br />
<br />
I created a new repository for the combined games,<br />
so that there is a separate repository for each video.<br />
But you don't need to.<br />
I'll rename my output file to othello.js to differentiate it from my previous project.<br />
<br />
Let's check that the minified build still works.<br />
Interrupt the current build with "control c"<br />
and type `lein cljsbuild once min`<br />
open index.html if you want to check it locally.<br />
Commit and push to the gh-pages branch.<br />
Navigate to username.github.io/project to see your page.<br />
<br />
Let's review:<br />
We converted our original game into two games.<br />
<br />
The othello namespace contains our game specific rules for othello:<br />
An othello game has a green background, the player is B, and computer is W.<br />
The board has four discs in a diagonal configuration at the center.<br />
Valid moves capture one or more lines of discs of the other player.<br />
A draw occurs if there are no available moves to the player or computer.<br />
We can shorten this test by using a new-board instead of repeating the values.<br />
A win occurs when neither player can move, and one has more discs.<br />
<br />
The core namespace has the game selection view and attaches the rendering.<br />
The view namespace has the blank, circle, cross, discs, and game-board component.<br />
The game namespace declares the multimethods that will dispatch by game type,<br />
and some logic functions which are common between games.<br />
The tictactoe namespace contains implementations specific to Tic Tac Toe.<br />
<br />
We have a minified build which is published on github pages,<br />
a dev build, and a devcards build.<br />
Devcards presents cards and tests that occur in our namespaces,<br />
so that we can see what our components will look like in predefined states.<br />
We added an on-jsload hook to report a test summary to the console while developing.<br />
<br />
If you have any questions, please drop a comment.<br />
<br />
Until next time, keep coding!<br />
<div>
<br /></div>
Timothy Pratleyhttp://www.blogger.com/profile/11081038249454453409noreply@blogger.com