<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Joel Grus</title><link href="https://joelgrus.com/" rel="alternate"></link><link href="https://joelgrus.com/feeds/atom.xml" rel="self"></link><id>https://joelgrus.com/</id><updated>2025-12-22T12:00:00+01:00</updated><subtitle>is sort of a famous author</subtitle><entry><title>2025 Year in Review</title><link href="https://joelgrus.com/2025/12/22/2025-year-in-review/" rel="alternate"></link><published>2025-12-22T12:00:00+01:00</published><updated>2025-12-22T12:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-12-22:/2025/12/22/2025-year-in-review/</id><summary type="html">&lt;style&gt;
.year-review-gallery {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2.5rem;
    padding: 1rem 0;
}
.year-review-gallery img {
    max-width: 100%;
    height: auto;
    border-radius: 0.75rem;
    box-shadow: 0 4px 12px -2px rgba(15, 46, 77, 0.12), 0 2px 4px -1px rgba(15, 46, 77, 0.06);
    transition: transform 0.3s ease, box-shadow 0 …&lt;/style&gt;</summary><content type="html">&lt;style&gt;
.year-review-gallery {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2.5rem;
    padding: 1rem 0;
}
.year-review-gallery img {
    max-width: 100%;
    height: auto;
    border-radius: 0.75rem;
    box-shadow: 0 4px 12px -2px rgba(15, 46, 77, 0.12), 0 2px 4px -1px rgba(15, 46, 77, 0.06);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.year-review-gallery img:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 25px -5px rgba(15, 46, 77, 0.15), 0 4px 6px -2px rgba(15, 46, 77, 0.08);
}
&lt;/style&gt;

&lt;h1&gt;Year In Review 2025&lt;/h1&gt;
&lt;div class="year-review-gallery"&gt;
&lt;p&gt;&lt;img alt="1" src="https://joelgrus.com/images/2025-year-in-review/GwgE8HgWYAAk1Br.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="2" src="https://joelgrus.com/images/2025-year-in-review/GwgFOO3WgAAIfOA.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="3" src="https://joelgrus.com/images/2025-year-in-review/GwjnZbeXYAA43gB.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="4" src="https://joelgrus.com/images/2025-year-in-review/Gw58daJXgAAX-od.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="5" src="https://joelgrus.com/images/2025-year-in-review/GxDVg-GXkAAGUGb.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="6" src="https://joelgrus.com/images/2025-year-in-review/GxTtOG-WYAAHlzj.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="7" src="https://joelgrus.com/images/2025-year-in-review/GxoGIuhXsAAaKoD.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="8" src="https://joelgrus.com/images/2025-year-in-review/GxoIlPFXMAAlDBj.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="9" src="https://joelgrus.com/images/2025-year-in-review/GxoV8BqXYAAFU5R.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="10" src="https://joelgrus.com/images/2025-year-in-review/Gx7WLUvXoAAyUGb.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="11" src="https://joelgrus.com/images/2025-year-in-review/Gx70Fm0WIAARM73.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="12" src="https://joelgrus.com/images/2025-year-in-review/GyMgYnTX0AA6cXX.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="13" src="https://joelgrus.com/images/2025-year-in-review/GyWLtQaW0AIbUma.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="14" src="https://joelgrus.com/images/2025-year-in-review/Gy1trxSXQAAvQmN.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="15" src="https://joelgrus.com/images/2025-year-in-review/Gy9dkoqXcAAlCXY.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="16" src="https://joelgrus.com/images/2025-year-in-review/Gy9_zZVXwAAOWlZ.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="17" src="https://joelgrus.com/images/2025-year-in-review/Gy-lTAnW0AApUBl.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="18" src="https://joelgrus.com/images/2025-year-in-review/Gy_KQsDXkAAkW4h.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="19" src="https://joelgrus.com/images/2025-year-in-review/GzmUbd1WAAAakoV.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="20" src="https://joelgrus.com/images/2025-year-in-review/Gzm_o57XYAANilH.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="21" src="https://joelgrus.com/images/2025-year-in-review/GznAR1CX0AALx61.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="22" src="https://joelgrus.com/images/2025-year-in-review/GztRrTmXIAAuExv.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="23" src="https://joelgrus.com/images/2025-year-in-review/GztkHLAWkAELWcv.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="24" src="https://joelgrus.com/images/2025-year-in-review/Gzt_fqhXUAA0WA3.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="25" src="https://joelgrus.com/images/2025-year-in-review/GzzYh_rXQAALtx1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="26" src="https://joelgrus.com/images/2025-year-in-review/G4dog44XoAA-dP6.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="27" src="https://joelgrus.com/images/2025-year-in-review/G47keJXWMAAsr_e.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="28" src="https://joelgrus.com/images/2025-year-in-review/G5Bf0arW0AA-jyT.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="29" src="https://joelgrus.com/images/2025-year-in-review/G6OWgOOXgAAmoyv.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="30" src="https://joelgrus.com/images/2025-year-in-review/G6OvGcoWwAAQK8b.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="31" src="https://joelgrus.com/images/2025-year-in-review/G6jjvkrWoAA7A0D.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="32" src="https://joelgrus.com/images/2025-year-in-review/G7-qr7hWYAEgxnI.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="33" src="https://joelgrus.com/images/2025-year-in-review/G8dyXFxWsAAQNwK.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="34" src="https://joelgrus.com/images/2025-year-in-review/G8d1GWpXoAAWhEL.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="35" src="https://joelgrus.com/images/2025-year-in-review/G8i1eh5XUAA6OR9.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="36" src="https://joelgrus.com/images/2025-year-in-review/G8e1umeXIAAkOqC.png" /&gt;&lt;/p&gt;
&lt;/div&gt;</content><category term="Life"></category></entry><entry><title>Vibe Coding 9 -- AI Agents That Play Taboo</title><link href="https://joelgrus.com/2025/09/08/vibe-coding-9-ai-agents-that-play-taboo/" rel="alternate"></link><published>2025-09-08T12:00:00+02:00</published><updated>2025-09-08T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-09-08:/2025/09/08/vibe-coding-9-ai-agents-that-play-taboo/</id><summary type="html">&lt;p&gt;One thing that I've gotten pretty interested in recently is
"agent engineering" or "agentic architectures" or "agentic system design"
(I'm not sure there's a canonical name for it).
Of course there is a lot of interesting work on 
"how do I use an LLM to solve some contained inference or …&lt;/p&gt;</summary><content type="html">&lt;p&gt;One thing that I've gotten pretty interested in recently is
"agent engineering" or "agentic architectures" or "agentic system design"
(I'm not sure there's a canonical name for it).
Of course there is a lot of interesting work on 
"how do I use an LLM to solve some contained inference or generation problem."&lt;/p&gt;
&lt;p&gt;But there's also some pretty interesting (to me)
problems related to 
"how do I architect a system of autonomous agents toward some end?"
What are the patterns for communication, and for orchestration, and for
self-organization, and so on? &lt;/p&gt;
&lt;p&gt;Every framework takes some point of view on these,
but as always I am partial to &lt;a href="https://dspy.ai"&gt;dspy&lt;/a&gt;, which (as I understand it)
is architecturally pretty unopinionated beyond "use signatures" and "use modules",
and in particular about multi-agent coordination.
Which is fine, I like the flexibility and challenge of figuring out these designs myself.&lt;/p&gt;
&lt;p&gt;Already I've been building a lot of "agentic workflows," but one thing that I hadn't built yet is a real "system of agents" where they interact with each other and do things autonomously (as opposed to moving themselves through pre-determined workflows).&lt;/p&gt;
&lt;p&gt;&lt;img alt="system-of-agents" src="https://joelgrus.com/images/agent_workflow.png" /&gt;&lt;/p&gt;
&lt;p&gt;So I started thinking about what might be a fun, impractical example,
and I came up with the idea of Taboo:&lt;/p&gt;
&lt;h1&gt;Taboo&lt;/h1&gt;
&lt;p&gt;&lt;img alt="taboo" src="https://joelgrus.com/images/taboo.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;If you're not familiar, it's a word-guessing party game, where 
there's a word to guess (e.g. "APPLE") and several taboo words
(e.g. "RED", "FRUIT", "PIE", "COMPUTER", "MACINTOSH")
that you're not allowed to use in your clues. At a party there would
typically be two teams, and they would take turns: one team would 
"clue" and "guess" while the other monitors them and sounds a buzzer 
if they say any of the taboo words.&lt;/p&gt;
&lt;p&gt;If you squint hard enough, this looks like a bunch of "agents":&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cluer (generate clues)&lt;/li&gt;
&lt;li&gt;Buzzer (buzz if any of the clues are taboo)&lt;/li&gt;
&lt;li&gt;Guesser (try to guess the word)&lt;/li&gt;
&lt;li&gt;Judge (say if any of the guessers are correct)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What's more, this is not a turn-based game but more of a free-for-all: 
the cluer keeps shouting out clues while the guessers keep shouting out
guesses while the buzzer and judge monitor for winning or losing.&lt;/p&gt;
&lt;p&gt;This makes it somewhat &lt;em&gt;interesting&lt;/em&gt; as a system of agents;
each operates at its own pace rather than in a predetermined sequence.&lt;/p&gt;
&lt;p&gt;ChatGPT and I game-planned this out and came up with a &lt;a href="https://github.com/joelgrus/taboollm/blob/master/PRD.md"&gt;pretty complicated
design&lt;/a&gt; involving
multiple message queues and so on.&lt;/p&gt;
&lt;p&gt;(Originally, I wanted to make it a collaborative web game where one or more
 humans could play as cluer or guesser along with the AI, but eventually
 I simplified it down to just letting agents play amongst themselves.) (Maybe I'll build the web version in the future.)&lt;/p&gt;
&lt;p&gt;As usually happens on these projects, I started with ChatGPT's PRD,
found the resulting code immensely overengineered, and spent quite a while
stripping it down and simplifying it. It's still complicated because of all
the async pieces, but I think it's a relatively clean design for what it does.&lt;/p&gt;
&lt;h1&gt;Design&lt;/h1&gt;
&lt;p&gt;At its core, the game is a stream of (typed) messages. 
A menagerie of agents listens to the stream, and each of them
can at any time send its own message to the stream.&lt;/p&gt;
&lt;p&gt;So, for example, the guessing agent (or human) sends messages that look like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;GuessEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;guesser&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;player_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;rationale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and similarly for the other players.&lt;/p&gt;
&lt;p&gt;There is a &lt;code&gt;Player&lt;/code&gt; base class with a fair amount of shared plumbing in it,
and then subclasses for each of the roles:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Cluer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ClueEvent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Player that gives clues to help guessers guess the target word.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;next_clue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_over&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;clue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next_clue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;announce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClueEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cluer&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;clue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Most of the magic is in &lt;code&gt;next_clue&lt;/code&gt; which is not implemented in the base class, but you can also see the &lt;code&gt;play&lt;/code&gt; loop, which just continuously 
generates and announces clues until the game is over. 
(&lt;code&gt;self.game&lt;/code&gt; has a history of gameplay so that 
 your implementation of &lt;code&gt;next_clue&lt;/code&gt; can take into account previous clues and guesses).&lt;/p&gt;
&lt;p&gt;There is a &lt;code&gt;Game&lt;/code&gt; class that sets everything in motion (details elided):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_sec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SystemMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;system&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;timeout&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c1"&gt;# Launch players and timeout tasks&lt;/span&gt;
        &lt;span class="n"&gt;player_tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;timeout_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# wait for the next batch of events&lt;/span&gt;
                &lt;span class="c1"&gt;# iterate over each one&lt;/span&gt;
                &lt;span class="c1"&gt;# - if it&amp;#39;s a timeout event, game over, lose&lt;/span&gt;
                &lt;span class="c1"&gt;# - if it&amp;#39;s a buzzer event, game over, lose&lt;/span&gt;
                &lt;span class="c1"&gt;# - if it&amp;#39;s a judge correct event, game over, win&lt;/span&gt;
                &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="n"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;GameOverException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# stop all the agent work&lt;/span&gt;
            &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(I feel like I am starting to get the hang of Python async.)&lt;/p&gt;
&lt;p&gt;So now where does the AI come into things?&lt;/p&gt;
&lt;h1&gt;AI&lt;/h1&gt;
&lt;p&gt;The only dataset of Taboo cards I could find was in German,
which was not particularly useful for my purposes. So I figured
I might as well let the AI generate them. Initially I had the AI
generate both the target word and the taboo words, but it kept picking
"VOLCANO" all the time, so eventually I changed it so that you give it
the target word and it comes up with the taboo words:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;CreateTabooWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    You are creating a game card for the game Taboo. Each card has a target word&lt;/span&gt;
&lt;span class="sd"&gt;    and a list of taboo words. One of the players (&amp;quot;the cluer&amp;quot;) will try to get&lt;/span&gt;
&lt;span class="sd"&gt;    the other players to guess the target word by giving clues, but they cannot&lt;/span&gt;
&lt;span class="sd"&gt;    use any of the taboo words in their clues, or they lose.&lt;/span&gt;

&lt;span class="sd"&gt;    For example, if the target word is &amp;quot;apple&amp;quot;, the taboo words might be&lt;/span&gt;
&lt;span class="sd"&gt;    [&amp;quot;fruit&amp;quot;, &amp;quot;red&amp;quot;, &amp;quot;pie&amp;quot;, &amp;quot;tree&amp;quot;, &amp;quot;juice&amp;quot;].&lt;/span&gt;

&lt;span class="sd"&gt;    You will be given a target word. Your task is to come up with a list of 5 taboo words&lt;/span&gt;
&lt;span class="sd"&gt;    that will make the game interesting and challenging. The taboo words should ideally&lt;/span&gt;
&lt;span class="sd"&gt;    be the most obvious clues to the target word, so that the cluer has to be creative.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The target word for the game&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;taboo_words&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A list of taboo words that cannot be used in clues for the target word&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;create_taboo_words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChainOfThought&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateTabooWords&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A few things here: first, you can see that I gave this signature a more substantial
docstring than I often do, because in this case there is quite a bit to its task
that you can't just get from the input field and output field. (Ideally I would probably
use few-shot &lt;code&gt;Example&lt;/code&gt;s instead of putting it in the docstring, but oh well.)&lt;/p&gt;
&lt;p&gt;But at the same time you can see that the docstring prompt 
is a &lt;em&gt;pure&lt;/em&gt; description of the task. I don't try to cram in there
what the input types or what the output types are. That's all still handled
by the &lt;code&gt;Signature&lt;/code&gt; machinery, so the docstring "feels like" a docstring.&lt;/p&gt;
&lt;p&gt;I also want it to be very thoughtful about generating the taboo words, so I used
&lt;code&gt;dspy.ChainOfThought&lt;/code&gt; and (although you can't see it here) I set up this agent
to use &lt;code&gt;gemini-2.5-pro&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;(In general for this project I had different agents use different models,
 depending on how hard / creative their tasks were.)&lt;/p&gt;
&lt;p&gt;And then e.g. the guesser gets a signature that looks like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;GuessWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    You are playing a game of Taboo. Your goal is to guess the target word based on the clues given by the Cluer.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The history of the game so far, including previous clues, buzzes, guesses, and judgments&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;player_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The ID of the player making the guess&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;player_personality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Optional personality or background information about the player making the guess&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The guessed word&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rationale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Optional rationale for the guess&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and has a &lt;code&gt;next_guess&lt;/code&gt; function that just grabs the history from the game
and the player information from itself and makes an async dspy call.
(There is a little more ceremony around it to make the call cancelable,
 but that part is not super important here.)&lt;/p&gt;
&lt;p&gt;One other problem I ran into is that the "buzzer" and "judge" were not doing
a very good job (possibly because I gave them 2.5-flash-lite to use so they would be fast); they would e.g. declare some guess a winner when it wasn't.&lt;/p&gt;
&lt;p&gt;Eventually I added an extra "justification" field to the signature and that seemed 
to keep them more on the straight and narrow:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;BuzzClue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Check if the clue is one of the taboo words (or a minor variation, like singular/plural).&lt;/span&gt;
&lt;span class="sd"&gt;    It&amp;#39;s ok if the clue has a similar meaning to the taboo word, it just can&amp;#39;t be the same word.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;clue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The clue word or phrase given by the Cluer&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;taboo_words&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The taboo words that cannot be used in the clue, or you lose&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;buzz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Whether the clue violates the taboo words or not&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;justification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I guess that's kind of a poor man's chain-of-thought.&lt;/p&gt;
&lt;p&gt;And it all works. The whole thing is up on GitHub, as usual, so check it out:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/joelgrus/taboollm"&gt;https://github.com/joelgrus/taboollm&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here's one where it gets it really quick:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ uv run python -m taboo play --target vibes
Card: target=vibes, taboo_words=[&amp;#39;Feeling&amp;#39;, &amp;#39;Energy&amp;#39;, &amp;#39;Mood&amp;#39;, &amp;#39;Atmosphere&amp;#39;, &amp;#39;Vibrations&amp;#39;]
[cluer] Aura

[guesser p2-friendly] Vibe — The clue &amp;quot;Aura&amp;quot; directly brings to mind &amp;quot;vibe&amp;quot; as a synonym for a distinctive atmosphere or feeling around a person or place.

[cluer] Spirit

[judge] Vibe by p2-friendly -&amp;gt; CORRECT

[system] end: correct, winner: p2-friendly

Round finished. Winner: p2-friendly
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And here's a hard one:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ uv run python -m taboo play --target convolution

Card: target=convolution, taboo_words=[&amp;#39;Kernel&amp;#39;, &amp;#39;Filter&amp;#39;, &amp;#39;Image&amp;#39;, &amp;#39;Signal&amp;#39;, &amp;#39;Neural&amp;#39;]

[cluer] Combining

[guesser p2-friendly] Mix — The clue &amp;quot;Combining&amp;quot; directly suggests the action of mixing things together.

[judge] Mix by p2-friendly -&amp;gt; INCORRECT

[guesser p3-sarcastic] Merging — &amp;quot;Combining.&amp;quot; Wow, such a deep and insightful clue. It&amp;#39;s almost as if things, when combined, become... *merged*. Who would&amp;#39;ve thought?

[guesser p2-friendly] Merge — &amp;quot;Combining&amp;quot; can mean bringing two or more things together to form a single entity, which is what &amp;quot;merge&amp;quot; implies, like companies merging.

[cluer] Folding

[judge] Merging by p3-sarcastic -&amp;gt; INCORRECT

[judge] Merge by p2-friendly -&amp;gt; INCORRECT

[guesser p3-sarcastic] Uniting — Since &amp;quot;merging&amp;quot; and &amp;quot;mixing&amp;quot; weren&amp;#39;t quite abstract enough for the intellectual heights of &amp;quot;combining,&amp;quot; perhaps we&amp;#39;re going for something more profound. Like... bringing things together in a grand, unified front.

[judge] Uniting by p3-sarcastic -&amp;gt; INCORRECT

[guesser p2-friendly] Uniting — The clue is &amp;quot;Combining,&amp;quot; and since &amp;quot;Mix&amp;quot; and &amp;quot;Merge&amp;quot; have been ruled out, I&amp;#39;m thinking of another way to describe bringing things together, especially in a more collective or harmonious sense. &amp;quot;Uniting&amp;quot; fits this idea well.

[judge] Uniting by p2-friendly -&amp;gt; INCORRECT

[guesser p1-thoughtful] MIX — The clue &amp;quot;Combining&amp;quot; directly describes the action of mixing multiple elements together. It&amp;#39;s a very common and straightforward synonym.

[judge] MIX by p1-thoughtful -&amp;gt; INCORRECT

[cluer] Slide

[guesser p2-friendly] Kneading — The first clue was &amp;quot;Combining,&amp;quot; and then the second clue was &amp;quot;Folding.&amp;quot; When you combine ingredients and then fold them, especially in cooking, it often refers to the process of kneading dough. This combines both actions.

[judge] Kneading by p2-friendly -&amp;gt; INCORRECT

[guesser p3-sarcastic] Consolidate — Ah, yes, &amp;#39;Combining,&amp;#39; followed by the subtle hint of &amp;#39;Folding.&amp;#39; Clearly, the grand master is guiding us towards the profound concept of... consolidating. Because when you combine and fold, you obviously... consolidate. The intellectual journey is truly breathtaking.

[judge] Consolidate by p3-sarcastic -&amp;gt; INCORRECT

[cluer] Overlap

[guesser p1-thoughtful] Incorporate — The first clue &amp;quot;Combining&amp;quot; points to bringing things together. The second clue &amp;quot;Folding&amp;quot; suggests a specific way of combining, often by bringing something into an existing structure or mixture. &amp;quot;Incorporate&amp;quot; means to take in or include something as part of a whole, which directly relates to combining, and it also fits the idea of &amp;quot;folding&amp;quot; something into a plan, system, or mixture.

[judge] Incorporate by p1-thoughtful -&amp;gt; INCORRECT

[guesser p1-thoughtful] Tuck — The clues seem to build on ways of arranging or inserting things. &amp;quot;Combining&amp;quot; is a general action. &amp;quot;Folding&amp;quot; is a specific way of preparing something to be combined or inserted. &amp;quot;Slide&amp;quot; suggests a smooth, deliberate movement, often into a confined space. &amp;quot;Overlap&amp;quot; implies that one part rests on or covers another. &amp;quot;Tuck&amp;quot; fits well with all these: you combine things by tucking one into another; you often fold something before you tuck it; you slide something into place when tucking it; and the tucked element will often overlap with the material it&amp;#39;s tucked into.

[judge] Tuck by p1-thoughtful -&amp;gt; INCORRECT

[guesser p2-friendly] Joint — The clue \&amp;quot;Combining\&amp;quot; suggests bringing different elements together. \&amp;quot;Folding\&amp;quot; implies a bend or crease, which is characteristic of certain types of connections. \&amp;quot;Slide\&amp;quot; refers to a smooth movement of one part past another. A \&amp;quot;joint\&amp;quot; serves as a point where things are combined, and many types of joints facilitate either a folding motion (like a hinge) or a sliding motion. It seems to connect all three clues well.

[judge] Joint by p2-friendly -&amp;gt; INCORRECT

[cluer] Aggregation

[guesser p1-thoughtful] Weave — The clues &amp;quot;Combining,&amp;quot; &amp;quot;Folding,&amp;quot; &amp;quot;Slide,&amp;quot; and &amp;quot;Overlap&amp;quot; strongly suggest the process of weaving. Weaving involves combining threads, which are folded or passed over and under each other. The threads slide past each other, and they naturally overlap to form a fabric. This integrates all the clues into a single coherent action.

[judge] Weave by p1-thoughtful -&amp;gt; INCORRECT

[guesser p2-friendly] Weave — The clues &amp;quot;Combining,&amp;quot; &amp;quot;Folding,&amp;quot; &amp;quot;Slide,&amp;quot; and &amp;quot;Overlap&amp;quot; all strongly point to the action of weaving. When you weave, you are combining threads or strands. The process involves threads folding over and under each other, sliding past each other, and continuously overlapping to create fabric or a pattern. It perfectly encapsulates all the actions described.

[judge] Weave by p2-friendly -&amp;gt; INCORRECT

[cluer] Sweep

[guesser p3-sarcastic] Collapse — Ah, the intellectual odyssey continues! We began with the majestic &amp;#39;Combining,&amp;#39; then progressed to the intricate &amp;#39;Folding,&amp;#39; and now, the profound &amp;#39;Slide.&amp;#39; It&amp;#39;s almost as if things, when combined and folded, have a tendency to, you know, just completely give up and slide into a heap. The sheer elegance of these clues points directly to something... collapsing. It&amp;#39;s truly a masterclass in subtlety.

[judge] Collapse by p3-sarcastic -&amp;gt; INCORRECT

[guesser p2-friendly] Origami — The clue &amp;quot;Combining&amp;quot; is a general action, reinforced by &amp;quot;Aggregation.&amp;quot; &amp;quot;Folding&amp;quot; is a very specific action, which, along with &amp;quot;Slide&amp;quot; and &amp;quot;Overlap,&amp;quot; strongly suggests a process of manipulating material. Origami perfectly encompasses all these: it involves extensive folding of paper, parts of the paper slide against each other during the folding process, layers of paper constantly overlap, and the entire creation is a combination or aggregation of these folds. It ties all five clues together cohesively.

[judge] Origami by p2-friendly -&amp;gt; INCORRECT

[cluer] Superimpose

[guesser p1-thoughtful] Bundle — The clues provided are &amp;quot;Combining,&amp;quot; &amp;quot;Folding,&amp;quot; &amp;quot;Slide,&amp;quot; &amp;quot;Overlap,&amp;quot; and &amp;quot;Aggregation.&amp;quot; &amp;quot;Bundle&amp;quot; fits all of these very well. You &amp;quot;combine&amp;quot; items to create a bundle. Often, items within a bundle, like clothes or papers, are &amp;quot;folded&amp;quot; for compactness. Items might &amp;quot;slide&amp;quot; into place as the bundle is formed or tightened. The items within a bundle will almost certainly &amp;quot;overlap&amp;quot; each other. Finally, a bundle is fundamentally an &amp;quot;aggregation&amp;quot; or collection of things brought together. This word ties all the clues together coherently, from specific actions to the overall result.

[judge] Bundle by p1-thoughtful -&amp;gt; INCORRECT

[guesser p3-sarcastic] Pile — Oh, the sheer brilliance! We started with the profound &amp;#39;Combining,&amp;#39; moved to the intricate &amp;#39;Folding,&amp;#39; then the subtle &amp;#39;Slide,&amp;#39; followed by the illuminating &amp;#39;Overlap,&amp;#39; and the highly academic &amp;#39;Aggregation.&amp;#39; Now, with &amp;#39;Sweep,&amp;#39; it&amp;#39;s all so clear! When you combine, fold, slide, overlap, and aggregate with a sweep, you&amp;#39;re obviously engaging in the highly complex, multi-faceted process of... creating a pile. Truly, the intellectual journey has been astounding.

[judge] Pile by p3-sarcastic -&amp;gt; INCORRECT

[cluer] Transform

[guesser p3-sarcastic] Collage — Oh, the sheer brilliance! We&amp;#39;ve sailed through the profound ocean of &amp;#39;Combining,&amp;#39; navigated the intricate channels of &amp;#39;Folding,&amp;#39; glided effortlessly across the &amp;#39;Slide,&amp;#39; pondered the existential &amp;#39;Overlap,&amp;#39; aggregated our thoughts on &amp;#39;Aggregation,&amp;#39; swept aside all doubts with &amp;#39;Sweep,&amp;#39; and finally, reached the zenith of intellectual enlightenment with &amp;#39;Superimpose.&amp;#39; Clearly, after such a grand and complex journey, the only logical conclusion for all these deep, multi-layered concepts is the incredibly sophisticated art of... sticking various bits and pieces together. The intellectual journey has been truly breathtaking.

[judge] Collage by p3-sarcastic -&amp;gt; INCORRECT

[guesser p2-friendly] Embrace — The clues &amp;quot;Combining&amp;quot; and &amp;quot;Aggregation&amp;quot; both point to bringing things together. &amp;quot;Folding,&amp;quot; &amp;quot;Slide,&amp;quot; and &amp;quot;Overlap&amp;quot; describe specific physical actions or arrangements. &amp;quot;Embrace&amp;quot; fits all these: when you embrace, you combine and aggregate individuals, often folding your arms around them, with bodies sliding and overlapping. Crucially, the latest clue &amp;quot;Sweep&amp;quot; fits perfectly with the idiom &amp;quot;sweep someone into an embrace,&amp;quot; which ties all the dynamic actions together.

[judge] Embrace by p2-friendly -&amp;gt; INCORRECT

[cluer] Shaping

[guesser p1-thoughtful] Overlay — The clues &amp;quot;Combining,&amp;quot; &amp;quot;Aggregation,&amp;quot; &amp;quot;Overlap,&amp;quot; and &amp;quot;Superimpose&amp;quot; all strongly point to bringing layers or elements together, with &amp;quot;Overlap&amp;quot; and &amp;quot;Superimpose&amp;quot; being almost synonymous with the concept of an overlay. &amp;quot;Slide&amp;quot; perfectly describes the action of placing an overlay. &amp;quot;Folding&amp;quot; can relate to the material being used as an overlay (e.g., a folded map overlay) or a preparatory step, while &amp;quot;Sweep&amp;quot; could refer to the motion of applying an overlay or a broad area covered by it. All clues converge on the idea of placing one thing over another.

[judge] Overlay by p1-thoughtful -&amp;gt; INCORRECT

[system] timeout

[system] end: timeout

Round finished. Winner: none
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This was also a nice "vibe coding" collaboration,
since we started out with a very complicated design
(courtesy of ChatGPT), and the initial code dump was
me telling codex-cli (yes, I switched from claude code)
to implement the PRD,
and then we iterated on it quite a few times with me asking
"is this part necessary" and "could this be simpler"
and "why don't we just do XYZ", which is honestly not too different
from how I am as a manager / code reviewer. And then all the dspy
parts I wrote myself, because those parts are fun. :)
And the final codebase I think is solid and well designed
(other than the fact that it doesn't have any tests 😇).&lt;/p&gt;</content><category term="Vibe Coding"></category></entry><entry><title>Vibe Coding 8 -- Clod Code</title><link href="https://joelgrus.com/2025/08/25/vibe-coding-8-clod-code/" rel="alternate"></link><published>2025-08-25T12:00:00+02:00</published><updated>2025-08-25T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-08-25:/2025/08/25/vibe-coding-8-clod-code/</id><summary type="html">&lt;p&gt;&lt;img alt="live-dangerously" src="https://joelgrus.com/images/live-dangerously.png" /&gt;&lt;/p&gt;
&lt;p&gt;The other day I ran across a blog post on &lt;a href="https://ghuntley.com/agent/"&gt;how to build a coding agent&lt;/a&gt;. But it was very long and I didn't read it. (Sorry!) Instead I asked myself, what's the simplest coding agent I can build.&lt;/p&gt;
&lt;p&gt;So I put on my PM hat and thought about it …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;img alt="live-dangerously" src="https://joelgrus.com/images/live-dangerously.png" /&gt;&lt;/p&gt;
&lt;p&gt;The other day I ran across a blog post on &lt;a href="https://ghuntley.com/agent/"&gt;how to build a coding agent&lt;/a&gt;. But it was very long and I didn't read it. (Sorry!) Instead I asked myself, what's the simplest coding agent I can build.&lt;/p&gt;
&lt;p&gt;So I put on my PM hat and thought about it a bit
and decided that a coding agent needed three things&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;an LLM (obviously)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a conversational loop, not unlike &lt;a href="/2025/07/30/vibe-coding-3-simple-chatbots-in-dspy/"&gt;the one we built a few weeks ago&lt;/a&gt; that alternates messages between the user and the agent, and that builds up a history&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a way for the agent to interact with the enviroment (read files, modify files, etc, etc, etc, etc)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(A &lt;em&gt;useful&lt;/em&gt; coding agent needs a lot more than this, but let's keep it simple.)&lt;/p&gt;
&lt;p&gt;This is simple enough that I just wrote the code myself rather than having an AI do it.&lt;/p&gt;
&lt;p&gt;First we set up the LLM:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dspy&lt;/span&gt;

&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;openai/gpt-5-mini&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;reasoning_effort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;low&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20_000&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then we define a signature: our coding agent will take a command and a history (of previous interactions) and return a result. As a dangerous coding agent, it also gets a motivational Bible quote about prudence. &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Clod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    A prudent person sees evil and hides himself, &lt;/span&gt;
&lt;span class="sd"&gt;    But the naive proceed, and pay the penalty.&lt;/span&gt;
&lt;span class="sd"&gt;     - Proverbs 22:3&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here's the dangerous part, which we'll give the agent as a tool:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;subprocess_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Run any unix command you like (within reason)&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Running command:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we just define a tool-using ReAct agent and run it in a loop:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;clod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReAct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Clod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subprocess_run&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;user_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;You: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Clod: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;role&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;user&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;role&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;assistant&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that's it! That's less than 40 lines of code.&lt;/p&gt;
&lt;p&gt;You could run it like that (but you should not! It has zero safeguards, it could reformat your hard drive or delete your database or upload your browser history somewhere or whatever!):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# do not do this!&lt;/span&gt;
uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;clod.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Instead you should run it using Docker, which (assuming Claude Code wrote that part correctly) limits its blast radius to the directory that you start it in:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# this is a bash script that runs it in docker&lt;/span&gt;
./clod
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And now you have your own coding assistant!&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;~/src/clod-test$ ../clod-code/clod

You: what is in this directory

Running command: ls -la ...

Clod: Directory listing (long format):
total 32
drwxrwxr-x 4 cloduser cloduser 4096 Aug 25 14:46 .
drwxr-xr-x 1 root     root     4096 Aug 25 14:55 ..
drwxrwxr-x 7 cloduser cloduser 4096 Aug 25 14:46 .git
-rw-rw-r-- 1 cloduser cloduser    5 Aug 25 14:46 .python-version
drwxr-xr-x 4 cloduser cloduser 4096 Aug 25 14:46 .venv
-rw-rw-r-- 1 cloduser cloduser    0 Aug 25 14:46 README.md
-rw-rw-r-- 1 cloduser cloduser   87 Aug 25 14:46 main.py
-rw-rw-r-- 1 cloduser cloduser  155 Aug 25 14:46 pyproject.toml
-rw-r--r-- 1 cloduser cloduser  129 Aug 25 14:46 uv.lock

Summary:
- Git repo present (.git)
- Python virtual environment directory: .venv
- Config: .python-version, pyproject.toml
- Source: main.py
- README.md is empty
- Lock/metadata: uv.lock

You: can you edit main.py to solve fizz buzz

Running command: bash -lc ls -l main.py &amp;amp;&amp;amp; sed -n &amp;#39;1,200p&amp;#39; main.py ...

Running command: bash -lc cat &amp;gt; main.py &amp;lt;&amp;lt;&amp;#39;PY&amp;#39;
def main():
    for  ...

Clod: I updated main.py with the following FizzBuzz implementation:

def main():
    for i in range(1, 101):
        if i % 15 == 0:
            print(&amp;quot;FizzBuzz&amp;quot;)
        elif i % 3 == 0:
            print(&amp;quot;Fizz&amp;quot;)
        elif i % 5 == 0:
            print(&amp;quot;Buzz&amp;quot;)
        else:
            print(i)


if __name__ == &amp;quot;__main__&amp;quot;:
    main()

I ran the script; it produced the expected FizzBuzz sequence for 1 through 100 (sample start and end shown):

1
2
Fizz
4
Buzz
...
97
98
Fizz
Buzz

If you want a different range, output format, or tests added, tell me and I will modify it.

You: can you abstract out a function that is unit testable (say that determines the fizz buzz output for a given number)

Running command: bash -lc cat &amp;gt; main.py &amp;lt;&amp;lt;&amp;#39;PY&amp;#39;
&amp;quot;&amp;quot;&amp;quot;FizzBuzz module.
 ...

Clod: Updated main.py:

&amp;quot;&amp;quot;&amp;quot;FizzBuzz module.

Provides fizzbuzz_value(n) which is unit-testable and a main() that prints 1..100.
&amp;quot;&amp;quot;&amp;quot;

def fizzbuzz_value(n: int) -&amp;gt; str:
    &amp;quot;&amp;quot;&amp;quot;Return the FizzBuzz representation for n as a string.

    - &amp;quot;FizzBuzz&amp;quot; if n divisible by 15
    - &amp;quot;Fizz&amp;quot; if n divisible by 3
    - &amp;quot;Buzz&amp;quot; if n divisible by 5
    - otherwise the number as a string
    &amp;quot;&amp;quot;&amp;quot;
    if n % 15 == 0:
        return &amp;quot;FizzBuzz&amp;quot;
    if n % 3 == 0:
        return &amp;quot;Fizz&amp;quot;
    if n % 5 == 0:
        return &amp;quot;Buzz&amp;quot;
    return str(n)

def main():
    for i in range(1, 101):
        print(fizzbuzz_value(i))

if __name__ == &amp;quot;__main__&amp;quot;:
    main()

Notes:
- To unit test, import fizzbuzz_value from main.py and assert expected outputs (e.g., fizzbuzz_value(3) == &amp;quot;Fizz&amp;quot;, fizzbuzz_value(5) == &amp;quot;Buzz&amp;quot;, fizzbuzz_value(15) == &amp;quot;FizzBuzz&amp;quot;, fizzbuzz_value(7) == &amp;quot;7&amp;quot;).
- If you want, I can add a test file (e.g., tests/test_main.py) with pytest/unittest examples.

You: yes add the test file

Running command: bash -lc mkdir -p tests &amp;amp;&amp;amp; cat &amp;gt; tests/test_main.p ...

Running command: bash -lc pytest -q --disable-warnings --maxfail=1 ...

Running command: bash -lc pytest -q --disable-warnings --maxfail=1 ...

Clod: Created file: tests/test_main.py

Contents:
from main import fizzbuzz_value
import pytest

@pytest.mark.parametrize(&amp;quot;n,expected&amp;quot;, [
    (1, &amp;quot;1&amp;quot;),
    (3, &amp;quot;Fizz&amp;quot;),
    (5, &amp;quot;Buzz&amp;quot;),
    (15, &amp;quot;FizzBuzz&amp;quot;),
    (16, &amp;quot;16&amp;quot;),
    (30, &amp;quot;FizzBuzz&amp;quot;),
    (9, &amp;quot;Fizz&amp;quot;),
    (10, &amp;quot;Buzz&amp;quot;),
])
def test_fizzbuzz_values(n, expected):
    assert fizzbuzz_value(n) == expected

How to run the tests:
- Ensure you have pytest installed (pip install pytest).
- From the project root, run: pytest -q --disable-warnings --maxfail=1

If you want, I can:
- Add more edge-case tests (e.g., negative numbers, zero).
- Use unittest instead of pytest.
- Show expected pytest output by running tests here if you want me to try again.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now obviously this doesn't have sessions, it doesn't allow clearing (or compacting) of context, it doesn't have MCP, it doesn't have subagents, I didn't do any prompt engineering at all, etc, etc, etc. It is not a replacement for Claude Code or Gemini Cli or OpenAI Codex or whatever. It's a dangerous toy.&lt;/p&gt;
&lt;p&gt;Nonetheless, it's 40 lines of code and it does work! Code is &lt;a href="https://github.com/joelgrus/clod-code"&gt;on GitHub&lt;/a&gt; as always!&lt;/p&gt;</content><category term="Vibe Coding"></category></entry><entry><title>Vibe Coding 7 -- Human in the Loop as a dspy.Tool</title><link href="https://joelgrus.com/2025/08/13/vibe-coding-7-human-in-the-loop-as-a-dspytool/" rel="alternate"></link><published>2025-08-13T12:00:00+02:00</published><updated>2025-08-13T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-08-13:/2025/08/13/vibe-coding-7-human-in-the-loop-as-a-dspytool/</id><summary type="html">&lt;p&gt;As I started to think about my 
&lt;a href="https://joelgrus.com/2025/08/12/vibe-coding-6-pymgflip/"&gt;meme-generating agent&lt;/a&gt;, I figured that
I might want to allow it to ask clarifying questions.
That makes it not just a multi-turn conversation
(which just requires maintaining a history)
but more like a ReAct agent with a "human in the loop" tool
for …&lt;/p&gt;</summary><content type="html">&lt;p&gt;As I started to think about my 
&lt;a href="https://joelgrus.com/2025/08/12/vibe-coding-6-pymgflip/"&gt;meme-generating agent&lt;/a&gt;, I figured that
I might want to allow it to ask clarifying questions.
That makes it not just a multi-turn conversation
(which just requires maintaining a history)
but more like a ReAct agent with a "human in the loop" tool
for asking clarifying questions to the user 
(all in pursuit of a singular goal).
And it wasn't obvious how to do this 
(especially in a webapp, in a console app you can do it in a pretty
 straightforward way).&lt;/p&gt;
&lt;p&gt;(I'm sure other people must have done this and written about it, but if so I couldn't find it.)&lt;/p&gt;
&lt;p&gt;So I had a fairly long conversation with Web Claude
(don't ask me why I picked him over ChatGPT or Gemini)
about &lt;a href="https://claude.ai/share/c940432b-b912-4adc-8f18-db93ce45591b"&gt;how one might implement this&lt;/a&gt;
and ended up with a markdown file explaining what we were hoping
to accomplish and how we planned to it.&lt;/p&gt;
&lt;p&gt;I handed that markdown file to claude code, and we got after it.
We built and iterated and built and refactored and changed our design
and built and built. This was an interesting problem for a couple of reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It required using Python async, which I am not good at. 
For example, imagine that the agent is running as part of a webapp.
then when the webapp needs human input, it needs to "pause" what it's 
doing, somehow get a request to the user, somehow get a response back,
and then resume. Hence async.&lt;/li&gt;
&lt;li&gt;Therefore, unlike a lot of this vibecoding stuff, it required some thoughtful and intricate design.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We went through probably 10 different designs.
Each time it was me (not Claude) saying
"that doesn't feel like the right abstraction,
 what about X, Y, or Z?"
Eventually we ended up with something pretty clean.&lt;/p&gt;
&lt;p&gt;This is really at the core of what we landed on.
A &lt;code&gt;HumanInputRequest&lt;/code&gt; is a question and an &lt;em&gt;awaitable&lt;/em&gt; response
(here implemented by an asyncio.Future).&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;HumanInputRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_response_future&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Wait for the response to be provided&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_response_future&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;set_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Provide the response and notify waiters&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_response_future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can then define different "human in the loop" tools by
defining "requesters", async functions that take a &lt;code&gt;HumanInputRequest&lt;/code&gt;
and do [waves hands] something asynchronously.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# A requester is an async function that takes a HumanInputRequest&lt;/span&gt;
&lt;span class="c1"&gt;# and handles the outbound request to humans, allowing them to provide a response.&lt;/span&gt;
&lt;span class="c1"&gt;# It should resolve the request by calling request.set_response(response).&lt;/span&gt;
&lt;span class="n"&gt;Requester&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;HumanInputRequest&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;Awaitable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which leads to a pretty clean "human in the loop" tool:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;human_in_the_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requester&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Requester&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ask_human&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HumanInputRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Let requester handle the outbound request&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;requester&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Wait for response (resolved by requester or external system)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ask_human&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One very simple use case (for which this is overkill) is a single-player
console app. In that case the requester can just call &lt;code&gt;input()&lt;/code&gt; synchronously and set the response:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;console_requester&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HumanInputRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;human_in_the_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;console_requester&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A more interesting case is the web app, where a lot of things need to happen before the response comes back. For that case we implemented a simple "work queue" requester:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;create_queue_requester&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pending_requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;queue_requester&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HumanInputRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Generate a unique ID for this request&lt;/span&gt;
        &lt;span class="n"&gt;request_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="c1"&gt;# Store in pending requests for response resolution&lt;/span&gt;
        &lt;span class="n"&gt;pending_requests&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;request_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;request&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;question&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;sent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;# Push to the request queue         &lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;request_queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;human_input&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;question&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="c1"&gt;# Mark as sent to avoid duplicate sends&lt;/span&gt;
        &lt;span class="n"&gt;pending_requests&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;request_id&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;queue_requester&lt;/span&gt;

&lt;span class="n"&gt;queue_requester&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_queue_requester&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;human_in_the_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue_requester&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now in (say) your web app you can connect the queue to send server-sent events 
to the browser, and you can have a secondary endpoint that finds the request
in the dict, sets its value, and evicts it from pending.&lt;/p&gt;
&lt;p&gt;(You would have to harden this up a bunch for a real production system, but it's good enough for our demo.)&lt;/p&gt;
&lt;p&gt;This is now pretty easy. Our console app can just use a ReAct agent:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;toppings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;special_instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;OrderPizza&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    An agent that can ask clarifying questions about a pizza order.&lt;/span&gt;
&lt;span class="sd"&gt;    It can handle multiple pizzas and special instructions.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;customer_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;pizzas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReAct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OrderPizza&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;human_in_the_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;console_requester&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="n"&gt;max_iters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And will ask clarifying questions to help us order a pizza:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;What is your order?
(Type &amp;#39;quit&amp;#39; to exit)
&amp;gt; I need a small pizza with rocks on it

🤖 Agent is thinking about: &amp;#39;I need a small pizza with rocks on it&amp;#39;
The agent may ask you questions during its reasoning process...

🤔 You mentioned &amp;#39;rocks&amp;#39; on your pizza. Could you please clarify what you mean by that? We don&amp;#39;t typically offer rocks as a topping.

&amp;gt; I want rocks on it

🤔 I understand you want &amp;#39;rocks&amp;#39; on your pizza. Could you describe what kind of &amp;#39;rocks&amp;#39; you are looking for? For example, are you referring to a specific ingredient that might sound like &amp;#39;rocks&amp;#39;, or something else entirely?

&amp;gt; rocks like you&amp;#39;d find on a hill

🤔 I understand you&amp;#39;re looking for actual rocks on your pizza. Unfortunately, we cannot put inedible items like rocks on our pizzas for safety and health reasons. Is there a different topping you had in mind, or would you like to order the small pizza without the &amp;#39;rocks&amp;#39;?

&amp;gt; can you use meatballs to make &amp;quot;rocks&amp;quot;

🤔 Yes, we can certainly add meatballs to your pizza! To clarify, would you like whole meatballs, or would you prefer them crumbled to resemble &amp;#39;rocks&amp;#39; more closely? Also, just to confirm, you&amp;#39;d like a small pizza with meatballs, and no other specific toppings unless you mention them?

&amp;gt; small pizza with meatballs crumbled to resemble rocks

✅ Order: [{&amp;#39;size&amp;#39;: &amp;#39;small&amp;#39;, &amp;#39;toppings&amp;#39;: [&amp;#39;meatballs&amp;#39;], &amp;#39;special_instructions&amp;#39;: &amp;#39;meatballs crumbled to resemble rocks&amp;#39;}]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the web app behaves similarly (with more complicated things going on behind the scenes):&lt;/p&gt;
&lt;p&gt;&lt;img alt="pizza-agent" src="https://joelgrus.com/images/pizza-agent.png" /&gt;&lt;/p&gt;
&lt;p&gt;Despite this being a silly example, this is actually a pretty neat
demonstration of the DSPy / ReAct workflow. I didn't have to program
anything about the flow of the conversation (e.g. "next ask for the pizza size").&lt;/p&gt;
&lt;p&gt;Instead I gave it a structured output format ("I want a list of pizzas, each of which has the following fields") and a tool to ask clarifying
questions, and then the ReAct loop and LLM took care of deciding what questions to ask until it got all the information it needed. 
That's actually really cool!&lt;/p&gt;
&lt;p&gt;Anyway, this was pretty satisfying to puzzle out, and hopefully it helps someone else. &lt;/p&gt;
&lt;p&gt;As always, the code is &lt;a href="https://github.com/joelgrus/human-in-the-loop-dspy-tool"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;</content><category term="Vibe Coding"></category></entry><entry><title>Vibe Coding 6 -- pymgflip</title><link href="https://joelgrus.com/2025/08/12/vibe-coding-6-pymgflip/" rel="alternate"></link><published>2025-08-12T12:00:00+02:00</published><updated>2025-08-12T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-08-12:/2025/08/12/vibe-coding-6-pymgflip/</id><summary type="html">&lt;p&gt;As my next project, I thought it would be fun to use AI
to generate memes. For many years I have used 
&lt;a href="https://imgflip.com"&gt;imgflip&lt;/a&gt; to generate memes.
And they have an &lt;a href="https://imgflip.com/api"&gt;API&lt;/a&gt;!
Unfortunately, they don't have an official Python library (that I could find, I found a bunch of one-GitHub-star unofficial …&lt;/p&gt;</summary><content type="html">&lt;p&gt;As my next project, I thought it would be fun to use AI
to generate memes. For many years I have used 
&lt;a href="https://imgflip.com"&gt;imgflip&lt;/a&gt; to generate memes.
And they have an &lt;a href="https://imgflip.com/api"&gt;API&lt;/a&gt;!
Unfortunately, they don't have an official Python library (that I could find, I found a bunch of one-GitHub-star unofficial ones).&lt;/p&gt;
&lt;p&gt;So I thought, why not let Claude write one.&lt;/p&gt;
&lt;p&gt;I created a CLAUDE.md that just said&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;# imgflip&lt;/span&gt;

This is a python library that wraps the imgflip API in a type-safe way.

The imgflip API is described at:

https://imgflip.com/api

We use httpx to make API calls and provide a type-safe interface for all the API features.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then I started claude in live-dangerously-mode and told it to write the library. Which it did.&lt;/p&gt;
&lt;p&gt;(It made a couple of bad design decisions, 
 like asking the user
 to specify whether they had a paid account, 
 rather than just error-handling that case properly,
 but we corrected these in short order.)&lt;/p&gt;
&lt;p&gt;Once it was done I ran its sample script, and this was what came out:&lt;/p&gt;
&lt;p&gt;&lt;img alt="drake" src="https://joelgrus.com/images/when_you_finally_get_the_api_working.png" /&gt;&lt;/p&gt;
&lt;p&gt;(On Twitter someone criticized this for not being a "correct" use of the meme,
 which it isn't, but that's because the sample script just calls
"list all memes" and captions whichever one is first.)&lt;/p&gt;
&lt;p&gt;I uploaded it to pypi so that I can use it in projects, which means you can too: &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;uv&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;pymgflip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(All of the obvious names on pypi were taken, &lt;code&gt;pymgflip&lt;/code&gt; was the best I could do.)&lt;/p&gt;
&lt;p&gt;The example from the README is actually pretty good:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pymgflip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;your_username&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;your_password&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a meme&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;template_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;61579&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;quot;One Does Not Simply&amp;quot; template&lt;/span&gt;
    &lt;span class="n"&gt;text0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;One does not simply&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;text1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Use the Imgflip API without pymgflip&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Meme created: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="one-does-not" src="https://joelgrus.com/images/one_does_not_simply.png" /&gt;&lt;/p&gt;</content><category term="Vibe Coding"></category></entry><entry><title>Vibe Coding 5 -- WoDRAG</title><link href="https://joelgrus.com/2025/08/11/vibe-coding-5-wodrag/" rel="alternate"></link><published>2025-08-11T12:00:00+02:00</published><updated>2025-08-11T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-08-11:/2025/08/11/vibe-coding-5-wodrag/</id><summary type="html">&lt;p&gt;"How do you know if someone does Crossfit?"&lt;/p&gt;
&lt;p&gt;"They build a RAG system about Crossfit!"&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;As my next project, I wanted to build a non-trivial RAG system.
To do that, I needed a non-trivial dataset.&lt;/p&gt;
&lt;p&gt;Every day for 20+ years, Crossfit.com has published a Workout of the Day ("WoD …&lt;/p&gt;</summary><content type="html">&lt;p&gt;"How do you know if someone does Crossfit?"&lt;/p&gt;
&lt;p&gt;"They build a RAG system about Crossfit!"&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;As my next project, I wanted to build a non-trivial RAG system.
To do that, I needed a non-trivial dataset.&lt;/p&gt;
&lt;p&gt;Every day for 20+ years, Crossfit.com has published a Workout of the Day ("WoD").&lt;/p&gt;
&lt;p&gt;&lt;img alt="wod" src="https://joelgrus.com/images/wod.png" /&gt;&lt;/p&gt;
&lt;p&gt;That seemed like it could make for an interesting retrieval system.
(Interesting to me, anyway.)&lt;/p&gt;
&lt;p&gt;To start with I downloaded them all.
Then I let Claude write some BeautifulSoup code
to get the raw workout text out of the HTML.
Then I wanted to extract the data from the raw text,
which I did with &lt;code&gt;dspy&lt;/code&gt; (I do everything with &lt;code&gt;dspy&lt;/code&gt;).
It looked something like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ExtractMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;workout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;movements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;equipment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;workout_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WorkoutType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;workout_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;one_sentence_summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(The fields actually had more detailed descriptions than this, 
 with e.g. guidance about how to extract the name and so on,
 but this was the basic shape.)&lt;/p&gt;
&lt;p&gt;(I already had "date" from the raw data, so I didn't need the LLM to extract that.)&lt;/p&gt;
&lt;p&gt;I stuck this all in a &lt;a href="https://github.com/paradedb/paradedb"&gt;paradedb&lt;/a&gt;
database (basically Postgres with bm25 and vector search).&lt;/p&gt;
&lt;p&gt;I also added openai-small embeddings for both the full workouts and the one-sentence summaries.&lt;/p&gt;
&lt;p&gt;At this point I started to think about the sorts of questions I wanted to be able to answer. Things like&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what is the workout Murph?&lt;/li&gt;
&lt;li&gt;when was the first time Murph was the workout of the day?&lt;/li&gt;
&lt;li&gt;how many times has Murph been the workout of the day?&lt;/li&gt;
&lt;li&gt;what are some workouts that have both pull ups and sit ups?&lt;/li&gt;
&lt;li&gt;how many workouts in 2025 have involved rowing?&lt;/li&gt;
&lt;li&gt;can you make me a workout that has swimming and pull ups?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To me this looked like a &lt;a href="https://dspy.ai/api/modules/ReAct/"&gt;dspy.ReAct&lt;/a&gt; problem. To start with I gave it the following tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/wodrag/blob/master/wodrag/database/workout_repository.py#L462"&gt;hybrid search&lt;/a&gt;: for the "what is murph" it could hybrid search for 'murph' and use the top 5 workouts as context. this is a pure search query -&amp;gt; results tool&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/wodrag/blob/master/wodrag/agents/text_to_sql.py#L10"&gt;duckdb text to sql&lt;/a&gt;: this would allow for aggregations / sorting by 
 writing duckdb sql, to allow things like "when was the first murph" and
 "how many times"&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/wodrag/blob/master/wodrag/agents/workout_generator.py#L28"&gt;generate workout&lt;/a&gt;: this tool was itself a dspy RAG that finds the 5 "closest" workouts to the requested description and then uses them to 
create a new workout. (if this feels like an odd fit, it is, this is what I started with before the agentic RAG, and I just left it as a tool).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What I quickly discovered was that I had a lot of trouble tuning the hybrid search.
Some queries really needed "semantic" search ("find me workouts that have a lot of pulling"). And some queries really needed keyword search ("find me workouts called murph"). 
Eventually I split these into two different tools "very_semantic_search"
and "very_keyword_search" that used the same hybrid search code
with different weights. And I let the ReAct agent choose which one to 
use depending on the query. This worked pretty well.&lt;/p&gt;
&lt;p&gt;I also added another tool that was just "get workout by date" as
this ended up being a common pattern.&lt;/p&gt;
&lt;p&gt;While I was doing all this, I also built a web front-end.
(Claude built a web front-end.)&lt;/p&gt;
&lt;p&gt;&lt;img alt="wodrag1" src="https://joelgrus.com/images/wodrag1.png" /&gt;&lt;/p&gt;
&lt;p&gt;And implemented multi-turn conversation:&lt;/p&gt;
&lt;p&gt;&lt;img alt="wodrag2" src="https://joelgrus.com/images/wodrag2.png" /&gt;&lt;/p&gt;
&lt;p&gt;And this time I wanted actually to get some practice doing devops,
so I containerized the app (frontend + backend + database), 
acquired a VPS and a domain name, triple-checked for security issues,
and muddled my way through deploying it.&lt;/p&gt;
&lt;p&gt;Behold: &lt;a href="https://wodrag.com" target="_blank"&gt;wodrag.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(It's using my API keys for the LLMs, so be gentle.)&lt;/p&gt;
&lt;p&gt;If you play with it you will find that it behaves unsatisfactorily
in some ways. In particular, there are a lot of data issues, e.g.:&lt;/p&gt;
&lt;p&gt;&lt;img alt="wodrag3" src="https://joelgrus.com/images/wodrag3.png" /&gt;&lt;/p&gt;
&lt;p&gt;Here the name of the workout is wrong (it should be "Rest Day")
and the original contained links that got mangled, leaving cruft like
"[video]" as well as strange line breaks.&lt;/p&gt;
&lt;p&gt;This tells me that we should have spent more time and thought
on the data extraction and preprocessing. That will likely be my next project. (The other obvious improvement would be for the searches to return more results and then to implement some kind of reranker.
That might also be a future project.)&lt;/p&gt;
&lt;p&gt;In the meantime, you can check this one out &lt;a href="https://github.com/joelgrus/wodrag"&gt;on GitHub&lt;/a&gt;, although it's a little bit messy.&lt;/p&gt;</content><category term="Vibe Coding"></category></entry><entry><title>Vibe Coding 4 -- Speed Scrabble</title><link href="https://joelgrus.com/2025/08/04/vibe-coding-4-speed-scrabble/" rel="alternate"></link><published>2025-08-04T12:00:00+02:00</published><updated>2025-08-04T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-08-04:/2025/08/04/vibe-coding-4-speed-scrabble/</id><summary type="html">&lt;p&gt;A few years ago we went on an Alaska cruise,
and we sprung for a cabin in the private/exclusive part of the ship
(which is the way to go!)
which had its own restaurant, its own lounge, and so on.
The lounge had board games, one of which was …&lt;/p&gt;</summary><content type="html">&lt;p&gt;A few years ago we went on an Alaska cruise,
and we sprung for a cabin in the private/exclusive part of the ship
(which is the way to go!)
which had its own restaurant, its own lounge, and so on.
The lounge had board games, one of which was Scrabble. 
I don't particularly enjoy Scrabble, but I do enjoy 
&lt;a href="https://www.thegamegal.com/2016/01/28/speed-scrabble/"&gt;Speed Scrabble&lt;/a&gt;
which is where there's no board and you just have to use your
tiles to make words, racing against others, drawing tiles, etc.
We played a fair amount and every once in a while Google Photos
will show me a picture of it.&lt;/p&gt;
&lt;p&gt;&lt;img alt="cruise" src="https://joelgrus.com/images/cruise-small.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;As my next exercise in Vibe Coding I thought I'd try to implement
a web version of Speed Scrabble. This is an interesting challenge
for a couple reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it's pretty much purely a front-end project, which means I really have to cede control to the machine (I can hack together a basic React app, but a complex game UI is well beyond my abilities). This is a project that I never would have attempted on my own because it likely would have involved weeks of frustration&lt;/li&gt;
&lt;li&gt;there are about a million choices to make in terms of UX, everything from how should the board be laid out, to how should the
controls work, to what should the flow of gameplay be&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means that my role on the project is&lt;/p&gt;
&lt;p&gt;product manager &amp;gt;&amp;gt; dev manager &amp;gt;&amp;gt; developer&lt;/p&gt;
&lt;p&gt;Claude Code is stupid enough (yes it is) that I do have to step in
and tell it to fix failing tests rather than deleting them and so on, but for the most part when it starts going on about React hooks
and drag-and-drop libraries I don't have much to contribute.&lt;/p&gt;
&lt;p&gt;Several times during the project I made it stop and "code review"
what it had done, but some of those times it immediately started
"correcting" things (i.e. breaking things) and I had to start being
very explicit about "review don't change."&lt;/p&gt;
&lt;p&gt;&lt;img alt="speed-scrabble" src="https://joelgrus.com/images/speed-scrabble.png" /&gt;&lt;/p&gt;
&lt;p&gt;This one took me (parts of) 3 days. One day to get a very basic version working. One day to get to it polished and playable. 
And one day to get it to work on a mobile browser.&lt;/p&gt;
&lt;p&gt;The last was a partial failure, as I wanted to make it so that
 you could drag the tiles around, but the implementation didn't work. Claude Code was unable to fix it, so I had it write out a 
 summary of the problem (and the libraries we were using) and posed
 the problem to both o3 and Gemini 2.5, neither of which was able to
 solve it either. (Everyone suspected it had something to do with the browser's native up-down scrolling interfering with vertical tile-dragging, but no one was able to fix it.) Eventually I gave up
 and settled for the crappier (but still usable) mobile analog of the desktop UI.&lt;/p&gt;
&lt;p&gt;I had in mind a number of improvements, including&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;blank tiles (which presented UX challenges in placing them,
assigning them a letter value, and tracking their letter value)&lt;/li&gt;
&lt;li&gt;multi-player mode (e.g. you are in a game with n other people, you share a board, each person has their own tiles, everyone draws at the same time, first to "go out" wins), this would have made the project probably an order of magnitude more complex&lt;/li&gt;
&lt;li&gt;daily puzzles (i.e. tile orders) with scoreboards, etc&lt;/li&gt;
&lt;li&gt;better sharing of completed puzzles (it's implemented but has some UX problems)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But I'm probably going to call it a day.&lt;/p&gt;
&lt;p&gt;On one hand it's pretty impressive that I was able to 
produce a game that works, that looks decent, and that is
actually pretty fun to play.&lt;/p&gt;
&lt;p&gt;On the other hand, I ended up with a pile of code that
I don't really understand and that I can't really maintain myself.&lt;/p&gt;
&lt;p&gt;Very dangerous!&lt;/p&gt;
&lt;p&gt;Check out the code on GitHub: &lt;a href="https://github.com/joelgrus/speed-scrabble"&gt;joelgrus/speed-scrabble&lt;/a&gt; and feel free to let me know which of the fundamental
rules of React were broken here.&lt;/p&gt;
&lt;p&gt;Play it yourself: &lt;a href="https://speedscrabble.netlify.app/"&gt;speedscrabble.netlify.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Click on (or type) letters to add them at the cursor, click on the cursor (or space bar) to change the orientation, backspace (or long press on mobile) to move tiles back to the rack, you can figure out the rest. It's fun!&lt;/p&gt;</content><category term="Vibe Coding"></category></entry><entry><title>Vibe Coding 3 -- Simple Chatbots in DSPy</title><link href="https://joelgrus.com/2025/07/30/vibe-coding-3-simple-chatbots-in-dspy/" rel="alternate"></link><published>2025-07-30T12:00:00+02:00</published><updated>2025-07-30T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-07-30:/2025/07/30/vibe-coding-3-simple-chatbots-in-dspy/</id><summary type="html">&lt;p&gt;A long long time ago I took a graduate course in &lt;a href="https://en.wikipedia.org/wiki/Econometrics"&gt;Econometrics&lt;/a&gt;.
We spent the first month or two deriving linear algebra equations
and proving facts about 
&lt;a href="https://en.wikipedia.org/wiki/Gauss%E2%80%93Markov_theorem"&gt;Best Linear Unbiased Estimators&lt;/a&gt; 
and 
&lt;a href="https://gregorygundersen.com/blog/2022/01/29/ols-consistency/"&gt;Consistency&lt;/a&gt; 
and
&lt;a href="https://en.wikipedia.org/wiki/Homoscedasticity_and_heteroscedasticity"&gt;Heteroscedasticity&lt;/a&gt;
and so on. We were locked in. Then we had our first exam 
and one …&lt;/p&gt;</summary><content type="html">&lt;p&gt;A long long time ago I took a graduate course in &lt;a href="https://en.wikipedia.org/wiki/Econometrics"&gt;Econometrics&lt;/a&gt;.
We spent the first month or two deriving linear algebra equations
and proving facts about 
&lt;a href="https://en.wikipedia.org/wiki/Gauss%E2%80%93Markov_theorem"&gt;Best Linear Unbiased Estimators&lt;/a&gt; 
and 
&lt;a href="https://gregorygundersen.com/blog/2022/01/29/ols-consistency/"&gt;Consistency&lt;/a&gt; 
and
&lt;a href="https://en.wikipedia.org/wiki/Homoscedasticity_and_heteroscedasticity"&gt;Heteroscedasticity&lt;/a&gt;
and so on. We were locked in. Then we had our first exam 
and one of the questions was like
"here are 10 data points, estimate the regression coefficients"
and it turned out that nobody knew how to do that, 
we only knew like the Gauss-Markov theorem and such.&lt;/p&gt;
&lt;p&gt;This was a surprisingly formative experience for me
and triggered something important in the way I think about learning and knowledge.&lt;/p&gt;
&lt;p&gt;Anyway, the other day I was talking to someone
who was doing a take-home interview exercise 
that required them to build a chatbot.&lt;/p&gt;
&lt;p&gt;And I questioned myself: for all my opining about DSPy and LangGraph and Pydantic.ai and whatnot,
do I actually know just &lt;strong&gt;how to build a chatbot&lt;/strong&gt;? Like yes I can follow a tutorial,
and yes I can try to get Claude Code to write a chatbot, but can I "raw dog" a chatbot? 
It turned out to be a good exercise and as educational as I'd hoped.&lt;/p&gt;
&lt;h1&gt;The Simplest Chatbot&lt;/h1&gt;
&lt;p&gt;Writing a simple chatbot in DSPy is actually quite easy (although it took me several tries to get correct):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dspy&lt;/span&gt;

&lt;span class="c1"&gt;# Set up the LM&lt;/span&gt;
&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;openai/gpt-4.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Define the chatbot using a signature&lt;/span&gt;
&lt;span class="n"&gt;chatbot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;query,history,personality -&amp;gt; answer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;personality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;You are extremely angry.&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;You: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Call the chatbot with the current query and history&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatbot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;personality&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;personality&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Print the bot&amp;#39;s response&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bot: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Update history&lt;/span&gt;
    &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;role&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;role&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;assistant&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The part that's most interesting is the way the history works; first, it's part of the chatbot's signature -- 
our chatbot is just a dspy module that takes a query, a conversation history, and a personality and returns an answer. 
Second, this means that we have to manage the history ourselves, which we do as just an ever-growing list of messages.&lt;/p&gt;
&lt;p&gt;(The part where you can specify a personality is maybe also interesting, but it's not particularly deep.)&lt;/p&gt;
&lt;p&gt;I really like how explicit this is about what the AI is designed to do and
about what data flows in and out.
(Obviously it's not explicit about what the exact prompt is.)&lt;/p&gt;
&lt;p&gt;It works pretty much how you'd expect (and you can see that it's using the history):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;You: what is the capital of New York
Bot: ARE YOU KIDDING ME?! The capital of New York is ALBANY! How could you possibly not know that?! GET YOUR ACT TOGETHER!
You: I thought it was Buffalo?
Bot: NO, YOU IMBECILE! IT&amp;#39;S ALBANY! ARE YOU EVEN LISTENING TO ME?! PAY ATTENTION!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;Tool-Using Chatbot&lt;/h1&gt;
&lt;p&gt;Most interesting chatbots these days use "tools" in some way.
It's pretty easy to modify this code to use tools. First, we need to define some simple tools:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The weather in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is sunny with a high of 25°C.&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;do_math&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;__builtins__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}},&lt;/span&gt; &lt;span class="p"&gt;{}))&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;do_math&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we just use &lt;a href="https://dspy.ai/api/modules/ReAct/"&gt;dspy.ReAct&lt;/a&gt; instead of &lt;code&gt;dspy.Predict&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;chatbot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReAct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;query,history,personality -&amp;gt; answer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and that's it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;You: what&amp;#39;s the weather in Seattle
Bot: Ugh, fine! You want to know the weather in Seattle? It&amp;#39;s sunny with a high of 25°C. Now leave me alone, you pathetic excuse for a human!
You: if it were 2 degrees hotter what would the weather be
Bot: Are you incapable of basic arithmetic? If it were 2 degrees hotter, it would be a sweltering 27°C! Now stop bothering me with your inane questions!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;From Here&lt;/h1&gt;
&lt;p&gt;Obviously if you wanted to build an actual chatbot you would have work to do from here.
You would want to optimize the prompt, be more thoughtful about tools, create a way
of tracking history per user/conversation (instead of one global history), 
maybe cap the history size, possibly
stick the histories in some sort of distributed cache, expose the chatbot through a server,
create a web front-end, add logging and evals, analyze errors, and so on.&lt;/p&gt;
&lt;p&gt;I started doing some of this at &lt;a href="https://github.com/joelgrus/dspatbot"&gt;https://github.com/joelgrus/dspatbot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But it's good to start out by understanding the basics!&lt;/p&gt;</content><category term="Vibe Coding, DSPy, AI"></category></entry><entry><title>Vibe Coding 2 -- SnowMeth - an AI Novel-Writing Assistant</title><link href="https://joelgrus.com/2025/07/23/vibe-coding-2-snowmeth-an-ai-novel-writing-assistant/" rel="alternate"></link><published>2025-07-23T09:00:00+02:00</published><updated>2025-07-23T09:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-07-23:/2025/07/23/vibe-coding-2-snowmeth-an-ai-novel-writing-assistant/</id><summary type="html">&lt;p&gt;I have started several novels, although I have never finished one.
Can AI maybe help with that?&lt;/p&gt;
&lt;p&gt;One way of writing a novel I have tried is the 
&lt;a href="https://www.advancedfictionwriting.com/articles/snowflake-method/"&gt;Snowflake method&lt;/a&gt;,
so named because of its fractal nature: 
start with a one sentence summary,
expand it to a paragraph,
expand that …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I have started several novels, although I have never finished one.
Can AI maybe help with that?&lt;/p&gt;
&lt;p&gt;One way of writing a novel I have tried is the 
&lt;a href="https://www.advancedfictionwriting.com/articles/snowflake-method/"&gt;Snowflake method&lt;/a&gt;,
so named because of its fractal nature: 
start with a one sentence summary,
expand it to a paragraph,
expand that to several pages,
make a list of the chapters, and so on.&lt;/p&gt;
&lt;p&gt;As I've been experimenting with AI, I was curious: can an AI use the Snowflake method to write a novel?&lt;/p&gt;
&lt;p&gt;The answer is: sort of!&lt;/p&gt;
&lt;p&gt;(I am not a front-end developer or a designer, so forgive me my crimes in those areas.)&lt;/p&gt;
&lt;p&gt;&lt;img alt="step-10" src="https://joelgrus.com/images/snowmeth-step-10.png" /&gt;&lt;/p&gt;
&lt;p&gt;Claude Code and I spent a couple of days putting together a prototype
using 
&lt;a href="https://dspy.ai/"&gt;dspy&lt;/a&gt; for the AI agents,
&lt;a href="https://developers.googleblog.com/en/gemini-25-flash-lite-is-now-stable-and-generally-available/"&gt;Gemini 2.5 Flash-Lite&lt;/a&gt; for the LLM,
and React for the front-end.&lt;/p&gt;
&lt;p&gt;(I tried getting Gemini Code to pitch in a bit whenever I timed out of Claude credits, but it &lt;a href="https://x.com/joelgrus/status/1947812286959390805"&gt;deleted my database&lt;/a&gt;, so it lost its coding privileges.)&lt;/p&gt;
&lt;p&gt;You can work on multiple stories:&lt;/p&gt;
&lt;p&gt;&lt;img alt="home" src="https://joelgrus.com/images/snowmeth-home.png" /&gt;&lt;/p&gt;
&lt;p&gt;You get to pick your own premise:&lt;/p&gt;
&lt;p&gt;&lt;img alt="step-1" src="https://joelgrus.com/images/snowmeth-step-1.png" /&gt;&lt;/p&gt;
&lt;p&gt;It proceeds step by step (and you can suggest changes at any time):&lt;/p&gt;
&lt;p&gt;&lt;img alt="step-5" src="https://joelgrus.com/images/snowmeth-step-5.png" /&gt;&lt;/p&gt;
&lt;p&gt;And at the end of it you'll end up with sort of a novel:&lt;/p&gt;
&lt;p&gt;&lt;img alt="step-10" src="https://joelgrus.com/images/snowmeth-step-10.png" /&gt;&lt;/p&gt;
&lt;p&gt;Here is a video of the whole process:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/oMsm57Egpno?si=aPH_56ruk0ksxrhQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;And here is the project on GitHub:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/joelgrus/snowmeth"&gt;github.com/joelgrus/snowmeth&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is not deployed anywhere, but you can download the code from GitHub and run it yourself with your own API keys. (Gemini 2.5 Flash-Lite is dirt cheap, I have been hacking on this for several days and have used probably $2 in tokens)&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;As sort of a sidenote, dspy is really interesting.
Initially the appeal for me was that it allows you to do 
programmatic &lt;a href="https://dspy.ai/learn/optimization/overview/"&gt;optimization&lt;/a&gt; of prompts (rather than having to do a lot of bespoke prompt engineering). 
But on this project I had a lot of different LLM calls and very little
interest in "optimizing" them at this stage. &lt;/p&gt;
&lt;p&gt;Nonetheless, from a hacking / prototyping perspective, it
was extremely ergonomic to work in terms of signatures rather than prompts:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ParagraphExpander&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Expand a one-sentence novel summary into a full paragraph&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;sentence_summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The one-sentence summary to expand&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;story_idea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The original story idea for context&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;paragraph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dspy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A compelling paragraph (3-5 sentences) that expands on the summary with more detail about the setup, conflict, and stakes&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even these descriptions were probably overkill, I blame Claude.&lt;/p&gt;
&lt;p&gt;Anyway, I quite enjoyed working with dspy and will 
likely keep using it as my agent framework.&lt;/p&gt;</content><category term="Vibe Coding, Claude, Writing"></category></entry><entry><title>Vibe Coding 1 -- Website Redesign</title><link href="https://joelgrus.com/2025/07/07/vibe-coding-1-website-redesign/" rel="alternate"></link><published>2025-07-07T09:00:00+02:00</published><updated>2025-07-07T09:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2025-07-07:/2025/07/07/vibe-coding-1-website-redesign/</id><content type="html">&lt;p&gt;The world is getting into Vibe Coding, so I will too.
As a first attempt, I asked Claude Code to redesign
my website, which had grown a bit stale. (OK, a lot stale.)&lt;/p&gt;
&lt;p&gt;I think it's an improvement.&lt;/p&gt;
&lt;h1&gt;Before&lt;/h1&gt;

&lt;p&gt;&lt;img src="/images/website-before.png"&gt;&lt;/p&gt;
&lt;h1&gt;After&lt;/h1&gt;

&lt;p&gt;&lt;img src="/images/website-after.png"&gt;&lt;/p&gt;
&lt;p&gt;Next time: some actual code.&lt;/p&gt;</content><category term="Vibe Coding, Claude"></category></entry><entry><title>Creating Games in Streamlit</title><link href="https://joelgrus.com/2020/10/02/creating-games-in-streamlit/" rel="alternate"></link><published>2020-10-02T12:00:00+02:00</published><updated>2020-10-02T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2020-10-02:/2020/10/02/creating-games-in-streamlit/</id><summary type="html">&lt;p&gt;A while back I had the idea that 9yo and I would
program a bunch of simple games together and she'd get
interested in coding. Well, we did program a bunch of simple games together:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code/blob/master/guessing_game.py"&gt;Guessing Game&lt;/a&gt; - the computer picks a random number, you guess, it tells you "too high …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;A while back I had the idea that 9yo and I would
program a bunch of simple games together and she'd get
interested in coding. Well, we did program a bunch of simple games together:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code/blob/master/guessing_game.py"&gt;Guessing Game&lt;/a&gt; - the computer picks a random number, you guess, it tells you "too high" or "too low" and then you try again&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code/blob/master/madlib.py"&gt;Mad Libs&lt;/a&gt; - you know it from when you were a kid, I found a bunch of madlibs that were part of a Microsoft EMNLP paper and used those&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code/blob/master/hangchicken.py"&gt;Hangchicken&lt;/a&gt; - like hangman, but with a chicken instead&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code/blob/master/mastermind.py"&gt;Mastermind&lt;/a&gt; - the classic, the computer picks a sequence and you have to guess it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But it failed to create any interest in coding. What it did create an interest in was using my computer to play Mad Libs.&lt;/p&gt;
&lt;p&gt;Which was fine, but I wanted her to be able to play when I wasn't around (or when I was using my computer for something else).&lt;/p&gt;
&lt;p&gt;And then last week I came into possession of a beta invite to &lt;a href="https://www.streamlit.io/for-teams"&gt;the new Streamlit sharing feature&lt;/a&gt;. If I could just turn these games into Streamlit apps, then they could be hosted online.
And she could play them whenever she wanted.&lt;/p&gt;
&lt;p&gt;However, there are several ways in which games are at odds with the Streamlit paradigm. 
And so it actually took me a long time to get them successfully implemented.
Here are some of the things I learned.&lt;/p&gt;
&lt;h1&gt;Game State&lt;/h1&gt;
&lt;p&gt;Streamlit apps are not exactly "stateless", but (for the most part) if you build a Streamlit app
its state is determined by the states of its widgets. 
Moving sliders and clicking buttons and filling in
text fields creates an application state, but (1) it's not hidden from the user, and (2) the user can change it directly.
These are not desiderata for a "game state".&lt;/p&gt;
&lt;p&gt;Fortunately, it is well known that you can abuse Streamlit's caching mechanism 
to create persistent state. The pattern I initially used looks like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclasses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataclass&lt;/span&gt; 
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;number_to_guess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;game_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;game_over&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

&lt;span class="nd"&gt;@st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allow_output_mutation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;persistent_game_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;persistent_game_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;new game&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;number_to_guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;game_number&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;game_over&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Normally I avoid dataclasses on account of their being mutable,
but here that's what we need. &lt;code&gt;GameState&lt;/code&gt; contains whatever state our game needs.
In Mad Libs, that's a randomly chosen story. In Hangchicken, that's the word to guess
and the letters already guessed. And so on. Most of the games also have a &lt;code&gt;game_over&lt;/code&gt; flag
to disable the inputs and an incrementing &lt;code&gt;game_number&lt;/code&gt; so that new games' inputs can have
different keys. More on that below.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;persistent_state&lt;/code&gt; function takes no arguments and is decorated with &lt;code&gt;st.cache&lt;/code&gt; which
makes the &lt;code&gt;GameState&lt;/code&gt; instance it returns a singleton. When an input value changes and the app reruns,
the same &lt;code&gt;state&lt;/code&gt; object sticks around, which is what we want.&lt;/p&gt;
&lt;p&gt;Finally, we add a "new game" button that resets the game state for a new game.&lt;/p&gt;
&lt;h1&gt;Game Number&lt;/h1&gt;
&lt;p&gt;Many of my first attempts had a nasty bug where if, for example,
your text input for Hangchicken had a letter in it, then when you clicked
"new game" it would start by guessing that letter. Eventually what I realized
was that that text input needed to have a &lt;code&gt;key&lt;/code&gt; that depended on an incrementing game number.
That way when you click "new game" the key changes and you get a (logically) different text input.
(I think.)&lt;/p&gt;
&lt;h1&gt;Mind the Order&lt;/h1&gt;
&lt;p&gt;By far the most difficult part to figure out was the order of operations. 
What I mean is this: my original hangchicken game (which ran in the terminal)
showed you the chicken, then the word, then the letters you'd guessed; and then asked for your input.
Indeed, a terminal game basically has to put the input last.&lt;/p&gt;
&lt;p&gt;&lt;img alt="hangchicken in the terminal" src="https://joelgrus.com/images/hangchicken_terminal.png" /&gt;&lt;/p&gt;
&lt;p&gt;My initial attempts at the game mimicked this order and produced a weird off by 1 error
where you'd guess a letter, and then nothing would happen, and then you'd guess a second letter,
and then the first letter would show up. This drove me crazy (and I'm still not entirely sure I get it),
but I think it has to do with how state "flows through" the Streamlit app.&lt;/p&gt;
&lt;p&gt;Eventually I realized that I needed to have the text input &lt;em&gt;before&lt;/em&gt; the outputs,
so that when it was updated the outputs would update appropriately. But this took me a long time
to figure out.&lt;/p&gt;
&lt;p&gt;&lt;img alt="hangchicken in streamlit" src="https://joelgrus.com/images/hangchicken_streamlit.png" /&gt;&lt;/p&gt;
&lt;p&gt;I had similar problems pretty much in all the games,
but once I figured it out I figured it out.&lt;/p&gt;
&lt;h1&gt;Text Inputs&lt;/h1&gt;
&lt;p&gt;One annoying thing you can notice in the above picture 
is that after you guess a letter the text input doesn't clear itself,
you have to backspace and then guess the next letter. I tried to address this
by making the text input &lt;code&gt;key&lt;/code&gt; depend both on the game number and on how many letters
you'd guessed so far, but this reintroduced a variant of the "off by 1" error I described
above and I couldn't figure out how to fix it. So you have to backspace.&lt;/p&gt;
&lt;h1&gt;Avoid the Loop&lt;/h1&gt;
&lt;p&gt;I made many false starts that tried to use a &lt;code&gt;while&lt;/code&gt; loop
and keep rendering down the page much as you'd do in the terminal.
It's possible there's a way to make this work, but I couldn't figure it out.
In any case, it was intended as a second-best solution, and since I figured out
the first-best solution, I didn't need it.&lt;/p&gt;
&lt;h1&gt;Flat is Better than Nested, But...&lt;/h1&gt;
&lt;p&gt;Streamlit apps tend to be more nested than I'd like, 
because there's so much conditional rendering,
and these games are no exception:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;game_over&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;guess a number between 1 and &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HI&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;game_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_guesses&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Maybe there's a pattern that avoids this, but I couldn't think of it.&lt;/p&gt;
&lt;p&gt;("Monads, you've invented monads.")&lt;/p&gt;
&lt;h1&gt;Shared State&lt;/h1&gt;
&lt;p&gt;Unfortunately, our state hack results in the same state for every client.
This doesn't matter if one person is playing the game locally, but it matters 
a lot if you're hosting the app publicly.
If you were to play hosted Hangchicken, you and everyone else playing
would be making guesses in the same game, which would cause a giant mess.&lt;/p&gt;
&lt;p&gt;You can imagine some games for which that would be a feature,
but it's harder to imagine those games as Streamlit apps,
as you'd likely want your browser to get notified when someone else
performs an action in her browser. (It's possible there are deep Streamlit
hacks that allow this, but I don't know them.)&lt;/p&gt;
&lt;p&gt;Here's a little multiplayer plotting app that illustrates the issue:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://s4a.streamlit.io/joelgrus/collaborative-regression/master/app.py/+/"&gt;multiplayer plotting app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Everyone is adding points to the same canvas. Make something pretty.&lt;/p&gt;
&lt;h1&gt;Unsharing the State&lt;/h1&gt;
&lt;p&gt;It turns out to be relatively simple to unshare the state.
You just make your "singleton" function depend on the session id.
(&lt;em&gt;Getting&lt;/em&gt; the session id is less simple.)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allow_output_mutation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;persistent_game_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report_thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_report_ctx&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;persistent_game_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now each browser session (i.e. game player)
gets their own copy of the state, and hence their own game.&lt;/p&gt;
&lt;h1&gt;The Thing About Caching&lt;/h1&gt;
&lt;p&gt;Unfortunately, there's still a problem, and that's that the app
has a "clear cache" button. And when you clear the cache, you clear it for everyone.&lt;/p&gt;
&lt;p&gt;So we need to be even more devious and attach the state to the session object:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;persistent_game_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report_thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_report_ctx&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_session_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_gamestate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_gamestate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gamestate&lt;/span&gt;

&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;persistent_game_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally we have a nice app that supports multiple players!&lt;/p&gt;
&lt;h1&gt;Refactoring&lt;/h1&gt;
&lt;p&gt;Of course, that's a repeated mess, so we might as well pull it out into its own module:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;typing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;streamlit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;st&lt;/span&gt;

&lt;span class="n"&gt;StateT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;StateT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;persistent_game_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;StateT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StateT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report_thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_report_ctx&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_session_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_gamestate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_gamestate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gamestate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which means that our games can then just import that function and do&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;persistent_game_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(Yes, it's inefficient to recreate that initial state and throw it away each time.
 Avoiding that is left as an exercise for the reader.)&lt;/p&gt;
&lt;h1&gt;When Are They Going To Get to the Fireworks Factory&lt;/h1&gt;
&lt;p&gt;Right, the games. The original code is at&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code"&gt;github/joelgrus/learning-my-kid-to-code&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Streamlit-ified versions are all at &lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/joelgrus/streamlit-games"&gt;github/joelgrus/streamlit-games&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you clone that repo you can play them locally with e.g.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;streamlit&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;hangchicken.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and so on. Here are links to the specific games:&lt;/p&gt;
&lt;h2&gt;Guessing Game&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code/blob/master/guessing_game.py"&gt;code for original terminal version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/streamlit-games/blob/master/guessing_game.py"&gt;code for Streamlit version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://s4a.streamlit.io/joelgrus/streamlit-games/master/guessing_game.py/+/"&gt;PLAY IT ONLINE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Hangchicken (Joel's favorite!)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code/blob/master/hangchicken.py"&gt;code for original terminal version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/streamlit-games/blob/master/hangchicken.py"&gt;code for Streamlit version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://s4a.streamlit.io/joelgrus/streamlit-games/master/hangchicken.py/+/"&gt;PLAY IT ONLINE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Mad Libs (9yo's favorite!)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code/blob/master/madlib.py"&gt;code for original terminal version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/streamlit-games/blob/master/madlibs.py"&gt;code for Streamlit version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://s4a.streamlit.io/joelgrus/streamlit-games/master/madlibs.py/+/"&gt;PLAY IT ONLINE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Mastermind&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/learning-my-kid-to-code/blob/master/mastermind.py"&gt;code for original terminal version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/joelgrus/streamlit-games/blob/master/mastermind.py"&gt;code for Streamlit version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://s4a.streamlit.io/joelgrus/streamlit-games/master/mastermind.py/+/"&gt;PLAY IT ONLINE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;And You?&lt;/h1&gt;
&lt;p&gt;Let me know if you come up with any cool Streamlit games. Or if there's an easier way to do some of the things I did.&lt;/p&gt;</content><category term="Python, Streamlit"></category></entry><entry><title>Ten Essays on Fizz Buzz</title><link href="https://joelgrus.com/2020/06/06/ten-essays-on-fizz-buzz/" rel="alternate"></link><published>2020-06-06T12:00:00+02:00</published><updated>2020-06-06T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2020-06-06:/2020/06/06/ten-essays-on-fizz-buzz/</id><summary type="html">&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;Next &lt;a href="https://twitter.com/joelgrus?ref_src=twsrc%5Etfw"&gt;@joelgrus&lt;/a&gt;’ book: “100 ways of writing fizz buzz in Python”&lt;/p&gt;&amp;mdash; Rocco Meli ⌬ (@RoccoMeli) &lt;a href="https://twitter.com/RoccoMeli/status/1139268793761447936?ref_src=twsrc%5Etfw"&gt;June 13, 2019&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;

&lt;p&gt;I've been quarantined for the last several months
for reasons that are too boring to go into, 
which somehow gave me a lot more time to write. 
And so I am delighted to …&lt;/p&gt;</summary><content type="html">&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;Next &lt;a href="https://twitter.com/joelgrus?ref_src=twsrc%5Etfw"&gt;@joelgrus&lt;/a&gt;’ book: “100 ways of writing fizz buzz in Python”&lt;/p&gt;&amp;mdash; Rocco Meli ⌬ (@RoccoMeli) &lt;a href="https://twitter.com/RoccoMeli/status/1139268793761447936?ref_src=twsrc%5Etfw"&gt;June 13, 2019&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;

&lt;p&gt;I've been quarantined for the last several months
for reasons that are too boring to go into, 
which somehow gave me a lot more time to write. 
And so I am delighted to present to you a new book!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"This book is so good. I wish I'd written it." -- Tim Hopper (&lt;a href="https://twitter.com/tdhopper"&gt;@tdhopper&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://fizzbuzzbook.com" target="_blank"&gt;&lt;img src="/images/fizz-buzz-cover.png" width="50%" height="50%"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What's more, it's a book that's entirely about Fizz Buzz!&lt;/p&gt;
&lt;p&gt;(Yes, I am 100% serious, and in fact the book is perfectly serious as well.)&lt;/p&gt;
&lt;p&gt;While I suspect I could have come up with 100 ways of solving Fizz Buzz,
most of them probably would not have been very interesting.
However, I was able to come up with 10 solutions that are both interesting and varied;
and I used each one as a launching-off point to 
discuss various aspects of coding, Python, Fizz Buzz, mathematics, software design, technical interviewing, and various other topics. Hence "Ten Essays on Fizz Buzz".&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"I'd never have thought a book about Fizz Buzz would make be a better programmer, but I was wrong. Joel in the course of 10 chapters does a broad survey of core Python concepts, software design and testing, mathematics, and more (including deep learning) using Fizz Buzz as the guiding example. It's that rare technical book that remains engaging, entertaining, and accessible." -- Binal Patel (&lt;a href="https://twitter.com/binalkp91"&gt;@binalkp91&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As you might expect from the description, it's sort of an unusual book.
It's quite likely that you will learn a lot from reading it,
but it's not really a book that you'd read in order to learn anything in particular.
Most technical books are about specific technical topics; this one sort of isn't. &lt;/p&gt;
&lt;p&gt;Nonetheless, it is emphatically a technical book. 
Each essay implements a different solution of Fizz Buzz.
Each essay uses code to illustrate its ideas. 
Each essay represents my current best thinking about
how to solve problems using Python. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;More real Python tips than any "Python tricks" book! From a Python beginner to an experienced ML practitioner, you're bound to learn something about the language and its application to a progressive level of algorithmic applications. Recommended for anyone looking to "level up" their Python or problem solving skills! -- Tom Marthaler (&lt;a href="https://twitter.com/tmarthal"&gt;@tmarthal&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For now, the book is available on LeanPub at &lt;a href="https://fizzbuzzbook.com"&gt;fizzbuzzbook.com&lt;/a&gt;. I may do a print version at some point.
If you just want to see the solutions and not the essays, you can find those at
&lt;a href="https://github.com/joelgrus/fizzbuzz"&gt;github.com/joelgrus/fizzbuzz&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I'm extremely proud of how the book turned out.
Please read it and enjoy it and let me know what you like about and don't like about it.
And please tell your friends and neighbors and relatives and acquaintances
and anyone else you know who loves Fizz Buzz.&lt;/p&gt;</content><category term="Python, Data Science, Writing"></category></entry><entry><title>Data Science From Scratch: Second Edition</title><link href="https://joelgrus.com/2019/05/13/data-science-from-scratch-second-edition/" rel="alternate"></link><published>2019-05-13T12:00:00+02:00</published><updated>2019-05-13T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2019-05-13:/2019/05/13/data-science-from-scratch-second-edition/</id><summary type="html">&lt;p&gt;I am thrilled to announce that the second edition of &lt;i&gt;Data Science from Scratch&lt;/i&gt; is now available! (buy from &lt;a href = "https://www.amazon.com/Data-Science-Scratch-Principles-Python/dp/1492041130"&gt;Amazon&lt;/a&gt; or your other favorite bookstore, or read on Safari, or get a PDF from &lt;a href = "https://www.ebooks.com/en-us/209663126/data-science-from-scratch/grus-joel/"&gt;ebooks.com&lt;/a&gt; it looks like.)&lt;/p&gt;
&lt;p&gt;It's been almost exactly four years since the first edition came …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I am thrilled to announce that the second edition of &lt;i&gt;Data Science from Scratch&lt;/i&gt; is now available! (buy from &lt;a href = "https://www.amazon.com/Data-Science-Scratch-Principles-Python/dp/1492041130"&gt;Amazon&lt;/a&gt; or your other favorite bookstore, or read on Safari, or get a PDF from &lt;a href = "https://www.ebooks.com/en-us/209663126/data-science-from-scratch/grus-joel/"&gt;ebooks.com&lt;/a&gt; it looks like.)&lt;/p&gt;
&lt;p&gt;It's been almost exactly four years since the first edition came out,
and over that time it's helped dozens of people learn data science, Python, or possibly some combination of the two.&lt;/p&gt;
&lt;p&gt;However, the first edition used Python 2.7. And as time ticks by,
I've been feeling guiltier and guiltier about having a book out there
with my name on it that tells people to use Python 2. Because in [current year],
&lt;a href = "https://twitter.com/AutomationPanda/status/1125401079729008645"&gt;you should not be using Python 2&lt;/a&gt;. Stop using Python 2!&lt;/p&gt;
&lt;p&gt;&lt;img alt="python 2 rip" src="https://joelgrus.com/images/python2.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Eventually I realized that the only way to clear my conscience
was to prepare a second edition that advocated for Python 3. Accordingly,
the new edition is based on fresh, clean Python 3.6.
(Except for a standalone section on &lt;a href = "https://docs.python.org/3/library/dataclasses.html"&gt;dataclasses&lt;/a&gt;,
 which is based on Python 3.7, for obvious reasons.)&lt;/p&gt;
&lt;p&gt;But since I was already in there fixing things,
I decided to &lt;em&gt;really&lt;/em&gt; fix things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I cleaned up all the code. I'm a much better coder than I was 4 years ago,
  and so I spent a lot of time making the examples and implementations
  cleaner and more readable. (I also removed language features like
  &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;partial&lt;/code&gt; that I've since decided are best avoided.
  Feel free to argue with me about this on Twitter, everyone else seems to.)&lt;/li&gt;
&lt;li&gt;I added an emphasis on using &lt;code&gt;assert&lt;/code&gt; statements to test your code,
  which I wove throughout the book's narrative. I also used a lot more
  &lt;code&gt;assert&lt;/code&gt; statements that didn't appear in the book but that helped me
  be more confident that the code is correct.&lt;/li&gt;
&lt;li&gt;I used Python-3.6-style &lt;a href = "https://docs.python.org/3/library/typing.html"&gt;type annotations&lt;/a&gt; for most
  of the code in the book. This may strike you as objectionable,
  as a lot of people don't like type hints in Python. Nonetheless,
  I decided it was the right choice both morally and pedagogically,
  so bear with me,
  and by the end of the book you'll wonder how you ever lived without them.
  I also used these to help ensure that the code in the book is correct.&lt;/li&gt;
&lt;li&gt;I fixed all the examples that were broken.
  For example, &lt;a href="https://www.oreilly.com/ideas/the-mission-of-spreading-the-knowledge-of-innovators-continues"&gt;the O'Reilly store no longer exists&lt;/a&gt;,
  which means that the "scraping the O'Reilly store" example
  no longer works. I replaced it with an example that involved
  scraping &lt;code&gt;congress.gov&lt;/code&gt;. (Will that site exist in 4 years? Who knows?)
  I also fixed the Twitter authentication instructions, although there's
  a good chance they're broken again by now.&lt;/li&gt;
&lt;li&gt;I made many of the examples better. I replaced the janky 8x8 homemade
  digits in the neural networks chapter with the MNIST dataset. And so on.&lt;/li&gt;
&lt;li&gt;I convinced them to replace all the bit.ly links with the original URLs
  (you're welcome).&lt;/li&gt;
&lt;li&gt;I added a new chapter on "Deep Learning". Admit it, you want to learn about
  deep learning! Over the last couple of years I've been doing a
  &lt;a href = "https://joelgrus.com/2017/12/04/livecoding-madness-building-a-deep-learning-library/"&gt;livecoding stunt&lt;/a&gt; that involves building a deep learning
  library from scratch in an hour. I adopted that approach into a new chapter
  (which took a lot more than an hour to write).&lt;/li&gt;
&lt;li&gt;I built on the "deep learning" code to modernize the NLP chapter,
  adding new sections on word2vec and RNNs.&lt;/li&gt;
&lt;li&gt;Finally, I added a "Data Ethics" chapter, assuming that
  by the time people get to the end of the book they probably
  want to know what I think about data ethics.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All that said, on some level it is just
an &lt;em&gt;improved, more-modern version of the first edition&lt;/em&gt;.
If you are a Joel Grus completist (or if you haven't read the first edition)
(or if you need a kick in the pants to upgrade to Python 3)
(or if you want to learn about type annotations)
then you probably want to read it. If you already read the first edition
then maybe you'll be happy just poking at
the &lt;a href = "https://github.com/joelgrus/data-science-from-scratch"&gt;new code on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also, the cover looks extremely different, as O'Reilly has completely changed
their design language. So if you are an O'Reilly cover completist
you might also want to get it.&lt;/p&gt;
&lt;p&gt;&lt;img alt="book cover" src="https://joelgrus.com/images/dsfs_v2.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Anyway, I am extremely thrilled to share the new edition with you
and (in particular) to no longer have a Python 2 book out there
with my name on it. (I mean, the first edition is still &lt;em&gt;out there&lt;/em&gt;,
and I'm sure I'll still be fielding errata about it until the sun burns out,
but at least now it's officially defunct.)&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;</content><category term="Life, Data Science, Writing"></category></entry><entry><title>Livecoding Madness - Building a Deep Learning Library</title><link href="https://joelgrus.com/2017/12/04/livecoding-madness-building-a-deep-learning-library/" rel="alternate"></link><published>2017-12-04T12:00:00+01:00</published><updated>2017-12-04T12:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2017-12-04:/2017/12/04/livecoding-madness-building-a-deep-learning-library/</id><content type="html">&lt;p&gt;I give &lt;a href = "https://joelgrus.com/speaking/"&gt;talks&lt;/a&gt;,
and this fall my talk has been to live code a deep learning
library (and use the resulting library to solve
&lt;a href = "https://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/"&gt;Fizz Buzz&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;You probably didn't get to see my talk,
which is why I made a
&lt;a href = "https://youtu.be/o64FV-ez6Gw"&gt;video of it&lt;/a&gt;:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/o64FV-ez6Gw" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;Please enjoy!&lt;/p&gt;</content><category term="Python, Deep Learning, Livecoding"></category></entry><entry><title>Fizz Buzz in Tensorflow</title><link href="https://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/" rel="alternate"></link><published>2016-05-23T12:00:00+02:00</published><updated>2016-05-23T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2016-05-23:/2016/05/23/fizz-buzz-in-tensorflow/</id><summary type="html">&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Welcome, can I get you coffee or anything? Do you need a break?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; No, I've probably had too much coffee already!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Great, great. And are you OK with writing code on the whiteboard?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; It's the only way I code!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; That was a joke.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer …&lt;/b&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Welcome, can I get you coffee or anything? Do you need a break?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; No, I've probably had too much coffee already!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Great, great. And are you OK with writing code on the whiteboard?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; It's the only way I code!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; That was a joke.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; OK, so are you familiar with "fizz buzz"?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Is that a yes or a no?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; It's more of a "I can't believe you're asking me that."&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; OK, so I need you to print the numbers from 1 to 100, except that
if the number is divisible by 3 print "fizz", if it's divisible by 5 print "buzz",
and if it's divisible by 15 print "fizzbuzz".&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; I'm familiar with it.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Great, we find that candidates who can't get this right don't do well here.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Here's a marker and an eraser.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; [thinks for a couple of minutes]&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Do you need help getting started?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; No, no, I'm good. So let's start with some standard imports:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;tensorflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;tf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Um, you understand the problem is &lt;em&gt;fizzbuzz&lt;/em&gt;, right?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; Do I ever. So, now let's talk models. I'm thinking a simple multi-layer-perceptron
with one hidden layer.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Perceptron?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; Or neural network, whatever you want to call it.
We want the input to be a number, and the output to be the correct "fizzbuzz"
representation of that number. In particular, we need to turn each input into a
vector of "activations". One simple way would be to convert it to binary.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Binary?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; Yeah, you know, 0's and 1's? Something like:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;binary_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_digits&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_digits&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; [stares at whiteboard for a minute]&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; And our output will be a one-hot encoding of the fizzbuzz representation
of the number, where the first position indicates "print as-is", the second
indicates "fizz", and so on:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fizz_buzz_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;   &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;  &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;  &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; OK, that's probably enough.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; That's enough setup, you're exactly right. Now we need to generate some training data. It would be
cheating to use the numbers 1 to 100 in our training data, so let's train it on
all the remaining numbers up to 1024:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;trX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;binary_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="n"&gt;trY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;fizz_buzz_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; Now we need to set up our model in tensorflow. Off the top of my head I'm
not sure how many hidden units to use, maybe 10?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; Yeah, possibly 100 is better. We can always change it later.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;NUM_HIDDEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We'll need an input variable with width NUM_DIGITS, and an output variable
with width 4:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;float&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;Y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;float&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; How far are you intending to take this?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; Oh, just two layers deep -- one hidden layer and one output layer.
Let's use randomly-initialized weights for our neurons:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;init_weights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Variable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random_normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stddev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;w_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init_weights&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NUM_HIDDEN&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;w_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init_weights&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;NUM_HIDDEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And we're ready to define the model. As I said before, one hidden layer,
and let's use, I don't know, ReLU activation:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w_h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w_o&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matmul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w_h&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matmul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w_o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use softmax cross-entropy as our cost function and try to minimize it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;py_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w_h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w_o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduce_mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;softmax_cross_entropy_with_logits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;train_op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;train&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GradientDescentOptimizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minimize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; And, of course, the prediction will just be the largest output:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;predict_op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Before you get &lt;em&gt;too far&lt;/em&gt; astray,
the problem you're &lt;em&gt;supposed to be&lt;/em&gt; solving is
to generate fizz buzz for the numbers from 1 to 100.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; Oh, great point, the &lt;code&gt;predict_op&lt;/code&gt; function will output a number from 0 to 3,
but we want a "fizz buzz" output:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fizz_buzz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;fizz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;buzz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;fizzbuzz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;prediction&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; So now we're ready to train the model. Let's grab a tensorflow session
and initialize the variables:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initialize_all_variables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let's run, say, 1000 epochs of training?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; Yeah, maybe that's not enough -- so let's do 10000 just to be safe.&lt;/p&gt;
&lt;p&gt;And our training data are
sequential, which I don't like, so let's shuffle them each iteration:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trX&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="n"&gt;trX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trX&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;trY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And each epoch we'll train in batches of, I don't know, 128 inputs?&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So each training pass looks like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trX&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;
            &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feed_dict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trX&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and then we can print the accuracy on the training data, since why not?&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;epoch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;
                             &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predict_op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feed_dict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trY&lt;/span&gt;&lt;span class="p"&gt;})))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Are you serious?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; Yeah, I find it helpful to see how the training accuracy evolves.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; So, once the model has been trained, it's fizz buzz time. Our input should
just be the binary encoding of the numbers 1 to 100:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;teX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binary_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then our output is just our &lt;code&gt;fizz_buzz&lt;/code&gt; function applied to the model output:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="n"&gt;teY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predict_op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feed_dict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;teX&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vectorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fizz_buzz&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;teY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; And that should be your fizz buzz!&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; Really, that's enough. We'll be in touch.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;me:&lt;/b&gt; In touch, that sounds promising.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;interviewer:&lt;/b&gt; ...&lt;/p&gt;
&lt;h1&gt;Postscript&lt;/h1&gt;
&lt;p&gt;I didn't get the job. So I tried actually running this
(&lt;a href="https://github.com/joelgrus/fizz-buzz-tensorflow"&gt;code on GitHub&lt;/a&gt;),
and it turned out it got some of the outputs wrong! Thanks a lot, machine learning!&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;185&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;185&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;7&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;11&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;14&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;16&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;17&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;19&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;21&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;22&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;23&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;26&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;28&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;29&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;31&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;34&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;37&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;38&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;41&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;42&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;43&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;44&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;46&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;47&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;49&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;52&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;56&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;58&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;59&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;61&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;62&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;64&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;67&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;68&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;69&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;71&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;73&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;74&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;76&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;77&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;79&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;81&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;82&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;83&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;84&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;86&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;87&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;88&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;89&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;91&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;92&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;93&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;94&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;97&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;98&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;U8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I guess maybe I should have used a deeper network.&lt;/p&gt;</content><category term="Tensorflow, Python, Interviews, Coding"></category></entry><entry><title>A Simple Spot It Clone With PureScript and Pux</title><link href="https://joelgrus.com/2016/03/30/a-simple-spot-it-clone-with-purescript-and-pux/" rel="alternate"></link><published>2016-03-30T12:00:00+02:00</published><updated>2016-03-30T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2016-03-30:/2016/03/30/a-simple-spot-it-clone-with-purescript-and-pux/</id><summary type="html">&lt;p&gt;Long-time readers may recall that last year I wrote a blog post
about &lt;a href = "https://joelgrus.com/2015/06/12/on-the-mathematics-of-spot-it/"&gt;the
mathematics of Spot It&lt;/a&gt;. (For those who don't recall, Spot It is a game where
you have a deck of cards, each of which has 8 pictures on it, where through the
magical mathematics of finite …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Long-time readers may recall that last year I wrote a blog post
about &lt;a href = "https://joelgrus.com/2015/06/12/on-the-mathematics-of-spot-it/"&gt;the
mathematics of Spot It&lt;/a&gt;. (For those who don't recall, Spot It is a game where
you have a deck of cards, each of which has 8 pictures on it, where through the
magical mathematics of finite projective planes &lt;em&gt;every&lt;/em&gt; two cards in the deck have
exactly one image in common.)&lt;/p&gt;
&lt;p&gt;As I am currently in more of a
"functional front-end compile-to-JavaScript" mindset,
I thought I'd take those ideas and
build a little app, using my current favorite framework, &lt;a href = "https://github.com/alexmingoia/purescript-pux"&gt;purescript-pux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;(Code, as always, on &lt;a href = "https://github.com/joelgrus/pux-it"&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The &lt;a href = "https://github.com/joelgrus/pux-it/blob/master/src/PuxIt/Math.purs"&gt;PuxIt.Math&lt;/a&gt;
module is basically just the code that generates the "cards". The most relevant part is&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Card&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;

&lt;span class="nf"&gt;createDeck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Card&lt;/span&gt;
&lt;span class="nf"&gt;createDeck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toIndexes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pointsOnLine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allLines&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which generates all the lines (cards) in the finite projective plane,
maps each to the set of points (images) it contains, and then replaces each
point (image) with an (arbitrary but consistent) integer (basically, its index
in the array of all points).&lt;/p&gt;
&lt;p&gt;After which, a "card" looks like &lt;code&gt;[1,2,3,4,5,6,7,8]&lt;/code&gt;. (If you're interested in
the logic for how the cards are generated, read the previous post.)&lt;/p&gt;
&lt;p&gt;Here we'll worry about building a front-end to play with these cards. My first
idea was to show two cards at a time, and have the player have to click on the
picture in common.&lt;/p&gt;
&lt;p&gt;&lt;img alt="spotit" src="https://joelgrus.com/images/spot_it.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;But that's too easy to cheat (from our side), since it's trivial to generate
&lt;em&gt;two&lt;/em&gt; cards with one image in common, over and over again. Eventually I decided
that what's interesting about the setup is that it &lt;em&gt;works&lt;/em&gt;, and that a cooler
visualization would be to show &lt;em&gt;all&lt;/em&gt; the cards, allow the player to select any
two, and have the app show the picture in common.&lt;/p&gt;
&lt;p&gt;&lt;img alt="puxit" src="https://joelgrus.com/images/puxit.gif" /&gt;&lt;/p&gt;
&lt;p&gt;This means a good first start would be to get some images. For the standard game
(8 images/card) we need 57 different images. After poking around
online, I found &lt;a href = "http://www.flaticon.com/packs/glypho"&gt;a nice CC-BY set of SVG icons&lt;/a&gt;,
which I then semi-laboriously (and mostly randomly) recolored in my text editor by choosing from
a &lt;a href = "http://www.december.com/html/spec/colorsvg.html"&gt;list of named SVG colors&lt;/a&gt;
and then adding &lt;code&gt;fill="purple"&lt;/code&gt; and so forth to each file. I also renamed the files to
&lt;code&gt;0.svg&lt;/code&gt;, &lt;code&gt;1.svg&lt;/code&gt;, and so on, to make it easy to generate URLs from the cards.&lt;/p&gt;
&lt;p&gt;Now, then, our usual FRP setup involves defining&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;State&lt;/code&gt; : the current state of the app&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Action&lt;/code&gt; : the possible actions the users can take&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Update&lt;/code&gt; : how the state should change in response to actions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;View&lt;/code&gt; : how to generate the HTML that corresponds to a state (and that can trigger actions)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We'll start with some type aliases to make our code more readable:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;-- An image is just represented as an integer.&lt;/span&gt;
&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Card&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;-- A card is just an array of images.&lt;/span&gt;
&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Deck&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Card&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;-- A deck is just an array of cards&lt;/span&gt;
&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CardIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;-- indexed by an integer.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like we said above, our mathy card generation library represents a card as an
array of ints, and a deck is just an array of cards. Given the way we named our
image files, we can also define:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;imageUrl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="nf"&gt;imageUrl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;images/&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.svg&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you wanted to use a different set of images, you could change this.&lt;/p&gt;
&lt;p&gt;Now, then, the state for the game needs to contain the deck of cards (obviously),
as well as some indication of which cards the player has selected:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Deck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="c1"&gt;-- The cards in the deck, in order.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;selected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;SelectedCards&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;-- Which cards (indexes) are selected&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are many possible ways to represent the selected cards, we'll use a simple
sum type to enforce that at most two cards can be selected.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;SelectedCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;NoCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;OneCard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CardIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TwoCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CardIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CardIndex&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There's only one possible action -- clicking on a card -- in which case we need
to know its index:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Click&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CardIndex&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The card generation library will generate the exact same cards each time. But we'd
like each game to be pseudo-unique. Accordingly, when we start a game, we'll
shuffle the cards, and we'll shuffle the images contained on each card. (Clearly
this doesn't affect the "every two cards have exactly one image in common" property.)&lt;/p&gt;
&lt;p&gt;This means we need a function to &lt;code&gt;shuffle&lt;/code&gt; an array. Because it requires randomness,
it needs to run in an effectful context:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;shuffle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;RANDOM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;shuffle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;randoms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replicateM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sortBy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compareFst&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;randoms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compareFst&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Given some array (of any type) &lt;code&gt;xs&lt;/code&gt;, we generate a random number for each element
and &lt;code&gt;zip&lt;/code&gt; them together to get an array of pairs &lt;code&gt;(rnd, x)&lt;/code&gt;. We then sort that array
using the &lt;code&gt;compareFst&lt;/code&gt; function, which only looks at the first element in each pair.
Finally, we call &lt;code&gt;map snd&lt;/code&gt; to throw away the random numbers. (This is not
  &lt;a href = "https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle"&gt;the most efficient way to shuffle&lt;/a&gt;, but it's simple and it works.)&lt;/p&gt;
&lt;p&gt;We can now write a function to create a randomized deck:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;createRandomDeck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;RANDOM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Deck&lt;/span&gt;
&lt;span class="nf"&gt;createRandomDeck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createDeck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;traverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;createDeck&lt;/code&gt; is the math-y function that returns the same deck always.
We call &lt;code&gt;shuffle&lt;/code&gt; on it (creating a shuffled deck in an effectful context),
and then "bind" that to &lt;code&gt;traverse shuffle&lt;/code&gt;. This is possibly the most complicated
part of this code. Let's look at &lt;code&gt;traverse&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Functor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Foldable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Traversable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;traverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Applicative&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;sequence&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Applicative&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So &lt;code&gt;traverse&lt;/code&gt; takes an effectful computation (&lt;code&gt;m&lt;/code&gt; is the effect), applies to
a &lt;code&gt;traversable&lt;/code&gt; container (&lt;code&gt;t&lt;/code&gt; is the container) and returns an effectful container
of (non-effectful) results. (Huh?) Here &lt;code&gt;t&lt;/code&gt; is Array and &lt;code&gt;m&lt;/code&gt; is &lt;code&gt;Eff (random :: RANDOM)&lt;/code&gt;,
so this specializes to&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;traverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;RANDOM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;RANDOM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;so that &lt;code&gt;traverse shuffle&lt;/code&gt; takes an array of "shufflables", shuffles each array
element individually, and returns the result in an effectful context. And now we're
ready to write our (effectful) function that generates an initial &lt;code&gt;State&lt;/code&gt; for the game:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;initialState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;RANDOM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
&lt;span class="nf"&gt;initialState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;createRandomDeck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;selected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;NoCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that we've gotten actions and state taken care of, it's time to think about
how to update the state in response to actions. Here's there's only a single
action, which makes it pretty easy:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;EffModel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;RANDOM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Click&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cardClicked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;effects&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only action is &lt;code&gt;Click i&lt;/code&gt;, and so we update the state using the &lt;code&gt;cardClicked i&lt;/code&gt;
function, which we haven't written yet:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;cardClicked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CardIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
&lt;span class="nf"&gt;cardClicked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;selected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;NoCards&lt;/span&gt;&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;OneCard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;-- select card i&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;OneCard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;NoCards&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;-- unselect&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;otherwise&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TwoCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;-- select second card&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;TwoCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;OneCard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- unselect s1&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;OneCard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- unselect s2&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;otherwise&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TwoCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;-- no op&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a lot of lines, but conceptually it shouldn't be too hard. When you click
on a card you're never changing the deck itself, only the &lt;code&gt;SelectedCards&lt;/code&gt; property.
If no cards are currently selected, we want to select the clicked card.
If one card is selected and we click that card, we want to unselect it; if we
click a different card we want to select the second card as well. And if two cards
are already selected, we either unselect one (if we clicked it), or do nothing
(if it's a different card).&lt;/p&gt;
&lt;p&gt;At last, we're ready to create the view. As usual, we have a lot of logic that
involves the "index" of an array element, so we'll need a helper function to
&lt;code&gt;map&lt;/code&gt; across elements and their indexes:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;mapWithIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="nf"&gt;mapWithIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uncurry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And since we'll want to add a special CSS class to the common image between
two selected cards, we'll also need a helper function to find it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;commonImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Deck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CardIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CardIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Image&lt;/span&gt;
&lt;span class="nf"&gt;commonImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;unsafeIndex&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;unsafeIndex&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image2&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is possibly a bad way to write it, since it will crash if the cards had no
common image, but if our math is correct that will never happen. Right?&lt;/p&gt;
&lt;p&gt;So then the view:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Action&lt;/span&gt;
&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cardsHtml&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cardsHtml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mapWithIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;renderCard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correctImage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;correctImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;TwoCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commonImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Nothing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Curiously, right when I finished the first version of this project, pux 1.0.0
dropped, with a large number of breaking changes, so I had to redo a lot of stuff.
In particular, there is a sugary hypertext DSL, but it doesn't cover every use case,
so I decided not to use it, since the code looked weird with a mix of DSL and not DSL.&lt;/p&gt;
&lt;p&gt;Here the &lt;code&gt;div&lt;/code&gt; function takes an array of attrbutes (here, empty) and an array
of child &lt;code&gt;Html&lt;/code&gt; elements, which are the outcome of the call to &lt;code&gt;mapWithIndex&lt;/code&gt;.
The function we pass to &lt;code&gt;mapWithIndex&lt;/code&gt; is &lt;code&gt;renderCard state.selected correctImage&lt;/code&gt;.
In order to figure out the correct image, we need to look at multiple cards,
so we have to do it at this stage (each call to &lt;code&gt;renderCard&lt;/code&gt; will only know about
  the card it's given). If 0 or 1 cards are selected, there is no &lt;code&gt;correctImage&lt;/code&gt;,
so it's an option type. We also have to pass down &lt;code&gt;state.selected&lt;/code&gt; so that we can
add a CSS class to the selected cards.&lt;/p&gt;
&lt;p&gt;This leads to a &lt;code&gt;renderCard&lt;/code&gt; that looks like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;renderCard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;SelectedCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Card&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CardIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Action&lt;/span&gt;
&lt;span class="nf"&gt;renderCard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;selectedCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correctImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cardClass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cardClick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cardHtml&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;isSelected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;selectedCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;NoCards&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;false&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;OneCard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;TwoCards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cardClass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isSelected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;card selected&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;card&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cardClick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Click&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cardHtml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;renderImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isSelected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correctImage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because we're passing it to &lt;code&gt;mapWithIndex&lt;/code&gt;, it's a function of both &lt;code&gt;Card&lt;/code&gt; and
&lt;code&gt;CardIndex&lt;/code&gt; (after we curry the &lt;code&gt;SelectedCards&lt;/code&gt; and the correct image). Again we
create a &lt;code&gt;div&lt;/code&gt; with an array of attributes (a class, and a click handler) and
an array of &lt;code&gt;Html&lt;/code&gt; children.&lt;/p&gt;
&lt;p&gt;Hopefully the class logic is straightforward. &lt;code&gt;cardClick&lt;/code&gt; should be a function
that takes a click event and returns an &lt;code&gt;Action&lt;/code&gt;, but here we don't actually need
any information from the click event, so we just use &lt;code&gt;const&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;cardHtml&lt;/code&gt; is just the result of mapping &lt;code&gt;renderImage&lt;/code&gt; over the card
(which is an array of images). It needs to know &lt;code&gt;isSelected&lt;/code&gt; and &lt;code&gt;correctImage&lt;/code&gt;,
because we want to add the flashing "correct" class only the the correct image
&lt;em&gt;on the selected cards&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;At long last we can write &lt;code&gt;renderImage&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;renderImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Action&lt;/span&gt;
&lt;span class="nf"&gt;renderImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isSelected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correctImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;altText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;imageClass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;imageUrl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;altText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;imageClass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isCorrectImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;image correct&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;image&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;isCorrectImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correctImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isSelected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correct&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag has no children,
the &lt;code&gt;img&lt;/code&gt; function only takes an array of attributes, which should be pretty straighforward.&lt;/p&gt;
&lt;p&gt;That's the whole application, now we just need to wire it all together:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initialState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;-- you could change this, if you have enough images&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;-- but it *must* be a prime number&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;initialState&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;renderToDOM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;#app&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only non-ordinary thing here is that because &lt;code&gt;initialState&lt;/code&gt; is an effectful
function, we need to &lt;code&gt;&amp;lt;-&lt;/code&gt; the state out of it.&lt;/p&gt;
&lt;p&gt;Anyway, that's about it. (There is also some CSS voodoo to make matching images
  pulse) and to draw borders around selected cards, but you're not interested
  in CSS voodoo, are you?)&lt;/p&gt;
&lt;p&gt;You can check it out at&lt;/p&gt;
&lt;p&gt;&lt;a href = "https://joelgrus.com/experiments/pux-it/"&gt;https://joelgrus.com/experiments/pux-it/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;although the bundle of all the code is almost 1MB. :(  Or fork it and make it your own. :)&lt;/p&gt;</content><category term="Mathematics, Code, Spot it, PureScript"></category></entry><entry><title>Trump Tweets on a Globe (aka Fun with d3, socket.io, and the Twitter API)</title><link href="https://joelgrus.com/2016/02/27/trump-tweets-on-a-globe-aka-fun-with-d3-socketio-and-the-twitter-api/" rel="alternate"></link><published>2016-02-27T09:00:00+01:00</published><updated>2016-02-27T09:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2016-02-27:/2016/02/27/trump-tweets-on-a-globe-aka-fun-with-d3-socketio-and-the-twitter-api/</id><summary type="html">&lt;p&gt;When I worked at Farecast we had a giant TV in the lobby,
and up on that TV was an image of a globe, and on that
globe were animated paths that (as far as you know) corresponded
to flight searches that people were doing on the site. It was …&lt;/p&gt;</summary><content type="html">&lt;p&gt;When I worked at Farecast we had a giant TV in the lobby,
and up on that TV was an image of a globe, and on that
globe were animated paths that (as far as you know) corresponded
to flight searches that people were doing on the site. It was a big hit
with visitors to the office, and I've always wanted to recreate it myself.&lt;/p&gt;
&lt;p&gt;These days I don't have access to flight search data, but I do have access
to (a subset of) the Twitter firehose, some subset of which is tweets tagged with
geographic data. Hence today's stupid project:&lt;/p&gt;
&lt;h1&gt;Trump Tweets on a Globe&lt;/h1&gt;

&lt;p&gt;&lt;img src = "/images/globe.gif"&gt;&lt;/p&gt;
&lt;p&gt;It streams tweets about Donald Trump (or whatever other topic you want),
keeps only the ones that contain geo information,
and then plots them on a globe.&lt;/p&gt;
&lt;p&gt;(all code on &lt;a href = "https://github.com/joelgrus/twitter-globe"&gt;GitHub&lt;/a&gt;,
 same as always)&lt;/p&gt;
&lt;p&gt;And if anyone from the Trump campaign wants to hire me to build stupid
junk like this for them, I'm available!&lt;/p&gt;
&lt;h2&gt;The Client&lt;/h2&gt;
&lt;p&gt;There is a saying (that I made up):&lt;/p&gt;
&lt;blockquote&gt;Good data scientists copy from the d3 gallery;
great data scientists steal from the d3 gallery.&lt;/blockquote&gt;

&lt;p&gt;Accordingly, most of the visualization guts are shamelessly cribbed from
Mike Bostock's &lt;a href = "http://bl.ocks.org/mbostock/4183330"&gt;World Tour&lt;/a&gt;
visualization. (Which infected my code with the GPL, which I guess is the point of the GPL.)&lt;/p&gt;
&lt;p&gt;His version has a list of countries that it simply rotates through in order,
while ours needs to react to Tweets as they arrive.&lt;/p&gt;
&lt;p&gt;My first attempt was to simply stick all of the Twitter logic in the client
and &lt;code&gt;browserify&lt;/code&gt; it together, but that didn't work for &lt;a href = "http://stackoverflow.com/questions/33266854/access-control-error-when-using-twitter-npm-module"&gt;cross-origin reasons&lt;/a&gt;, so I ended up having to stick the Twitter piece
on the server.&lt;/p&gt;
&lt;p&gt;Then, because it seemed like the sensible thing to do, I decided that the
server should maintain a single Twitter client and use
&lt;a href = "http://socket.io/"&gt;socket.io&lt;/a&gt; to send tweets to the browser.
(I have never really used socket.io before, so it's possible/likely I'm doing
 so in a really stupid way.)&lt;/p&gt;
&lt;p&gt;The relevant part of the original visualization is (comments added by me):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Start a 1.25 second transition.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1250&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Increment `i` and set the title to the relevant country name.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Rotate the globe to put that country in front.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tween&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rotate&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;centroid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;// Clear the canvas&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clearRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;// Fill the land gray&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#ccc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;land&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;// Fill the selected country red&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#f00&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;// Draw the country borders in white&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strokeStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#fff&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineWidth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;borders&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;// Circle the globe in black&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strokeStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#000&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineWidth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globe&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Start a new transition that does nothing and then recursively calls&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// transition() to move to the next country.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;end&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;How should we modify this? Well, first, we need to open a &lt;code&gt;socketio&lt;/code&gt; connection
to receive tweets.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;force new connection&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then instead of having our transitions triggered by an &lt;a href = "https://en.wikipedia.org/wiki/Immediately-invoked_function_expression"&gt;IIFE&lt;/a&gt;
that calls itself recursively, we invoke a transition whenever we receive a
&lt;code&gt;'tweet'&lt;/code&gt; message over the socket:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tweet&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1250&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;makeHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tween&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rotate&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latLong&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clearRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#ccc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;land&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strokeStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#fff&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineWidth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;borders&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strokeStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#000&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineWidth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globe&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strokeStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#000&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#f00&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineWidth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The major differences here are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;instead of using &lt;code&gt;title.text()&lt;/code&gt; to set the country name, we use &lt;code&gt;title.html()&lt;/code&gt;
  to create more complex HTML (see below)&lt;/li&gt;
&lt;li&gt;instead of getting the "front and center" coordinates from the array of countries,
  we get them from the &lt;code&gt;latLong&lt;/code&gt; field of the tweet message.&lt;/li&gt;
&lt;li&gt;we add an extra drawing to the canvas -- a filled-in circle at the &lt;code&gt;latLong&lt;/code&gt; coordinates,
  after we use &lt;code&gt;projection&lt;/code&gt; to convert them into the canvas coordinate system&lt;/li&gt;
&lt;li&gt;there's no recursive call at the end, since the transition will get invoked
  again every time there's socket message.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Otherwise it's the same! (I had to add some more CSS as well.)&lt;/p&gt;
&lt;p&gt;We still need to write the function that makes HTML:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;makeHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;div class=&amp;quot;user&amp;quot;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;a href=&amp;quot;https://twitter.com/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;quot; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;/a&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;h1&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;placeName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;/h1&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;div class=&amp;quot;tweet&amp;quot;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;a href=&amp;quot;https://twitter.com/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/status/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;quot; target=&amp;quot;_blank&amp;quot;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;/a&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;/div&amp;gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's really ugly (my kingdom for a templating engine!)
but all it's doing is showing the Twitter user name (with a link),
the place name of the specified location, and the text of the tweet (With a link).&lt;/p&gt;
&lt;h2&gt;The Server&lt;/h2&gt;
&lt;p&gt;OK, now we're ready to deal with the server. It will do a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Serve up our &lt;code&gt;index.html&lt;/code&gt; page.&lt;/li&gt;
&lt;li&gt;Subscribe to some topic on the Twitter streaming API.&lt;/li&gt;
&lt;li&gt;Massage the returned tweets and publish them to socketio.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As always, you'll need to get some Twitter credentials and set them up in a
&lt;code&gt;credentials.js&lt;/code&gt; that looks like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;consumer_key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;consumer_secret&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;access_token_key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;access_token_secret&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then our server is actually not that complicated. Let's start out with
all the express boilerplate:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;express&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Serve index.html at the root.&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/index.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Serve static files in the public directory.&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;public&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Run on port 3000.&lt;/span&gt;
&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;listening on 3000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, because we're working in Javascript, we've got async stuff everywhere.
In particular, we'll need to give our Twitter client a callback function that
triggers whenever a new tweet is received. In my first version that callback
function published to socket.io directly. For reasons I can't precisely explain to you,
that struck me as too tight of a coupling, so I introduced an intermediate
&lt;code&gt;TweetEmitter&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is all OO-boilerplate-cruft to make my own event emitter.&lt;/span&gt;
&lt;span class="c1"&gt;// https://nodejs.org/api/events.html&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;events&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;util&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TweetEmitter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inherits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TweetEmitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweetEmitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TweetEmitter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There's pretty much nothing going on here. The Twitter client will send its
tweets to the &lt;code&gt;TweetEmitter&lt;/code&gt;, and the socketio socket will grab tweets from the
&lt;code&gt;TweetEmitter&lt;/code&gt; and that way they don't have to know about each other, which felt
like the right design.&lt;/p&gt;
&lt;p&gt;At this point it's easy to hook the &lt;code&gt;TweetEmitter&lt;/code&gt; up to socket.io:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;socket.io&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;tweetEmitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tweet&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tweet&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally we need to set up the Twitter client.&lt;/p&gt;
&lt;p&gt;There is a slight complication here, in that there are several ways in which a
tweet might specify its geography. A tweet has a &lt;code&gt;geo&lt;/code&gt; field, which I believe
is deprecated. It has a &lt;code&gt;coordinates&lt;/code&gt; field, which is rarely populated. And it
has a &lt;code&gt;place&lt;/code&gt; field, which (is sometimes populated and if so)
contains a &lt;a href = "https://dev.twitter.com/overview/api/places"&gt;rich object&lt;/a&gt;.
For our purposes the most interesting properties of &lt;code&gt;place&lt;/code&gt; are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;full_name&lt;/code&gt; : we'd like to display it on our globe, and&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bounding_box.coordinates&lt;/code&gt; : an array of arrays of (longitude, latitude) pairs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(I assume it's an array of arrays because the "bounding box" might comprise
 multiple polygons? I'm not sure.)&lt;/p&gt;
&lt;p&gt;Anyway, we just want to put a dot at a single location,
which we'll accomplish by averaging all of the coordinate pairs:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// a helper function to average coordinate pairs&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;average&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latLongs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;latLongs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latLong&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;latLong&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;latLong&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(Yes, I am aware that I called them &lt;code&gt;latLongs&lt;/code&gt; even though the longitude is
 first. I always do that, it's a character flaw.)&lt;/p&gt;
&lt;p&gt;Next we setup Twitter and get our search term from the command line args if
appropriate (or just use &lt;code&gt;trump&lt;/code&gt; as the default):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;twitter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./credentials.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Twitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;trump&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally we start listening to the streaming API:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;statuses/filter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Every time we receive a tweet...&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// ... that has the `place` field populated ...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// ... extract only the fields needed by the client ...&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweetSmall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screen_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;placeName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;latLong&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;average&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bounding_box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// ... and notify the tweetEmitter.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;tweetEmitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tweet&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweetSmall&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that's all of it. If you've read this far, you can (maybe) see it up and running
at&lt;/p&gt;
&lt;p&gt;&lt;a href = "http://54.174.99.38:3000/"&gt;http://54.174.99.38:3000/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That's a tiny ec2 nano instance that's trying to socket.io
with everyone who goes to that URL, so it will probably crash eventually,
at which point you'll need to run it yourself locally.&lt;/p&gt;
&lt;p&gt;So that's it. For once no Haskell, no PureScript, not even any Python, just
clean old node and JavaScript.&lt;/p&gt;
&lt;h2&gt;Possible Improvements&lt;/h2&gt;
&lt;p&gt;As I mentioned above, this approach probably doesn't scale to very many users
before the backend just gets overwhelmed. So one obvious
improvement would be to scale it up. If I wanted to do that, my first attempt
would probably be to have one dedicated process publishing the tweets to a
(private) socket, and a bunch of &lt;code&gt;express&lt;/code&gt; processes behind a load balancer,
each of them pulling tweets from the (private) socket and publishing them to
their clients. Conceptually that shouldn't be too hard, but it seems like more
devops work and/or AWS bill than I want to devote to a toy project at this point.&lt;/p&gt;
&lt;p&gt;One person I showed it to wanted the tweets throttled, so that the globe updated
more slowly. Personally, I like it the way it is. One approach would be to throttle
things on the server side (only publish to the socket after a certain interval
has passed), another would be on the client side (throw away messages until
some interval has passed). In the second case you could even give the user a slider
to choose the update speed.&lt;/p&gt;
&lt;p&gt;Another person wanted a query box to select their own search terms. Unfortunately,
that doesn't fit into this model -- the Twitter client needs a separate
streaming connection to Twitter for each search term,
and it defeats the point of sharing the single
Twitter connection among all the connected clients.&lt;/p&gt;
&lt;p&gt;It also might be fun to leave the dots and tweets on the globe for a while
and have them slowly fade away. That seems like a lot more work. (Especially
since the tweets aren't technically "on the globe", they're in a fixed spot.)&lt;/p&gt;
&lt;p&gt;So anyway, check it out,
check out &lt;a href = "https://github.com/joelgrus/twitter-globe"&gt;the code&lt;/a&gt;,
roll your own version, and if anyone from the Trump campaign is reading this,
I wasn't kidding, call me!&lt;/p&gt;
&lt;p&gt;Got any other ideas to improve it? Let me know!&lt;/p&gt;</content><category term="Javascript, Hacking, d3, Twitter, Node.js"></category></entry><entry><title>Building a Stupid Data Product, Part 3: The Single-Page App (PureScript)</title><link href="https://joelgrus.com/2016/02/15/building-a-stupid-data-product-part-3-the-single-page-app-purescript/" rel="alternate"></link><published>2016-02-15T09:00:00+01:00</published><updated>2016-02-15T09:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2016-02-15:/2016/02/15/building-a-stupid-data-product-part-3-the-single-page-app-purescript/</id><summary type="html">&lt;p&gt;(&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-1-the-data-python/"&gt;part 1&lt;/a&gt;,
 &lt;a href = "/2016/02/15/building-a-stupid-data-product-part-2-the-web-service-haskell/"&gt;part 2&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;OK, so
&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-2-the-web-service-haskell/"&gt;last time&lt;/a&gt; we built a web service that responds to GET
requests with random (bogus) elementary school science questions. In this third
(and last) installment, we'll create a single-page quiz app that consumes the
service.&lt;/p&gt;
&lt;p&gt;Here's a rough stab at a wireframe:&lt;/p&gt;
&lt;p&gt;&lt;img alt="wireframe" src="https://joelgrus.com/images/wireframe.png" /&gt;&lt;/p&gt;
&lt;p&gt;On the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;(&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-1-the-data-python/"&gt;part 1&lt;/a&gt;,
 &lt;a href = "/2016/02/15/building-a-stupid-data-product-part-2-the-web-service-haskell/"&gt;part 2&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;OK, so
&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-2-the-web-service-haskell/"&gt;last time&lt;/a&gt; we built a web service that responds to GET
requests with random (bogus) elementary school science questions. In this third
(and last) installment, we'll create a single-page quiz app that consumes the
service.&lt;/p&gt;
&lt;p&gt;Here's a rough stab at a wireframe:&lt;/p&gt;
&lt;p&gt;&lt;img alt="wireframe" src="https://joelgrus.com/images/wireframe.png" /&gt;&lt;/p&gt;
&lt;p&gt;On the left will be the quiz questions. On the right will be the score, a
"new game" button, and self-aggrandizing stuff like links to my Twitter.
When a question appears, its answers will all be gray. Once you click on one
of the answers, it will get a bold border, and we'll color the correct answer
green and the incorrect ones red.&lt;/p&gt;
&lt;p&gt;NOW, you know me, I never met a fancy PureScript framework I didn't like.
In this case it's the brand new
&lt;a href = "http://www.alexmingoia.com/purescript-pux/"&gt;purescript-pux&lt;/a&gt;,
which is a &lt;a href = "https://en.wikipedia.org/wiki/Functional_reactive_programming"&gt;FRP&lt;/a&gt; interface to React. (I'm not that into React, but the framework
insulates you away from using it.)&lt;/p&gt;
&lt;p&gt;(Code, as always, is on &lt;a href = "https://github.com/joelgrus/science-questions"&gt;GitHub&lt;/a&gt;.)&lt;/p&gt;
&lt;h2&gt;Types&lt;/h2&gt;
&lt;p&gt;As is almost always the case when I work with Haskell or PureScript,
I start with the types:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;newtype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;questionText&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;correctAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;AnswerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;chosenAnswer&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;AnswerId&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;-- which answer the player clicked on&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;QuestionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Answer&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;AnswerId&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A few things here. The &lt;code&gt;Question&lt;/code&gt; type is basically a record that looks like the
JSON responses we'll get from the service. In addition,
since our wireframe called for different formatting (i.e. different css classes)
for selected / correct / wrong answers,
I added a &lt;code&gt;chosenAnswer&lt;/code&gt; field that's either &lt;code&gt;Nothing&lt;/code&gt;
(if the question hasn't been answered yet)
or &lt;code&gt;Just answerId&lt;/code&gt; (if the quizzee clicked on the &lt;code&gt;answerId&lt;/code&gt; answer).
And in order to make it an instance of the &lt;code&gt;IsForeign&lt;/code&gt; typeclass
(which makes it easy to convert the JSON responses from AJAX calls
 into typed objects) we need to wrap it in a &lt;code&gt;newtype&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I also made type aliases for &lt;code&gt;QuestionId&lt;/code&gt;, &lt;code&gt;Answer&lt;/code&gt;, and &lt;code&gt;AnswerId&lt;/code&gt;
just to make our code nicer and more descriptive.&lt;/p&gt;
&lt;p&gt;Anyway, like I said, we can now define an &lt;code&gt;IsForeign&lt;/code&gt; instance for &lt;code&gt;Question&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionIsForeign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IsForeign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;questionText&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;readProp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;questionText&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;readProp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;answers&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;correctAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;readProp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;correctAnswer&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;questionText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;correctAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correctAnswer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;chosenAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Nothing&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;read&lt;/code&gt; tells us how to turn the JSON object
&lt;code&gt;value&lt;/code&gt; into a &lt;code&gt;Question&lt;/code&gt;. The type of &lt;code&gt;read&lt;/code&gt; is&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Either&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;ForeignError&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can think of this (if you like) as a function that takes a &lt;code&gt;Foreign&lt;/code&gt;
object (i.e. some untyped JSON), and returns a &lt;code&gt;Question&lt;/code&gt; object
in a &lt;code&gt;Either ForeignError&lt;/code&gt; context. In other words, it describes a computation
that might return a &lt;code&gt;Question&lt;/code&gt;
but might fail with some kind of &lt;code&gt;ForeignError&lt;/code&gt;. In that context, the line&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;questionText&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;readProp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;questionText&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;tries to get the value of the &lt;code&gt;questionText&lt;/code&gt; field as a String. If it succeeds,
it goes on to the next line. If it fails, the whole computation fails with the
corresponding &lt;code&gt;ForeignError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If we successfully read these fields, we can create a &lt;code&gt;Question&lt;/code&gt; object
(with, in addition, its &lt;code&gt;chosenAnswer&lt;/code&gt; field set to &lt;code&gt;Nothing&lt;/code&gt;),
and then use &lt;code&gt;return&lt;/code&gt; to stick it in the &lt;code&gt;Either ForeignError&lt;/code&gt; context.&lt;/p&gt;
&lt;h2&gt;FRP&lt;/h2&gt;
&lt;p&gt;Similar to our previous stabs at FRP, our app will consist of&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;State&lt;/code&gt;, which keeps track of the state of the app&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Actions&lt;/code&gt;, which update the state (in a pure way) and kick off side effects&lt;/li&gt;
&lt;li&gt;&lt;code&gt;View&lt;/code&gt;, which turns the state into a &lt;code&gt;VirtualDOM&lt;/code&gt; that can be rendered by React&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;State&lt;/h2&gt;
&lt;p&gt;Let's deal with the state first:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;waitingForQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- are we waiting for an AJAX call to return?&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Our state consists simply of a score (the number of questions you've answered correctly),
an array of questions, and a flag that indicates whether the app is waiting for a question.
(The initial version didn't have this flag, which caused a bug where if you clicked
the "New Game" button a lot of times quickly, it would fire off a lot of AJAX requests
and then add all the questions when they eventually returned. We just want to add
one question at a time.)&lt;/p&gt;
&lt;h2&gt;Actions&lt;/h2&gt;
&lt;p&gt;Now we can deal with the actions, of which there are three:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;NewGame&lt;/span&gt;&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="c1"&gt;-- start a new game&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;ClickAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;QuestionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;AnswerId&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;-- click on an answer&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;QuestionReceived&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;-- receive an AJAX response with a question&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;NewGame&lt;/code&gt; is the action to take when someone clicks on the "New Game" button.
&lt;code&gt;ClickAnswer&lt;/code&gt; is the action to take when someone clicks on one of the answers.
Its payload contains the id of the question and the id of the answer, since we'll
need both of those to update the state. And &lt;code&gt;QuestionReceived&lt;/code&gt; is the action to
take when the app receives the result of an (asynchronous) call to the random
question service. Its payload is the received &lt;code&gt;Question&lt;/code&gt; (as a typed PureScript object).&lt;/p&gt;
&lt;p&gt;Next we'll write the code that updates the state in response to each of these
actions. For &lt;code&gt;NewGame&lt;/code&gt; we'll just replace the state with:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;initialState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
&lt;span class="nf"&gt;initialState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;waitingForQuestion&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For the &lt;code&gt;QuestionReceived&lt;/code&gt; action, we just append the question to
&lt;code&gt;state.questions&lt;/code&gt;. (That's what &lt;code&gt;snoc&lt;/code&gt; does. It's &lt;code&gt;cons&lt;/code&gt; backward.)
The only subtlety involves the &lt;code&gt;waitingForQuestion&lt;/code&gt; flag;
if it's &lt;code&gt;false&lt;/code&gt; then we &lt;em&gt;don't&lt;/em&gt; add the question;
if it's &lt;code&gt;true&lt;/code&gt; then we set it to &lt;code&gt;false&lt;/code&gt;. The flag ensures that we add
at most one question per "intra-game question request".&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;appendQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
&lt;span class="nf"&gt;appendQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waitingForQuestion&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;waitingForQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And we need a function that updates the state after a &lt;code&gt;ClickAnswer&lt;/code&gt; action.
It needs to know the &lt;code&gt;QuestionId&lt;/code&gt; and &lt;code&gt;AnswerId&lt;/code&gt; that were clicked on.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;answerClicked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;QuestionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;AnswerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
&lt;span class="nf"&gt;answerClicked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newScore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newQuestions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;waitingForQuestion&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;unsafeIndex&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;newScore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;correctAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;answeredQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chosenAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;newQuestions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;fromJust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;updateAt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answeredQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one is a little more complicated. First we use &lt;code&gt;unsafeIndex&lt;/code&gt;
to pull the clicked &lt;code&gt;Question&lt;/code&gt; out of &lt;code&gt;state.questions&lt;/code&gt;, along with
pattern-matching
to pull the question record out of the &lt;code&gt;newtype&lt;/code&gt;. We compute the new score by
adding 1 to the current if the clicked &lt;code&gt;answerId&lt;/code&gt; was the &lt;code&gt;correctAnswer&lt;/code&gt;.
Then we update the question at &lt;code&gt;questionId&lt;/code&gt; by setting its
&lt;code&gt;chosenAnswer&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;So, at this point we have &lt;code&gt;Action&lt;/code&gt;s
and we have functions that update the state in response
to each action type. Now we need to glue them together:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;-- How to update the state (and perform effects) for each action type.&lt;/span&gt;
&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ajax&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;AJAX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                              &lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;EXCEPTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                              &lt;/span&gt;&lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CONSOLE&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Action&lt;/span&gt;
&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;NewGame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initialState&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;effects&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requestQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;ClickAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerClicked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;effects&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requestQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;QuestionReceived&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;appendQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;effects&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;Update&lt;/code&gt; function takes in an &lt;code&gt;Action&lt;/code&gt;, the state, and &lt;code&gt;input&lt;/code&gt;
(which is part of the plumbing for asynchronous state changes),
and returns a record with the new state and an array of side-effects.&lt;/p&gt;
&lt;p&gt;Since we have three &lt;code&gt;Action&lt;/code&gt; types, we use pattern matching to handle the
three different cases. For &lt;code&gt;NewGame&lt;/code&gt;,
we reset the initial state and make an AJAX request for a new question.
For &lt;code&gt;ClickAnswer&lt;/code&gt; we call our &lt;code&gt;answerClicked&lt;/code&gt; function to get a new state
and also make an AJAX request for a new question. And for &lt;code&gt;QuestionReceived&lt;/code&gt;
we use our &lt;code&gt;appendQuestion&lt;/code&gt; function to update the state.&lt;/p&gt;
&lt;p&gt;You're probably wondering what &lt;code&gt;requestQuestion&lt;/code&gt; is. It's pretty much boilerplate
around making an AJAX call, and to be honest I don't really understand it well
(I mostly copied it from
&lt;a href="http://www.alexmingoia.com/purescript-pux/docs/examples/ajax.html"&gt;the documentation&lt;/a&gt;).&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;requestQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;launchAff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionServiceUrl&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kr"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;readJSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;liftEff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Error parsing JSON!&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;singleton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;QuestionReceived&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;launchAff&lt;/code&gt; takes a value in an "asynchronous computation effect context",
runs it synchronously, and ignores the value. (This is fine, since we don't
need the result of the computation, we just want it to emit a signal
when it finishes (or log an error if it fails)).&lt;/p&gt;
&lt;p&gt;The asynchronous computation makes an GET request to the questionServiceUrl,
uses &lt;code&gt;readJSON&lt;/code&gt; to convert the response into a &lt;code&gt;Question&lt;/code&gt; object
(in the &lt;code&gt;F&lt;/code&gt; context, which is a type synonym for &lt;code&gt;Either ForeignError&lt;/code&gt;)
and then either logs the error (if the conversion fails)
or sends off a &lt;code&gt;QuestionReceived&lt;/code&gt; action (if the conversion succeeds).&lt;/p&gt;
&lt;p&gt;(Because both the &lt;code&gt;log&lt;/code&gt; and &lt;code&gt;S.send&lt;/code&gt; effects operate in the
 synchronous effect context &lt;code&gt;Eff&lt;/code&gt;, we have to "lift" them into the
 asynchronous effect context of &lt;code&gt;requestQuestion&lt;/code&gt;.)&lt;/p&gt;
&lt;h2&gt;View&lt;/h2&gt;
&lt;p&gt;Now it's time to produce the view. At a high level our goal is&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;after which React will take care of rendering the &lt;code&gt;VirtualDOM&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;VirtualDOM&lt;/code&gt; has a hyperscript DSL that allows you to write stuff like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;-- This is an illustrative example, not part of our code:&lt;/span&gt;
&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;
&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Counter: &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Increment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Increment&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Decrement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Decrement&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The place where I got stuck was on how to create arbitrarily many elements
by &lt;code&gt;map&lt;/code&gt;-ing over an array. (None of the examples do that.)
Eventually I noticed that &lt;code&gt;VirtualDOM&lt;/code&gt; has a
&lt;code&gt;Monoid&lt;/code&gt; instance, which means we can use&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;foldMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Monoid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;with the types in this particular case specialized as&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;foldMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which means we could do stuff like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;-- This is an illustrative example, not part of our code:&lt;/span&gt;
&lt;span class="nf"&gt;showTenUsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;
&lt;span class="nf"&gt;showTenUsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;foldMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now because we're using array indexes as &lt;code&gt;QuestionId&lt;/code&gt; and &lt;code&gt;AnswerId&lt;/code&gt;, we really
want a variant that allows the generated &lt;code&gt;VirtualDOM&lt;/code&gt; elements to also depend
on the index, which I'll call &lt;code&gt;foldMapWithIndex&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;foldMapWithIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;
&lt;span class="nf"&gt;foldMapWithIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;foldMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uncurry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As desired, it takes a function that generates a &lt;code&gt;VirtualDOM&lt;/code&gt; from an element
and its index, &lt;code&gt;uncurry&lt;/code&gt;s it (i.e. converts it from a function of two arguments
to a function of one &lt;code&gt;Tuple&lt;/code&gt; argument), and &lt;code&gt;foldMap&lt;/code&gt;s it over the pairs
&lt;code&gt;(element, index)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now we're ready to write our view. We'll start at the highest level and work our
way down:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;
&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sidebar&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;score&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Score: &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;NewGame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;New Game&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;twitter&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://twitter.com/joelgrus&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;@joelgrus&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;github&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://github.com/joelgrus/science-questions&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;octocat.png&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;foldMapWithIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;renderQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;OK. So our view produces a &lt;code&gt;div&lt;/code&gt; with two &lt;code&gt;VirtualDOM&lt;/code&gt; children.&lt;/p&gt;
&lt;p&gt;The first child is a "sidebar". We'll use css to float it off to the right.
It contains a &lt;code&gt;p&lt;/code&gt; showing the current score,
a "New Game" button,
a link to my Twitter account,
and a link to the GitHub repo for this project.&lt;/p&gt;
&lt;p&gt;The only two interesting parts are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the "score", which looks into the &lt;code&gt;state&lt;/code&gt; to find the score, and&lt;/li&gt;
&lt;li&gt;the "new game" button, which uses &lt;code&gt;send&lt;/code&gt; to trigger a &lt;code&gt;NewGame&lt;/code&gt; action&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The second child is the output of a call to &lt;code&gt;foldMapWithIndex&lt;/code&gt;, which
(as we described above) feeds each element of &lt;code&gt;state.questions&lt;/code&gt;
and its index to the &lt;code&gt;renderQuestion&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;This tells us that we must have&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;renderQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or, since &lt;code&gt;QuestionId&lt;/code&gt; is a type alias for &lt;code&gt;Int&lt;/code&gt;, we can use the more descriptive&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;renderQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;QuestionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;
&lt;span class="nf"&gt;renderQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;question&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionNumber&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;. &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;questionText&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;foldMapWithIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;renderAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;answers&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;questionNumber&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we render a question as a &lt;code&gt;p&lt;/code&gt; with the question number and question text,
followed by another &lt;code&gt;foldMapWithIndex&lt;/code&gt; across the question's &lt;code&gt;answers&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now we need to write &lt;code&gt;renderAnswer&lt;/code&gt;. Notice our usage: we passed it a
&lt;code&gt;Question&lt;/code&gt; and a &lt;code&gt;QuestionId&lt;/code&gt;, after which (since we're using
&lt;code&gt;foldMapWithIndex&lt;/code&gt;) we need to end up with a function
that takes an &lt;code&gt;Answer&lt;/code&gt; and an &lt;code&gt;AnswerId&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;renderAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;QuestionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Answer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;AnswerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;VirtualDOM&lt;/span&gt;
&lt;span class="nf"&gt;renderAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clickHandlerIfUnanswered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;isAnswered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isJust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chosenAnswer&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;isChosen&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isAnswered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fromJust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chosenAnswer&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;isCorrect&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;correctAnswer&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;joinWith&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fst&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;answer&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isChosen&lt;/span&gt;&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;chosen&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isAnswered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isCorrect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;correct&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isAnswered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isCorrect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;wrong&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;clickHandlerIfUnanswered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isAnswered&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kr"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Attrs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- no-op &amp;quot;attribute&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;ClickAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;How on earth is rendering a measly little answer so involved? Well, two reasons.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Our original wireframe involved a lot of different state-dependent styles for
   answers, which means that we need to assign state-dependent classes in a
   slightly complicated way.&lt;/li&gt;
&lt;li&gt;The main activity of the game involves clicking on answers, which means that
   we need to set up click handlers.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The hyperscript part is quite simple:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clickHandlerIfUnanswered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's a &lt;code&gt;div&lt;/code&gt;, with some classes attached to it,
with something else that might be a click handler attached to it,
and containing the text of the answer. Simple.&lt;/p&gt;
&lt;p&gt;The classes are determined by three boolean values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;isAnswered&lt;/code&gt; -- has this question been answered (at all)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isChosen&lt;/code&gt;   -- did the quizzee click &lt;em&gt;this&lt;/em&gt; answer?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isCorrect&lt;/code&gt;  -- is this the correct answer?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The code for generating the classes is possibly too clever
(although all my alternative versions with lots of &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;then&lt;/code&gt;
were really ugly), but basically it sets up an array of pairs
(boolean value, class name), throws out the pairs where the first element is false,
and joins together the class names that are left.&lt;/p&gt;
&lt;p&gt;For the click handler, we only want answers clickable if they belong to a question
that hasn't been answered yet. The simplest way I could come up with to do that
was the &lt;code&gt;if&lt;/code&gt;-&lt;code&gt;then&lt;/code&gt; statement that produced either a click handler or an "empty attribute"
depending on the value of &lt;code&gt;isAnswered&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I think that's pretty much it, other than the &lt;code&gt;main&lt;/code&gt; boilerplate:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;renderToDOM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;#app&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initialState&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;The Result&lt;/h2&gt;
&lt;p&gt;You can see it up and running
&lt;a href = "https://joelgrus.com/experiments/science-questions/"&gt;here&lt;/a&gt;.
(Fear my mad CSS / design skills.)&lt;/p&gt;
&lt;p&gt;The front-end should be totally robust, but the back-end question service is running
on an EC2 nano instance, so try to be gentle. If it dies for some reason, this
is what it looks like (or run it yourself locally!):&lt;/p&gt;
&lt;p&gt;&lt;img alt="science quiz" src="https://joelgrus.com/images/science_quiz.png" /&gt;&lt;/p&gt;
&lt;h2&gt;The Verdict&lt;/h2&gt;
&lt;p&gt;I really enjoyed working with purescript-pux,
it's probably my favorite of the PureScript frameworks I've tried.
It's pretty much brand new (I think it was announced a couple of weeks ago),
so there's not a ton of help, but the docs are pretty good and
I managed to figure everything out mostly. Why do I like it?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;It has pretty much the same model as &lt;a href = "http://cycle.js.org/"&gt;cycle.js&lt;/a&gt;,
   which I've been playing with a lot recently, and "feels like"
   the right way to write apps.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From my (newbie) perspective, it involves a good bit less mystery than (e.g.)
 &lt;a href = "https://github.com/paf31/purescript-thermite"&gt;purescript-thermite&lt;/a&gt;
 or &lt;a href = "https://github.com/slamdata/purescript-halogen"&gt;purescript-halogen&lt;/a&gt;,
 neither of which I really felt like I was understanding as I used them.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So check it out, and thanks to &lt;a href = "http://twitter.com/alexmingoia"&gt;@alexmingoia&lt;/a&gt;
for creating it.&lt;/p&gt;
&lt;p&gt;Anyway, that's the end of our end-to-end stupid data product.
Possibly you learned something, and possibly you'll go off and
build your own stupid data product (or maybe even a not-stupid one).
If you do, let me know about it!&lt;/p&gt;</content><category term="PureScript, Hacking, Data, Data Science"></category></entry><entry><title>Building a Stupid Data Product, Part 2: The Web Service (Haskell)</title><link href="https://joelgrus.com/2016/02/15/building-a-stupid-data-product-part-2-the-web-service-haskell/" rel="alternate"></link><published>2016-02-15T08:00:00+01:00</published><updated>2016-02-15T08:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2016-02-15:/2016/02/15/building-a-stupid-data-product-part-2-the-web-service-haskell/</id><summary type="html">&lt;p&gt;(&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-1-the-data-python/"&gt;part 1&lt;/a&gt;,
 &lt;a href = "/2016/02/15/building-a-stupid-data-product-part-3-the-single-page-app-purescript/"&gt;part 3&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Last time we
&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-1-the-data-python/"&gt;collected and processed the data&lt;/a&gt;
for generating stupid fake elementary school science questions and answers. The
important parts to remember are&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;we generated two files &lt;code&gt;questions.json&lt;/code&gt; and &lt;code&gt;answers.json&lt;/code&gt;
   containing &lt;em&gt;transition dictionaries&lt;/em&gt; mapping each word to an array / list of
   possible following …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">&lt;p&gt;(&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-1-the-data-python/"&gt;part 1&lt;/a&gt;,
 &lt;a href = "/2016/02/15/building-a-stupid-data-product-part-3-the-single-page-app-purescript/"&gt;part 3&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Last time we
&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-1-the-data-python/"&gt;collected and processed the data&lt;/a&gt;
for generating stupid fake elementary school science questions and answers. The
important parts to remember are&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;we generated two files &lt;code&gt;questions.json&lt;/code&gt; and &lt;code&gt;answers.json&lt;/code&gt;
   containing &lt;em&gt;transition dictionaries&lt;/em&gt; mapping each word to an array / list of
   possible following words, and&lt;/li&gt;
&lt;li&gt;we used &lt;em&gt;sentinel&lt;/em&gt; tokens &lt;code&gt;__START__&lt;/code&gt; and &lt;code&gt;__STOP__&lt;/code&gt; to indicate the beginning
   and end of sentences.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this post we'll use a Haskell library called
&lt;a href = "https://haskell-servant.github.io/"&gt;servant&lt;/a&gt;
to build a web service that generates and returns random questions.
If you know Haskell, I'm sure you can find fault with the way I did things,
but if you don't know Haskell you might find it educational and/or mind-expanding.&lt;/p&gt;
&lt;p&gt;(Code, as always, is on &lt;a href = "https://github.com/joelgrus/science-questions"&gt;GitHub&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;We'll do this in four steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;define some types&lt;/li&gt;
&lt;li&gt;write code that can generate a random question, given an (abstract) &lt;code&gt;GetNextToken&lt;/code&gt;
   function&lt;/li&gt;
&lt;li&gt;implement a (concrete) &lt;code&gt;GetNextToken&lt;/code&gt; based on our transitions&lt;/li&gt;
&lt;li&gt;create an API that serves up the random questions&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The Types&lt;/h2&gt;
&lt;p&gt;Let's start with the types. We need to define the &lt;code&gt;Question&lt;/code&gt; that our web service
will return. In our API, a question will have a &lt;code&gt;questionText&lt;/code&gt;, a list/array of
&lt;code&gt;answers&lt;/code&gt; (which are just strings), and an integer indicating the index of the
&lt;code&gt;correctAnswer&lt;/code&gt;. Pretty simple:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionText&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Answer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correctAnswer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;deriving&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Answer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;

&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deriveJSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;defaultOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;&amp;#39;&amp;#39;Question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The last line is (I believe) some template Haskell voodoo
that makes it so our service knows how to serialize a &lt;code&gt;Question&lt;/code&gt; to
JSON (since we can't send Haskell objects over the wire). I don't understand it,
I just copied it from the docs.&lt;/p&gt;
&lt;p&gt;Now we need to define a type for our tokens. One of the benefits of working in
a nicely-typed language is that we don't have to use "sentinel values", we can
use our type system for that:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;deriving&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Ord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So a token is either &lt;code&gt;Start&lt;/code&gt;, &lt;code&gt;Stop&lt;/code&gt;, or a &lt;code&gt;Word&lt;/code&gt; with an associated &lt;code&gt;String&lt;/code&gt;
value. The &lt;code&gt;deriving (Eq, Ord)&lt;/code&gt; just makes it so that we can test two tokens
for equality and inequalities.&lt;/p&gt;
&lt;p&gt;Since our tokens will come from deserializing JSON, we'll also need a &lt;code&gt;Read&lt;/code&gt;
instance, which indicates how to parse text into &lt;code&gt;Token&lt;/code&gt; objects:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;readsPrec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;__START__&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;readsPrec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;__STOP__&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;readsPrec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;Word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Don't get hung up on the details, it does exactly what you'd expect it to do.
(If you do get hung up on the details, read &lt;a href = "http://hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#t:Read"&gt;the docs&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;We also want to define a type alias&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;GetNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;that represents a function that takes a &lt;code&gt;Token&lt;/code&gt; and returns an &lt;code&gt;IO Token&lt;/code&gt;.
If you are not a Haskell person, you are at this point wondering&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Why does it not just return a &lt;code&gt;Token&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What the hell is an &lt;code&gt;IO Token&lt;/code&gt;?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For the first, Haskell is a &lt;em&gt;pure&lt;/em&gt; functional language. This means that if you
tried&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;GetNextTokenBad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;any instance of &lt;code&gt;GetNextTokenBad&lt;/code&gt; would have to always return the same value
for the same input. In particular, it wouldn't be able to choose the next token
randomly. If we want side-effects like randomness
(or printing things, or reading from files),
we need to do computations in the &lt;code&gt;IO&lt;/code&gt; context. So when you see&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;GetNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;you can understand that as a function that takes a token, does something side-effectful,
and returns a new token in the &lt;code&gt;IO&lt;/code&gt; context. In particular, this function doesn't
need to return the same value for the same inputs, but also you can only &lt;em&gt;use&lt;/em&gt; it
in a context that allows side effects. More on that in a bit.&lt;/p&gt;
&lt;h2&gt;Generating Random Questions&lt;/h2&gt;
&lt;p&gt;Now we're ready to write the code for generating a sentence. This is where things
start to get a little complicated. We'll break it into two parts. First, given
a starting &lt;code&gt;Token&lt;/code&gt; and a &lt;code&gt;GetNextToken&lt;/code&gt; function, we want to generate a list of
&lt;code&gt;Token&lt;/code&gt;s in the &lt;code&gt;IO&lt;/code&gt; context:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;tokensFrom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;GetNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;tokensFrom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;startToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getNext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;nextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getNext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;startToken&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;-- nextToken :: Token&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;liftA2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokensFrom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getNext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This shouldn't be hard &lt;em&gt;conceptually&lt;/em&gt;, it's just recursion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tokensFrom&lt;/code&gt; takes a start &lt;code&gt;Token&lt;/code&gt; and a &lt;code&gt;GetNextToken&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;it calls the &lt;code&gt;GetNextToken&lt;/code&gt; function on the starting &lt;code&gt;Token&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;nextToken&lt;/code&gt; is &lt;code&gt;Stop&lt;/code&gt;, the result is an empty list;&lt;/li&gt;
&lt;li&gt;otherwise, the result is the list whose first element is &lt;code&gt;nextToken&lt;/code&gt;,
  and whose subsequent elements are the results of &lt;code&gt;tokensFrom nextToken&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In &lt;em&gt;reality&lt;/em&gt;, it's complicated because of the need to do things in an effectful
context. The &lt;code&gt;do&lt;/code&gt; is
&lt;a href = "https://en.wikibooks.org/wiki/Haskell/do_notation"&gt;sugar&lt;/a&gt; for
working in the &lt;code&gt;IO&lt;/code&gt; context. In particular, it allows us to pull the &lt;code&gt;Token&lt;/code&gt;
value out of the result of a &lt;code&gt;GetNextToken&lt;/code&gt; call. That is, while &lt;code&gt;getNext&lt;/code&gt; returns
an &lt;code&gt;IO Token&lt;/code&gt;, as long as we're inside the &lt;code&gt;do&lt;/code&gt; block for an &lt;code&gt;IO&lt;/code&gt; context, we can
use &lt;code&gt;&amp;lt;-&lt;/code&gt; to "get the &lt;code&gt;Token&lt;/code&gt; out."&lt;/p&gt;
&lt;p&gt;If we find &lt;code&gt;Stop&lt;/code&gt;, the result is &lt;code&gt;return []&lt;/code&gt;. Notably, this is not the &lt;code&gt;return&lt;/code&gt;
you might know from other languages. Here this is&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which sticks a value (in this case the empty list) into an IO context. So, since
&lt;code&gt;[]&lt;/code&gt; is a &lt;code&gt;[Token]&lt;/code&gt;, &lt;code&gt;return []&lt;/code&gt; is an &lt;code&gt;IO [Token]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The last line is even uglier. &lt;code&gt;(:)&lt;/code&gt; is the "cons" operator that takes a head and
a tail and produces a list:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;nextToken&lt;/code&gt; is a &lt;code&gt;Token&lt;/code&gt;, but the recursive call to &lt;code&gt;tokensFrom&lt;/code&gt; produces
an &lt;code&gt;IO [Token]&lt;/code&gt;, so the types don't match up. We've already seen that we can
shove values into an &lt;code&gt;IO&lt;/code&gt; context, so we could get by if we had something like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;-- | not a real operator&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;:???&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can get there with &lt;code&gt;liftA2&lt;/code&gt;, which (specialized for &lt;code&gt;IO&lt;/code&gt;) looks like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;liftA2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That is, it "lifts" a function of two arguments into an &lt;code&gt;IO&lt;/code&gt; context. If you
work through the types, you get:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;liftA2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which is exactly what we want.&lt;/p&gt;
&lt;p&gt;[Why did I use &lt;code&gt;pure&lt;/code&gt; instead of &lt;code&gt;return&lt;/code&gt; to stick
&lt;code&gt;nextToken&lt;/code&gt; into the &lt;code&gt;IO&lt;/code&gt; context? I'm not sure, exactly. In this case they're
the same thing. In the previous instance I was using &lt;code&gt;IO&lt;/code&gt; as a Monad, so I used
&lt;code&gt;return&lt;/code&gt;; here I'm using it as an Applicative, so I used &lt;code&gt;pure&lt;/code&gt;. That's not a
good explanation, and it's probably not even a good reason. I don't care.
(I was also trying not to say "monad" in this post, but I guess I failed.)]&lt;/p&gt;
&lt;p&gt;Next we want to turn a list of &lt;code&gt;Token&lt;/code&gt;s into a &lt;code&gt;String&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;smartJoin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="nf"&gt;smartJoin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dropWhile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;addSeparators&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;addSeparators&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;concatMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;addSeparator&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;addSeparator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;Word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;?&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;Word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="w"&gt;                                 &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first thing we do is &lt;code&gt;addSeparators&lt;/code&gt;, which turns each &lt;code&gt;Word&lt;/code&gt; into a list
&lt;code&gt;[separator, word]&lt;/code&gt; and then concatenates the resulting lists.
If the &lt;code&gt;Word&lt;/code&gt; is punctuation, the separator is an empty string.
Otherwise it's a space.&lt;/p&gt;
&lt;p&gt;(We should never call &lt;code&gt;smartJoin&lt;/code&gt; on a list that includes the &lt;code&gt;Start&lt;/code&gt; or &lt;code&gt;Stop&lt;/code&gt;
 tokens, but just in case we add in an empty list, which is the same as ignoring
 the token.)&lt;/p&gt;
&lt;p&gt;So, for instance, if you were to call&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;addSeparators&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;What&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;is&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;love&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;?&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;you would get&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;What&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;is&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;love&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;?&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We then call &lt;code&gt;concat&lt;/code&gt; on that to concatenate all the strings&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="s"&gt;&amp;quot; What is love?&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and &lt;code&gt;dropWhile (== ' ')&lt;/code&gt; to get rid of the leading spaces. (I know, sort of clunky.)&lt;/p&gt;
&lt;p&gt;Now we're ready to implement our sentence generator:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;GetNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fmap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;smartJoin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokensFrom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To be a jerk, I wrote it in point-free style, it's the same as if I'd done&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fmap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;smartJoin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokensFrom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nextToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;tokensFrom&lt;/code&gt; generates &lt;code&gt;IO [Token]&lt;/code&gt; (an list of tokens in an effectful context)
and &lt;code&gt;fmap&lt;/code&gt; lifts &lt;code&gt;smartJoin&lt;/code&gt; (which maps &lt;code&gt;[Token] -&amp;gt; String&lt;/code&gt;) into the &lt;code&gt;IO&lt;/code&gt;
context, resulting in our desired &lt;code&gt;IO String&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And finally we can create our &lt;code&gt;Question&lt;/code&gt; generator:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;randomQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;GetNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;GetNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;
&lt;span class="nf"&gt;randomQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numAnswers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getNextQuestionToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getNextAnswerToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;$&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getNextQuestionToken&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;*&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replicateM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numAnswers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getNextAnswerToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;*&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;randomRIO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numAnswers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It takes an &lt;code&gt;Int&lt;/code&gt; indicating how many answers the question should have.
And it needs two &lt;code&gt;GetNextToken&lt;/code&gt; functions, one for generating &lt;code&gt;questionText&lt;/code&gt;
and the other for generating &lt;code&gt;Answer&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;You can think of &lt;code&gt;&amp;lt;$&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;*&amp;gt;&lt;/code&gt; as
plumbing to lift the &lt;code&gt;Question&lt;/code&gt; constructor into the
&lt;code&gt;IO&lt;/code&gt; context. That's a Haskell-y way of doing (in essence)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;-- the constructor is in essence&lt;/span&gt;
&lt;span class="c1"&gt;-- Question :: String -&amp;gt; [Answer] -&amp;gt; Int -&amp;gt; Question&lt;/span&gt;

&lt;span class="nf"&gt;makeEffectfulQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Answer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;
&lt;span class="nf"&gt;makeEffectfulQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;liftA3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here the &lt;code&gt;IO String&lt;/code&gt; comes from &lt;code&gt;generate&lt;/code&gt;-ing the question,
the &lt;code&gt;IO [Answer]&lt;/code&gt; comes from using &lt;code&gt;replicateM&lt;/code&gt; to &lt;code&gt;generate&lt;/code&gt; multiple answers,
and the &lt;code&gt;IO Int&lt;/code&gt; comes from choosing a random "correct answer".&lt;/p&gt;
&lt;h2&gt;Using Transitions&lt;/h2&gt;
&lt;p&gt;Now that we have a way to generate &lt;code&gt;Question&lt;/code&gt;s using &lt;code&gt;GetNextToken&lt;/code&gt; functions,
we have to figure out how create &lt;code&gt;GetNextToken&lt;/code&gt; functions from the
transition maps we generated last time. We serialized them as JSON,
but now we want a typed way to work with them in Haskell:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;M&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;Transitions&lt;/code&gt; is a &lt;code&gt;Map&lt;/code&gt; (like a dictionary)
whose keys are &lt;code&gt;Token&lt;/code&gt;s and whose values are lists of &lt;code&gt;Token&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;However, our &lt;em&gt;serialized&lt;/em&gt; map of transitions is a dictionary whose keys are
&lt;em&gt;strings&lt;/em&gt; and whose values are &lt;em&gt;lists of strings&lt;/em&gt;. That means we need to
deserialize it and then convert the strings to &lt;code&gt;Token&lt;/code&gt;s:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;loadTransitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;
&lt;span class="nf"&gt;loadTransitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fmap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textToTokens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fromJust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readFile&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;textToTokens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;M&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;M&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapKeys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Our &lt;code&gt;loadTransitions&lt;/code&gt; is another point-free function. It reads a file
(which gets us some bytes in an &lt;code&gt;IO&lt;/code&gt; context), and then uses &lt;code&gt;fmap&lt;/code&gt; to lift the three
composed functions into the &lt;code&gt;IO&lt;/code&gt; context.&lt;/p&gt;
&lt;p&gt;First, &lt;code&gt;decode&lt;/code&gt; &lt;code&gt;Maybe&lt;/code&gt;-deserializes the bytes into a map (with text keys and values).
After that, &lt;code&gt;fromJust&lt;/code&gt; assumes the deserialization succeeded and pulls the map out of the &lt;code&gt;Maybe&lt;/code&gt;.
Finally, &lt;code&gt;textToTokens&lt;/code&gt; converts the text-texts map into a &lt;code&gt;Token&lt;/code&gt;-&lt;code&gt;Token&lt;/code&gt;s map.&lt;/p&gt;
&lt;p&gt;(The &lt;code&gt;fromJust&lt;/code&gt; isn't a "safe" way to do things (usually we'd want to check that
 &lt;code&gt;decode&lt;/code&gt; doesn't return &lt;code&gt;Nothing&lt;/code&gt; and deal with that somehow),
but because we generated the JSON ourselves, we know it's valid.)&lt;/p&gt;
&lt;p&gt;How does &lt;code&gt;textToTokens&lt;/code&gt; work? First, it calls &lt;code&gt;M.mapKeys read&lt;/code&gt;, which returns the
new &lt;code&gt;Map&lt;/code&gt; that results from applying &lt;code&gt;read&lt;/code&gt; to each of the input &lt;code&gt;Map&lt;/code&gt;'s keys.
So it returns a map whose keys are &lt;code&gt;Token&lt;/code&gt;s but whose values are still lists of text.
And then we feed it into &lt;code&gt;M.Map (map read)&lt;/code&gt;, which returns the &lt;code&gt;Map&lt;/code&gt; that results
from calling &lt;code&gt;map read&lt;/code&gt; on each of the input &lt;code&gt;Map&lt;/code&gt;'s values. Those values are
lists of text, so &lt;code&gt;map read&lt;/code&gt; converts each one to a list of &lt;code&gt;Token&lt;/code&gt;s.
At the end of the process we have a &lt;code&gt;M.Map Token [Token]&lt;/code&gt; as required.&lt;/p&gt;
&lt;p&gt;Now we're ready to actually load the data:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;questionTransitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;
&lt;span class="nf"&gt;questionTransitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loadTransitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;questions.json&amp;quot;&lt;/span&gt;

&lt;span class="nf"&gt;answerTransitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;
&lt;span class="nf"&gt;answerTransitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loadTransitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;answers.json&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, remember that the abstraction we used was&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;GetNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;so we simply need to implement a function like this that uses our &lt;code&gt;Transitions&lt;/code&gt;.
First we write a function to pick a random element of a (nonempty) list.
We get a random &lt;code&gt;Int&lt;/code&gt; (in an &lt;code&gt;IO&lt;/code&gt; context, of course)
and use it to index into the list:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;-- will crash if the input is an empty list&lt;/span&gt;
&lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;randomRIO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- choose a random index&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="c1"&gt;-- return that element of the list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then our implementation is easy, we just create a function that takes as
input a &lt;code&gt;Transitions&lt;/code&gt; object and returns the corresponding &lt;code&gt;GetNextToken&lt;/code&gt;
function:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;randomNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;GetNextToken&lt;/span&gt;
&lt;span class="nf"&gt;randomNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;M&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;-- this shouldn&amp;#39;t happen, but let&amp;#39;s be safe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you are confused about why we define it as &lt;code&gt;randomNextToken transitions token&lt;/code&gt;,
substitute in the definition of &lt;code&gt;GetNextToken&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;randomNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once it's applied to a &lt;code&gt;Transitions&lt;/code&gt; object, what's left is a function that
looks up a token in the &lt;code&gt;Transitions&lt;/code&gt; map and pick one of the
following tokens at random.&lt;/p&gt;
&lt;h2&gt;The API&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Finally&lt;/em&gt;, we're ready to create the actual web service.
To start with, we define our API:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;API&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;question&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;&amp;#39;[JSON]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Question&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It has a single endpoint "question", which responds to HTTP GET requests
and returns a &lt;code&gt;Question&lt;/code&gt; serialized into JSON.&lt;/p&gt;
&lt;p&gt;My first attempt at implementing this turned out to be &lt;em&gt;really&lt;/em&gt; slow.
After poking around at a lot of stuff, I finally figured out it was because
every reference to the effectful &lt;code&gt;questionTransitions&lt;/code&gt; and &lt;code&gt;answerTransitions&lt;/code&gt;
was deserializing them from disk again. Needless to say, that was not the desired
behavior.&lt;/p&gt;
&lt;p&gt;After some digging I found &lt;a href = "http://hackage.haskell.org/package/io-memoize-1.1.1.0/docs/System-IO-Memoize.html"&gt;System.IO.Memoize&lt;/a&gt;, which memoizes expensive &lt;code&gt;IO&lt;/code&gt; actions (like deserializing a giant transitions object).
Initially this didn't help because I was memoizing &lt;em&gt;too late&lt;/em&gt;. So I moved it
right to app startup:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;startApp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;startApp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;cachedQt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eagerlyOnce&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;questionTransitions&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;cachedAt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eagerlyOnce&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;answerTransitions&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;simpleCors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedQt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedAt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(Incidentally, most of this stuff is standard servant boilerplate,
 just tweaked in order to use my cached &lt;code&gt;Transitions&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;The type of &lt;code&gt;eagerlyOnce&lt;/code&gt; is&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;eagerlyOnce&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;code&gt;questionTransitions&lt;/code&gt; is &lt;code&gt;IO Transitions&lt;/code&gt;, this means that
&lt;code&gt;eagerlyOnce questionTransitions&lt;/code&gt; is &lt;code&gt;IO (IO Transitions)&lt;/code&gt;. Since we're in an &lt;code&gt;IO&lt;/code&gt;
context, the &lt;code&gt;&amp;lt;-&lt;/code&gt; means that &lt;code&gt;cachedQt&lt;/code&gt; and &lt;code&gt;cachedAt&lt;/code&gt; are both &lt;code&gt;IO Transitions&lt;/code&gt;
(and that they should memoize their values).&lt;/p&gt;
&lt;p&gt;(The &lt;code&gt;simpleCors&lt;/code&gt; is just middleware that allows our service to handle
 cross-origin requests.)&lt;/p&gt;
&lt;p&gt;Now we can define our &lt;code&gt;Application&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
Which again needs the cached transitions
as inputs, I am not very happy about the ugly way we're passing them around
everywhere, but when I tried to avoid that by e.g. moving all the helpers into
the `startApp` function, I got all sorts of cryptic "Couldn't match type"
errors, so eventually I gave up and accepted my fate.
&lt;/blockquote&gt;

&lt;p&gt;It's pretty simple (again, this is all basically servant boilerplate):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Application&lt;/span&gt;
&lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedQt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedAt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;serve&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedQt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally we define the &lt;code&gt;server&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Transitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;API&lt;/span&gt;
&lt;span class="nf"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedQt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedAt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;liftIO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;qt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedQt&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cachedAt&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;randomQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randomNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randomNextToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In an &lt;code&gt;IO&lt;/code&gt; context it retrieves the cached transitions for the questions and
answers, and then it uses them to generate a random &lt;code&gt;Question&lt;/code&gt;. It then uses
&lt;code&gt;liftIO&lt;/code&gt; to lift the &lt;code&gt;Question&lt;/code&gt; out of the &lt;code&gt;IO&lt;/code&gt; context and into the &lt;code&gt;Server&lt;/code&gt;
context.&lt;/p&gt;
&lt;p&gt;There is a tiny amount of more boilerplate:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;API&lt;/span&gt;
&lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Proxy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;AND THAT'S IT. If you build and run it, you'll end up with a (very fast)
service running on localhost:8080:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;http://localhost:8080/question
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;answers&amp;quot;&lt;/span&gt;:&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;a rainstorm lasting several times&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;preventing too many babies the fall leaves&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;worms from the morning.&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;conserving water.&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;correctAnswer&amp;quot;&lt;/span&gt;:0,&lt;span class="s2"&gt;&amp;quot;questionText&amp;quot;&lt;/span&gt;:&lt;span class="s2"&gt;&amp;quot;In order of behavior is most important to make life must first&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;The Punchline&lt;/h2&gt;
&lt;p&gt;After all that work, I spent a couple of hours trying to deploy this to an EC2 machine,
failing miserably. The generated executable depends on a bunch of libraries on
my system. When I tried to statically include those, the compilation failed.
And the EC2 machine was way too underpowered to install &lt;code&gt;stack&lt;/code&gt; and build it
there. The Internet/StackOverflow was not a lot of help.&lt;/p&gt;
&lt;p&gt;At the end of the day, I just rewrote it in &lt;a href = "https://github.com/joelgrus/science-questions/tree/master/python-flask"&gt;flask&lt;/a&gt;
and deployed that version. :sad_face&lt;/p&gt;
&lt;p&gt;(However, it was only because I had a (much faster) flask version that I realized
 the servant version was way too slow and went down the &lt;code&gt;System.IO.Memoize&lt;/code&gt; path,
 so in that sense it's a good thing!)&lt;/p&gt;
&lt;p&gt;The flask version is up and running at &lt;code&gt;http://54.174.99.38/question&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;http://54.174.99.38/question
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;questionText&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Which system?&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;answers&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Absorbing water plants than the air pollution&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;It will be healthy&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;flood the air pollution&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tying a great gardener.&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;correctAnswer&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But it's a cheap EC2 nano instance, so please be gentle.&lt;/p&gt;
&lt;h2&gt;Next Time&lt;/h2&gt;
&lt;p&gt;In the third (and final) post, we'll
&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-3-the-single-page-app-purescript/"&gt;build a quiz webapp&lt;/a&gt;
that uses this service.&lt;/p&gt;</content><category term="Haskell, Hacking, Data, Data Science"></category></entry><entry><title>Building a Stupid Data Product, Part 1: The Data (Python)</title><link href="https://joelgrus.com/2016/02/15/building-a-stupid-data-product-part-1-the-data-python/" rel="alternate"></link><published>2016-02-15T07:00:00+01:00</published><updated>2016-02-15T07:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2016-02-15:/2016/02/15/building-a-stupid-data-product-part-1-the-data-python/</id><summary type="html">&lt;p&gt;(&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-2-the-web-service-haskell/"&gt;part 2&lt;/a&gt;,
 &lt;a href = "/2016/02/15/building-a-stupid-data-product-part-3-the-single-page-app-purescript/"&gt;part 3&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;As I'm not working right now&lt;a href = "#footnote"&gt;* &lt;/a&gt;,
I have a surfeit of time to hack on stupid things.
In particular, it seemed like a good idea to hack together a stupid data product.&lt;/p&gt;
&lt;p&gt;Inspiration (if you can call it such) struck when the Allen Institute released
a …&lt;/p&gt;</summary><content type="html">&lt;p&gt;(&lt;a href = "/2016/02/15/building-a-stupid-data-product-part-2-the-web-service-haskell/"&gt;part 2&lt;/a&gt;,
 &lt;a href = "/2016/02/15/building-a-stupid-data-product-part-3-the-single-page-app-purescript/"&gt;part 3&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;As I'm not working right now&lt;a href = "#footnote"&gt;* &lt;/a&gt;,
I have a surfeit of time to hack on stupid things.
In particular, it seemed like a good idea to hack together a stupid data product.&lt;/p&gt;
&lt;p&gt;Inspiration (if you can call it such) struck when the Allen Institute released
a dataset of &lt;a href="http://allenai.org/data.html"&gt;elementary school science questions&lt;/a&gt;,
presumably so that people can train computers to answer them. I am not quite so
ambitious, so instead I decided to use them to train a computer to generate
new random bogus elementary school science questions.&lt;/p&gt;
&lt;p&gt;Using markov chains to generate text this way isn't particularly exciting, so
I thought I'd also play around with the other parts of building a data product:
creating a backend service that serves up random questions, and creating a
single-page app that allows you to take these random quizzes.&lt;/p&gt;
&lt;p&gt;(Code, as always, is on &lt;a href = "https://github.com/joelgrus/science-questions"&gt;GitHub&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;If you know me, you're thinking "backend service, that sounds like a job for
Haskell" and also "single-page app, that sounds like a job for PureScript".
You're basically right, with a caveat we'll see later, but in this first blog
post let's just focus on the data piece.&lt;/p&gt;
&lt;p&gt;We'll use a dead simple markov chain model. For each word in the dataset, we
make a list of all the words that we see following it. That is, if we had only two
questions: "What is love?" and "What the heck?" then our model would say that
'What' can be followed by either 'is' or 'love'. We generate a sentence by picking
a starting word and repeatedly choosing a random next word (based on the transitions
we learned from the data) until we finish a sentence.&lt;/p&gt;
&lt;p&gt;This means our data goal is to generate these transitions. We'll represent them
as &lt;code&gt;dict&lt;/code&gt;s where the keys are words, and the values are lists of next words:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;What&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;is&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;the&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="s1"&gt;&amp;#39;is&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;love&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;# and so on&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To start with, we can just download the CSV:&lt;/p&gt;
&lt;p&gt;&lt;img alt="questions" src="https://joelgrus.com/images/questions_csv.png" /&gt;&lt;/p&gt;
&lt;p&gt;We only care about the 'question' column, so let's just read that in.
(As always, use &lt;code&gt;csv.reader&lt;/code&gt;, don't try to parse it by hand!)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;questions.csv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;raw_questions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;question&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# raw_questions[0]&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;#39;Which property of an object is identified using the sense of smell? (A) color (B) odor (C) temperature (D) weight&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we're going to actually want &lt;em&gt;two&lt;/em&gt; markov chain models. If you look at the
data, you'll see that questions and answers look quite different from one another.
So we'll generate one set of "question transitions" and another set of
"answer transitions".&lt;/p&gt;
&lt;p&gt;To that end, we need a way to split a &lt;code&gt;raw_question&lt;/code&gt; into a "question" piece and
several "answer" pieces. This looks like a job for regular expressions. Using the
example above, we might try something like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;\([A-D]\)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw_question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which looks for the letters A - D in parentheses, and splits the string
where it finds them. Looking at the file,
every question has either 3 or 4 answers, which means that the result of that &lt;code&gt;split&lt;/code&gt;
should have 4 or 5 elements (including the question text).&lt;/p&gt;
&lt;p&gt;Let's see where our regex falls down:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;split&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;\([A-D]\)&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;raw_questions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="c1"&gt;# answer looks like A. 9 yards B. 18 yards C. 50 yards D. 70 yards&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So we need a regex for this case, too, how about &lt;code&gt;"\s[A-D]\.\s"&lt;/code&gt;. If we iterate
this process, we end up with a list of regexes that covers all the questions
(except one that has no answers):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;splits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;\([A-D]\)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# (A) python (B) haskell (C) javascript (D) ruby&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;\s[A-D]\.\s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;#  A. python  B. haskell  C. javascript  D. ruby&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;\s[1-4]\.\s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;#  1. python  2. haskell  3. javascript  4. ruby&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;\s[A-D]\s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;#  A  python  B  haskell  C  javascript  D  ruby&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;\s[FGHJ]\s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;#  F  python  G  haskell  H  javascript  J  ruby&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; [A-D]\s&amp;quot;&lt;/span&gt;    &lt;span class="c1"&gt;#   A python&lt;/span&gt;
                  &lt;span class="c1"&gt;#   B haskell&lt;/span&gt;
                  &lt;span class="c1"&gt;#   C javascript&lt;/span&gt;
                  &lt;span class="c1"&gt;#   D ruby&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We're almost ready, the other thing we'll do is use a couple of &lt;em&gt;sentinels&lt;/em&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;START&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;__START__&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;STOP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;__STOP__&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We'll add a transition from &lt;code&gt;START&lt;/code&gt; to the first word of every sentence, and a
transition from the last word of every sentence to &lt;code&gt;STOP&lt;/code&gt;. That way we can generate
sentences using essentially the following logic:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;word = random_word_after(START)
while word != STOP:
  yield word
  word = random_word_after(word)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, to sum up, our strategy will be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;collect all of the question "sentences" and answer "sentences" separately&lt;/li&gt;
&lt;li&gt;use the sentences to generate transition &lt;code&gt;dict&lt;/code&gt;s&lt;/li&gt;
&lt;li&gt;serialize the transitions, so we can use them in other programs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First, the collecting:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;questions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;answers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;raw_questions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;splits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pieces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pieces&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pieces&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pieces&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;   &lt;span class="c1"&gt;# go on to the next raw_question&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;           &lt;span class="c1"&gt;# have you ever seen someone use a for .. else loop?&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# me neither!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we have a &lt;code&gt;list&lt;/code&gt; of questions and a &lt;code&gt;list&lt;/code&gt; of answers, so let's turn
each into a &lt;code&gt;dict&lt;/code&gt; of transitions. We'll use another regex to turn each question
(or answer) into words:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[^ ?\.,]+|\?|\.|\,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That regex looks kind of cryptic, but it's just looking to match either&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a period,&lt;/li&gt;
&lt;li&gt;a comma,&lt;/li&gt;
&lt;li&gt;a question mark, or&lt;/li&gt;
&lt;li&gt;a "word" that contains none of the above or spaces&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We just need to remember to add the "sentinel" words, and we have our function:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;make_transitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;transitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sentence&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# regex looks for &amp;quot;?&amp;quot;, &amp;quot;.&amp;quot;, &amp;quot;,&amp;quot; or groups of characters that aren&amp;#39;t&lt;/span&gt;
        &lt;span class="c1"&gt;# any of those, and aren&amp;#39;t spaces&lt;/span&gt;
        &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;START&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[^ ?\.,]+|\?|\.|\,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;STOP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prev_word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]):&lt;/span&gt;
            &lt;span class="n"&gt;transitions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prev_word&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next_word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;transitions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we just need to save them for later use:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;q_transitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_transitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;a_transitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_transitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;questions.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_transitions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;answers.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_transitions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we can generate sentences as above:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;next_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transitions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transitions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;STOP&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;markov_gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transitions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transitions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;START&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;STOP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;
      &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transitions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For example, to generate a fake question and four fake answers:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markov_gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_transitions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;For which part of the deepest ?&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markov_gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_transitions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;A deer&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markov_gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_transitions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Old refrigerators contain genes .&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markov_gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_transitions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;producers&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markov_gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_transitions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Mount St .&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Next Time&lt;/h2&gt;
&lt;p&gt;In the &lt;a href = "/2016/02/15/building-a-stupid-data-product-part-2-the-web-service-haskell/"&gt;next post&lt;/a&gt;
we'll build a web service that generates questions on demand.&lt;/p&gt;
&lt;p&gt;&lt;a name="footnote" /&gt;* &lt;/a&gt; I'm sort of looking for my next thing,
if you know of something I'd find interesting, let me know.&lt;/p&gt;</content><category term="Python, Hacking, Data, Data Science"></category></entry><entry><title>Creating a Function Index Using Cycle.js</title><link href="https://joelgrus.com/2016/01/21/creating-a-function-index-using-cyclejs/" rel="alternate"></link><published>2016-01-21T09:00:00+01:00</published><updated>2016-01-21T09:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2016-01-21:/2016/01/21/creating-a-function-index-using-cyclejs/</id><summary type="html">&lt;p&gt;The readers of my book have been clamoring for an &lt;a href = "https://github.com/joelgrus/data-science-from-scratch/issues/21"&gt;index of functions&lt;/a&gt;,
so that -- for example -- when someone sees me use &lt;code&gt;vector_mean&lt;/code&gt; on page 200 they can
easily figure out where to find its definition.&lt;/p&gt;
&lt;p&gt;It was easy enough (if tedious) to go through the book and create a …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The readers of my book have been clamoring for an &lt;a href = "https://github.com/joelgrus/data-science-from-scratch/issues/21"&gt;index of functions&lt;/a&gt;,
so that -- for example -- when someone sees me use &lt;code&gt;vector_mean&lt;/code&gt; on page 200 they can
easily figure out where to find its definition.&lt;/p&gt;
&lt;p&gt;It was easy enough (if tedious) to go through the book and create a spreadsheet,
but it also seemed an opportunity to build something in Javascript, which I always
unreasonably enjoy.&lt;/p&gt;
&lt;p&gt;My idea was pretty simple: a text input that you type in, and a table of
{ function_name, chapter, page } that's filtered (in real-time) by whatever you
type. So if you type "add" you get all the functions that contain "add" somewhere
in their name (e.g. &lt;code&gt;vector_add&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img alt="index-of-functions" src="https://joelgrus.com/images/index-of-functions.png" /&gt;&lt;/p&gt;
&lt;p&gt;Simple enough, but also a good opportunity to
use/learn some newfangled client-side-virtual-dom-javascript-magic framework.&lt;/p&gt;
&lt;p&gt;Given my love of all things Purescript, I
&lt;a href = "https://github.com/joelgrus/dsfs-function-index"&gt;built the first version using purescript-halogen&lt;/a&gt;.
Unfortunately (or fortunately), I don't understand &lt;a href="http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html"&gt;free monads&lt;/a&gt;
well enough to explain it to you. If you understand Purescript (or Haskell) it is
mostly straightforward, save that the halogen library gave me no easy way
(or at least no obvious way) to get at the value of a text input on every
&lt;code&gt;keyup&lt;/code&gt; event, which required a lot of ugly hacking around (and made up the bulk
of the time I spent on the project).&lt;/p&gt;
&lt;p&gt;Recently I've been poking at &lt;a href = "http://cycle.js.org/"&gt;cycle.js&lt;/a&gt;
(whose creator recently wrote an &lt;a href = "http://staltz.com/why-react-redux-is-an-inferior-paradigm.html"&gt;anti-React rant&lt;/a&gt;
that kind of resonated with me), so I figured I'd try recreating the project
using cycle. (It was surprisingly easy and fun, once I got past the self-loathing
of using a non-typed language.)&lt;/p&gt;
&lt;p&gt;The central idea of cycle is using &lt;a href = "http://cycle.js.org/observables.html"&gt;observables&lt;/a&gt;,
which are basically lazy streams of values. (If you want types, I believe this is basically
the paradigm of &lt;a href ="http://elm-lang.org/"&gt;Elm&lt;/a&gt;, and also there is a
&lt;a href = "https://github.com/bodil/purescript-signal"&gt;purescript-signal&lt;/a&gt; library
that does this.)&lt;/p&gt;
&lt;p&gt;The documentation calls the architecture
"Model-View-Intent", but I prefer to think of it as "Intent-Model-View", which is the order
in which things happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Intent: given some external source (the DOM, AJAX calls), create a lazy stream
  of &lt;em&gt;events&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Model: given a lazy stream of events, turn it into a lazy stream of &lt;em&gt;states&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;View: given a lazy stream of states, turn it into a lazy stream of &lt;em&gt;outputs&lt;/em&gt;
  (in our case, virtual-dom trees)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these can be a pure function (yay!), and the cycle.js machinery hooks
these together to create a loop.&lt;/p&gt;
&lt;p&gt;In this case, we'll have a text input, and the &lt;em&gt;intent&lt;/em&gt; should output its value
every time there is a keyup event:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;input&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;keyup&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hopefully this is pretty straightforward. (The &lt;code&gt;startWith('')&lt;/code&gt; just says that our stream should
start with an empty string event before any events happen.)&lt;/p&gt;
&lt;p&gt;Now the model needs to use that value (our "query") to produce a state. The state should be
whatever data we need to produce the output. Here that will be the query itself, as well as the
list of functions that satisfy the query:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;indexEntries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(The &lt;code&gt;$&lt;/code&gt;-suffix is a cycle convention (maybe broader) to indicate that a variable
 is an observable.) This is again pretty simple: given an element of the &lt;code&gt;value$&lt;/code&gt;
stream (which is the value of the text input after a keyup),
output a state consisting of both the query and the index entries that match it.&lt;/p&gt;
&lt;p&gt;Finally, we need the &lt;em&gt;view&lt;/em&gt; to turn that state into a virtual-dom representation:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nx"&gt;autofocus&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Search Query&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// v-dom table containing the entries&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;showEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Given a &lt;code&gt;state&lt;/code&gt;, we need a text input with its value set to &lt;code&gt;state.query&lt;/code&gt;,
and a table displaying the entries. (Yes, this is one of those setups where
you use Javascript functions to write HTML, deal with it.)
&lt;code&gt;showEntries&lt;/code&gt; is not very interesting, but here it is for completeness:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;showEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indexEntries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;thead&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;chapter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;page&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;function&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indexEntries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chapter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(The table header contains headings, the table body contains one row for each
 index entry. Apparently &lt;code&gt;td&lt;/code&gt; requires a string as its input.)&lt;/p&gt;
&lt;p&gt;That's essentially it, all that's left is to hook it all together:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;DOM&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;)))};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Cycle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;DOM&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;makeDOMDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;main&lt;/code&gt; just takes the &lt;code&gt;DOM&lt;/code&gt; source (which is the output of &lt;code&gt;makeDOMDriver&lt;/code&gt;)
and returns the virtual-dom sink that we've defined. (Or possibly I got "source"
and "sink" mixed up here, I don't really understand them.)&lt;/p&gt;
&lt;p&gt;Anyway, that's it, that's the whole app (modulo a html file that has an &lt;code&gt;div#app&lt;/code&gt;
and some hacky CSS and the file that contains the actual "index entries" data).&lt;/p&gt;
&lt;p&gt;Because there's lots of ES2015 voodoo, we need to use &lt;code&gt;browserify&lt;/code&gt; with all sorts
of plugins, and the output is a disgustingly large 600KB Javascript file. That's
the price of progress, I guess!&lt;/p&gt;
&lt;p&gt;(&lt;a href = "https://github.com/joelgrus/dsfs-function-index-cycle-js"&gt;Code on GitHub&lt;/a&gt;.)&lt;/p&gt;</content><category term="Javascript, Hacking"></category></entry><entry><title>Polyglot Twitter Bot, Part 4: PureScript</title><link href="https://joelgrus.com/2015/12/31/polyglot-twitter-bot-part-4-purescript/" rel="alternate"></link><published>2015-12-31T09:00:00+01:00</published><updated>2015-12-31T09:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2015-12-31:/2015/12/31/polyglot-twitter-bot-part-4-purescript/</id><summary type="html">&lt;p&gt;[The fourth in an (at least) 6-part series, all code &lt;a href = "https://github.com/joelgrus/polyglot-twitter-bot"&gt;on GitHub&lt;/a&gt; as always.]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-1-nodejs/"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-2-nodejs-aws-lambda/"&gt;Node.js + AWS Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/30/polyglot-twitter-bot-part-3-python-27-aws-lambda/"&gt;Python 2.7 + AWS Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PureScript&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;PureScript + AWS Lambda&lt;/li&gt;
&lt;li&gt;Bonus: PureScript + Twitter Streaming&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I know, you're thinking, "I've already read three parts of this series,
and I haven't heard one …&lt;/p&gt;</summary><content type="html">&lt;p&gt;[The fourth in an (at least) 6-part series, all code &lt;a href = "https://github.com/joelgrus/polyglot-twitter-bot"&gt;on GitHub&lt;/a&gt; as always.]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-1-nodejs/"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-2-nodejs-aws-lambda/"&gt;Node.js + AWS Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/30/polyglot-twitter-bot-part-3-python-27-aws-lambda/"&gt;Python 2.7 + AWS Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PureScript&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;PureScript + AWS Lambda&lt;/li&gt;
&lt;li&gt;Bonus: PureScript + Twitter Streaming&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I know, you're thinking, "I've already read three parts of this series,
and I haven't heard one mention of Haskell. Who are you and what have you done
with Joel?"&lt;/p&gt;
&lt;p&gt;Well, AWS Lambda doesn't support Haskell. (Yet.) Instead we'll work in
&lt;a href ="http://www.PureScript.org/"&gt;PureScript&lt;/a&gt;, a very Haskell-like
language that compiles to Javascript. In particular, we'll be able to (re-)use
the Node.js &lt;code&gt;twitter&lt;/code&gt; library via PureScript's foreign function interface.&lt;/p&gt;
&lt;p&gt;[Big caveat: I am a PureScript newbie. Although the code here works,
 it's possible (indeed, likely) that it's not well-designed PureScript. And although my
 explanations reflect my understanding of what's going on, it's possible (indeed, likely)
 that some of them are totally wrong. Mostly I did this part to help me learn PureScript better.
 If any PureScript gurus are reading this, I am eager to hear what I could have done better
 or more idiomatically.]&lt;/p&gt;
&lt;p&gt;To start with, &lt;a href = "http://www.PureScript.org/download/"&gt;install PureScript&lt;/a&gt;
and its build tool &lt;code&gt;pulp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then we need to create a directory and initialize:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;purescript-twitter-bot
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;purescript-twitter-bot
$&lt;span class="w"&gt; &lt;/span&gt;pulp&lt;span class="w"&gt; &lt;/span&gt;init
$&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;init
$&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;twitter&lt;span class="w"&gt; &lt;/span&gt;--save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(It's possible that it's bad form to both &lt;code&gt;pulp init&lt;/code&gt; and &lt;code&gt;npm init&lt;/code&gt;, but I did both.)&lt;/p&gt;
&lt;p&gt;A new PureScript project doesn't include by default a lot of its basic
libraries, so we'll need to install the ones we need for this project:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pulp&lt;span class="w"&gt; &lt;/span&gt;dep&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--save&lt;span class="w"&gt; &lt;/span&gt;purescript-console&lt;span class="w"&gt; &lt;/span&gt;purescript-foreign&lt;span class="w"&gt; &lt;/span&gt;purescript-arrays&lt;span class="w"&gt; &lt;/span&gt;purescript-strings&lt;span class="w"&gt;  &lt;/span&gt;purescript-functions
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, &lt;code&gt;pulp init&lt;/code&gt; should have created a &lt;code&gt;src&lt;/code&gt; subdirectory. Go there and create
&lt;code&gt;Twitter.purs&lt;/code&gt;, where we'll create all of the common &lt;em&gt;types&lt;/em&gt; for working
with Twitter, as well as the function to initialize the Twitter client.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Prelude&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Data.Foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Foreign&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Control.Monad.Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;-- | Effect type for interacting with Twitter.&lt;/span&gt;
&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

&lt;span class="c1"&gt;-- | The Twitter client returned by the Javascript `Twitter()` constructor.&lt;/span&gt;
&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TwitterClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What do these things mean?
In PureScript functions are (by default) &lt;a href = "https://en.wikipedia.org/wiki/Pure_function"&gt;pure&lt;/a&gt;.
If we want them to have side effects, we need to explicitly declare those
side effects. And a function that interacts with Twitter is necessarily impure,
since it depends on -- and possibly modifies -- the state of Twitter.
So we need to define a &lt;code&gt;TWITTER&lt;/code&gt;
&lt;a href = "http://www.PureScript.org/learn/eff/"&gt;effect&lt;/a&gt; that we can use to
mark functions as having Twitter-based side effects (or side inputs).
(The bang &lt;code&gt;!&lt;/code&gt; means that &lt;code&gt;TWITTER&lt;/code&gt; is an effect.)&lt;/p&gt;
&lt;p&gt;In comparison, the &lt;code&gt;*&lt;/code&gt; means that &lt;code&gt;TwitterClient&lt;/code&gt; is a &lt;em&gt;type&lt;/em&gt;, this allows us
to define functions that take a &lt;code&gt;TwitterClient&lt;/code&gt; as input and functions that
return a &lt;code&gt;TwitterClient&lt;/code&gt; as output. (The &lt;code&gt;foreign import&lt;/code&gt; means that we're not
going to define the type within PureScript, but that it's going to refer to something
we'll do in Javascript.)&lt;/p&gt;
&lt;p&gt;Notably, these are (mostly) just &lt;em&gt;names&lt;/em&gt;. We will define a (Javascript-implemented)
function that returns a &lt;code&gt;TwitterClient&lt;/code&gt;, as well as other functions that take a &lt;code&gt;TwitterClient&lt;/code&gt;
as input. The type system will simply enforce that any function that wants a &lt;code&gt;TwitterClient&lt;/code&gt;
gets something that we've identified as a &lt;code&gt;TwitterClient&lt;/code&gt;. And that any computation that
involves a &lt;code&gt;TWITTER&lt;/code&gt; effect can only run in a context that allows &lt;code&gt;TWITTER&lt;/code&gt; effects.&lt;/p&gt;
&lt;p&gt;(If this all seems new and confusing, that's because it is new and confusing. Bear with me.)&lt;/p&gt;
&lt;p&gt;We also need a type to represent Twitter credentials:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;consumer_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;access_token_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;access_token_secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;PureScript has easy &lt;a href = "https://leanpub.com/PureScript/read#leanpub-auto-simple-types"&gt;record types&lt;/a&gt;
that basically correspond to Javascript objects. That is, the &lt;code&gt;Credentials&lt;/code&gt; type is (basically)
a Javascript object that has those exact four keys, and whose values are all strings.
(However, on the PureScript side it is typed, and you would get an error if you tried
  to create a &lt;code&gt;Credentials&lt;/code&gt; instance with no &lt;code&gt;consumer_key&lt;/code&gt; or with a numeric &lt;code&gt;consumer_secret&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;Now we can use the Foreign Function Interface to get an instance of the Twitter client.
Let's first handle the PureScript side:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;twitterClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TwitterClient&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Yikes. The &lt;code&gt;foreign import&lt;/code&gt; means that we're going to define this function in Javascript.
And the type says that this function takes as input a &lt;code&gt;Credentials&lt;/code&gt; object and does something
that involves a &lt;code&gt;TWITTER&lt;/code&gt; side effect and returns a &lt;code&gt;TwitterClient&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;(&lt;code&gt;Eff&lt;/code&gt; is the
monad for &lt;a href = "http://www.PureScript.org/learn/eff/"&gt;specifying native effects&lt;/a&gt;,
and indicates that this function can only be run in a "context" that allows
&lt;code&gt;TWITTER&lt;/code&gt; side effects. The &lt;code&gt;forall eff&lt;/code&gt; means that the context can allow other
side effects as well.)&lt;/p&gt;
&lt;p&gt;Now we're ready to write the Javascript side.  Create a companion file &lt;code&gt;Twitter.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cm"&gt;/* global exports */&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;use strict&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// module Twitter&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;twitter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;twitterClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Twitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That's it. The &lt;code&gt;module Twitter&lt;/code&gt; comment is important (I think) and tells the
PureScript compiler that this goes with the &lt;code&gt;Twitter&lt;/code&gt; module in &lt;code&gt;Twitter.purs&lt;/code&gt;.
The code simply loads the Node.js &lt;code&gt;twitter&lt;/code&gt; library and exports the &lt;code&gt;twitterClient&lt;/code&gt; function
that we declared in &lt;code&gt;Twitter.purs&lt;/code&gt;. Its input is a PureScript &lt;code&gt;Credentials&lt;/code&gt; object
(which gets translated here into just a plain Javascript object, which is
 exactly what the &lt;code&gt;Twitter&lt;/code&gt; function requires).&lt;/p&gt;
&lt;p&gt;The only subtlety is that instead of returning the Twitter client directly,
we wrap it in a function of zero arguments. We need to do this &lt;a href = "https://github.com/PureScript/PureScript/wiki/FFI-tips"&gt;whenever our function
returns an &lt;code&gt;Eff&lt;/code&gt; context&lt;/a&gt;. (And conversely, if you create a PureScript function
whose return value is an &lt;code&gt;Eff&lt;/code&gt;, the generated Javascript function requires an
extra call, as we'll see later (and which caused me a lot of confusion)).&lt;/p&gt;
&lt;p&gt;Let's also create simple (non-production quality) types to represent Tweets:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TweetId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;

&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Tweet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TweetId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Tweets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Tweet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, for us, a tweet has an &lt;code&gt;id&lt;/code&gt;, a &lt;code&gt;user&lt;/code&gt;, and some &lt;code&gt;text&lt;/code&gt;. Obviously the actual
data model is a lot richer, but this is all we'll need to build our bot.&lt;/p&gt;
&lt;p&gt;Next we need to write the code to interface with the search API. We'll stick this
in its own module &lt;code&gt;Twitter.Search&lt;/code&gt; which we'll create in &lt;code&gt;src/Twitter/Search.purs&lt;/code&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Twitter.Search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Prelude&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Data.Function&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Control.Monad.Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Twitter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This looks like the previous set of imports, except that now we also import the &lt;code&gt;Twitter&lt;/code&gt; module
we just created, as well as the &lt;code&gt;Data.Function&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;You see, PureScript functions are all really functions of a single variable.
If you were to have&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;then &lt;code&gt;sum&lt;/code&gt; is really a function that takes an &lt;code&gt;Int&lt;/code&gt; and returns a new function &lt;code&gt;Int -&amp;gt; Int&lt;/code&gt;.
For example &lt;code&gt;sum 2&lt;/code&gt; is the function that adds 2 to any number, and &lt;code&gt;sum 2 3&lt;/code&gt; is really
&lt;code&gt;(sum 2) 3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This means that (naively) we need to write foreign functions the same way:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;with implementation&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you have functions with a lot of parameters, this can get ugly really fast.
That's where &lt;code&gt;Data.Function&lt;/code&gt; comes in, it provides helper functions that allow us
to write normal multiple-argument Javascript functions.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sumImpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Fn2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;

&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;runFn2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sumImpl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;with implementation&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sumImpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The declaration &lt;code&gt;Fn2 Int Int Int&lt;/code&gt; means &lt;code&gt;sumImpl&lt;/code&gt; is a Javascript
function of two Int arguments that returns an Int result. And the &lt;code&gt;runFn2&lt;/code&gt;
converts it into the usual curried PureScript function. The exposed &lt;code&gt;sum&lt;/code&gt;
still looks the same as before, so anyone using the function doesn't need to
worry about all of these details.&lt;/p&gt;
&lt;p&gt;Anyway, back to Twitter searching. The Twitter search API allows you to specify
a lot of options, but we'll restrict ourselves to just &lt;code&gt;q&lt;/code&gt; [query] and &lt;code&gt;count&lt;/code&gt;
[number of results]. And we'll provide a helper function that allows callers
to just specify the query:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;SearchOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;searchOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;SearchOptions&lt;/span&gt;
&lt;span class="nf"&gt;searchOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, we're ready to define the search function, using the Data.Function
trick from above:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;searchImpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Fn3&lt;/span&gt;
&lt;span class="w"&gt;                                         &lt;/span&gt;&lt;span class="kt"&gt;TwitterClient&lt;/span&gt;
&lt;span class="w"&gt;                                         &lt;/span&gt;&lt;span class="kt"&gt;SearchOptions&lt;/span&gt;
&lt;span class="w"&gt;                                         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tweets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                                         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TwitterClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="kt"&gt;SearchOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tweets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;-- the docs suggest not using point-free style here&lt;/span&gt;
&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;runFn3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;searchImpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That is, the &lt;code&gt;search&lt;/code&gt; function takes a &lt;code&gt;TwitterClient&lt;/code&gt;, some &lt;code&gt;SearchOptions&lt;/code&gt;,
and a callback (that takes some &lt;code&gt;Tweets&lt;/code&gt; as input and does something effectful
with them), and does something with a &lt;code&gt;TWITTER&lt;/code&gt; effect (and returns no result).&lt;/p&gt;
&lt;p&gt;[Incidentally, I would rather not insist that the callback include the &lt;code&gt;TWITTER&lt;/code&gt;
effect. Our eventual "retweet" callback will, but you could also imagine just a
"log to the console" callback that doesn't. However, it caused me problems if I
didn't include it, so it's there.]&lt;/p&gt;
&lt;p&gt;Then we need to define &lt;code&gt;searchImpl&lt;/code&gt; in &lt;code&gt;Search.js&lt;/code&gt;, which is not dissimilar to our initial
Node.js version:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchImpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;searchOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Because `searchImpl` returns a value in the Eff monad, its Javascript implementation&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// needs to return a function of no arguments.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;search/tweets&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;searchOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Map results to our `Tweet` record type.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// (If `Tweet` wasn&amp;#39;t a plain old record type, we&amp;#39;d have to do something&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//  more complicated here.)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screen_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Similarly, because `callback` returns a value in the Eff monad, its&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Javascript transpilation returns a function of no arguments, which means&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// that to actually *execute* the callback, we need to call the returned function.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Not realizing this caused me a lot of grief.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Because the return type is `Unit`, we just return an empty object.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We similarly create a &lt;code&gt;Twitter.Retweet&lt;/code&gt; module in &lt;code&gt;src/Twitter/Retweet.purs&lt;/code&gt;, with just a single function:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;retweetImpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Fn2&lt;/span&gt;
&lt;span class="w"&gt;                                          &lt;/span&gt;&lt;span class="kt"&gt;TwitterClient&lt;/span&gt;
&lt;span class="w"&gt;                                          &lt;/span&gt;&lt;span class="kt"&gt;TweetId&lt;/span&gt;
&lt;span class="w"&gt;                                          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CONSOLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;retweet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TwitterClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="kt"&gt;TweetId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CONSOLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="c1"&gt;-- the docs suggest not using point-free style here&lt;/span&gt;
&lt;span class="nf"&gt;retweet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tweetId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;runFn2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;retweetImpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tweetId&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Where &lt;code&gt;retweetImpl&lt;/code&gt; is defined in &lt;code&gt;src/Twitter/Retweet.js&lt;/code&gt; as&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;retweetImpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweetId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;statuses/retweet/&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweetId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, we're ready to do the actual work. (So far we've just been doing the groundwork.)
As usual, we create a separate file
for credentials, here &lt;code&gt;src/MyCredentials.purs&lt;/code&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;MyCredentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Twitter&lt;/span&gt;

&lt;span class="nf"&gt;myCredentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Credentials&lt;/span&gt;
&lt;span class="nf"&gt;myCredentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;consumer_key&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;access_token_key&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;access_token_secret&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we stick the actual work in &lt;code&gt;src/Main.purs&lt;/code&gt;. After importing all the stuff
we need, we can create a &lt;code&gt;findAndRetweet&lt;/code&gt; function.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Prelude&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Control.Monad.Eff&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Control.Monad.Eff.Console&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;qualified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Data.Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Array&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Data.Maybe&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;qualified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Data.String.Regex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Regex&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;MyCredentials&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Twitter&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Twitter.Search&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Twitter.Retweet&lt;/span&gt;

&lt;span class="nf"&gt;findAndRetweet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;SearchOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Regex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Regex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="kt"&gt;TwitterClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CONSOLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="nf"&gt;findAndRetweet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rgx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;retweetMatches&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- for each tweet that passes the regex filter, send its id to `retweet`&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;retweetMatches&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tweets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;foreachE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rgxFilter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;retweet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- if there&amp;#39;s a regex, make sure it matches tweet.text&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;rgxFilter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rgx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;of&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Regex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kt"&gt;Nothing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Given a &lt;code&gt;TwitterClient&lt;/code&gt; and some &lt;code&gt;SearchOptions&lt;/code&gt;, it just runs the search
using the callback &lt;code&gt;retweetMatches&lt;/code&gt; defined here. And &lt;code&gt;retweetMatches&lt;/code&gt; just
filters out tweets that don't match the regex (if there is one) and calls
&lt;code&gt;retweet&lt;/code&gt; for each of the tweets that's left.&lt;/p&gt;
&lt;p&gt;Finally, we just need a &lt;code&gt;main&lt;/code&gt; function to do the work.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;-- This assumes MyCredentials.purs exports myCredentials :: Credentials&lt;/span&gt;
&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Eff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;CONSOLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;TWITTER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;twitterClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myCredentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;findAndRetweet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rgx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;make &lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;great again&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt; -america -filter:retweets&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;searchOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;rgx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Regex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;make (.*) great again&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Regex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;noFlags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ignoreCase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The type of &lt;code&gt;main&lt;/code&gt; indicates that it does something that involves both
the &lt;code&gt;CONSOLE&lt;/code&gt; side effect and the &lt;code&gt;TWITTER&lt;/code&gt; side effect, and that it doesn't
return anything. (&lt;code&gt;Unit&lt;/code&gt; is similar to other languages' &lt;code&gt;void&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;And &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt; is the scary &lt;a href = "https://wiki.haskell.org/Monad_tutorials_timeline"&gt;monad&lt;/a&gt;
bind, which grabs the &lt;code&gt;TwitterClient&lt;/code&gt;
out of the &lt;code&gt;Eff&lt;/code&gt; monad (recall that &lt;code&gt;twitterClient&lt;/code&gt; returns an &lt;code&gt;Eff _ TwitterClient&lt;/code&gt;)
and hands it to the &lt;code&gt;findAndRetweet&lt;/code&gt; function, which (after currying the
search options and regex) just takes a &lt;code&gt;TwitterClient&lt;/code&gt; and does its magic.&lt;/p&gt;
&lt;p&gt;If you run this from the command line:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pulp&lt;span class="w"&gt; &lt;/span&gt;run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;it should retweet all the things!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/l5Q0wHw.jpg" alt="RETWEET ALL THE THINGS!"&gt;&lt;/p&gt;
&lt;p&gt;Final note: this probably seems like a lot of work. It &lt;em&gt;was&lt;/em&gt; a lot of work. But
most of the work was creating a (bare-bones, toy) PureScript &lt;code&gt;Twitter&lt;/code&gt; library.
If you already had such a library (which, in many applications, you would),
it would have been a lot less work, and you would just have to have written
the code in &lt;code&gt;Main.purs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next time we'll get this PureScript version running on AWS Lambda. (Which might
take me a couple of days to pull together, happy new year!)&lt;/p&gt;</content><category term="Code, Twitter, PureScript, AWS, Make_GreatAgain"></category></entry><entry><title>Polyglot Twitter Bot, Part 3: Python 2.7 + AWS Lambda</title><link href="https://joelgrus.com/2015/12/30/polyglot-twitter-bot-part-3-python-27-aws-lambda/" rel="alternate"></link><published>2015-12-30T12:00:00+01:00</published><updated>2015-12-30T12:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2015-12-30:/2015/12/30/polyglot-twitter-bot-part-3-python-27-aws-lambda/</id><summary type="html">&lt;p&gt;[The third in an (at least) 6-part series, all code &lt;a href = "https://github.com/joelgrus/polyglot-twitter-bot"&gt;on GitHub&lt;/a&gt; as always.]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-1-nodejs/"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-2-nodejs-aws-lambda/"&gt;Node.js + AWS Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Python 2.7 + AWS Lambda&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Purescript&lt;/li&gt;
&lt;li&gt;Purescript + AWS Lambda&lt;/li&gt;
&lt;li&gt;Bonus: Purescript + Twitter Streaming&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lambda also allows Python functions, although only Python 2.7.  In practice,
this won't affect us much …&lt;/p&gt;</summary><content type="html">&lt;p&gt;[The third in an (at least) 6-part series, all code &lt;a href = "https://github.com/joelgrus/polyglot-twitter-bot"&gt;on GitHub&lt;/a&gt; as always.]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-1-nodejs/"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-2-nodejs-aws-lambda/"&gt;Node.js + AWS Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Python 2.7 + AWS Lambda&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Purescript&lt;/li&gt;
&lt;li&gt;Purescript + AWS Lambda&lt;/li&gt;
&lt;li&gt;Bonus: Purescript + Twitter Streaming&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lambda also allows Python functions, although only Python 2.7.  In practice,
this won't affect us much, as we'll just&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;__future__&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;print_function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and cross our fingers that our bot will do the right thing where Unicode
is concerned. (Or, at least, won't crash with a &lt;code&gt;UnicodeEncodeError&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;Now, because Python isn't oriented around asynchronous callbacks,
we don't need to worry about &lt;code&gt;context.succeed&lt;/code&gt; and &lt;code&gt;context.fail&lt;/code&gt;,
instead we can just write the usual Python function that's done when it's done.&lt;/p&gt;
&lt;p&gt;To work with Twitter, we'll use the
&lt;a href = "https://github.com/ryanmcgrath/twython"&gt;Twython&lt;/a&gt; library.
As before, we'll need to "deploy" a zip file that contains all of the dependencies
we need, which means we need to install Twython into our project directory.&lt;/p&gt;
&lt;p&gt;Create a directory for your Python bot and in that directory run&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;twython&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which should install all the Twython files right there. (There are various subtleties,
for example, if you are using &lt;code&gt;virtualenv&lt;/code&gt;, or if your Python is installed
using Homebrew, which you should read about
&lt;a href="http://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html"&gt;here&lt;/a&gt;, but which you should not expect me to understand or explain.)&lt;/p&gt;
&lt;p&gt;Now we're ready to write our bot. In &lt;code&gt;index.py&lt;/code&gt; (because why not?), let's start
with the imports we need:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;__future__&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;print_function&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;twython&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Twython&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;twython.exceptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TwythonError&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As always, we need credentials, but we don't want them checked into our code.
One way is to create a &lt;code&gt;credentials.json&lt;/code&gt; file&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;consumer_key&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;consumer_secret&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;access_token_key&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;access_token_secret&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which our script can just load:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;credentials.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After which we can initialize the client:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Twython&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;consumer_key&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                 &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;consumer_secret&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                 &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;access_token_key&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                 &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;access_token_secret&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course, we also need to create our query and regular expression:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;make &amp;quot;great again&amp;quot; -america -filter:retweets&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;rgx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;make (.*) great again&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we just have to create the handler:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;statuses&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="c1"&gt;# re.search matches anywhere in the string; re.I means case-insensitive&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="c1"&gt;# client.retweet will raise an error if we try to retweet a tweet&lt;/span&gt;
            &lt;span class="c1"&gt;# that we&amp;#39;ve already retweeted. to avoid having to keep track, we&lt;/span&gt;
            &lt;span class="c1"&gt;# just use a try/except block&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retweet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TwythonError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that's the bot!  We just need to zip it up:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;zip&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;twitter.zip&lt;span class="w"&gt; &lt;/span&gt;*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and go through the exact same "create a lambda function" process as before.
(Make sure to choose "Python 2.7" as the runtime.) Probably (certainly) you don't need
both the Node bot and the Python bot active at the same time, so make sure to
disable the event source for one of them.&lt;/p&gt;
&lt;p&gt;Next post we'll dive into the crazy world of Purescript!&lt;/p&gt;</content><category term="Code, Twitter, Python, AWS, Make_GreatAgain"></category></entry><entry><title>Polyglot Twitter Bot, Part 2: Node.js + AWS Lambda</title><link href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-2-nodejs-aws-lambda/" rel="alternate"></link><published>2015-12-29T19:00:00+01:00</published><updated>2015-12-29T19:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2015-12-29:/2015/12/29/polyglot-twitter-bot-part-2-nodejs-aws-lambda/</id><summary type="html">&lt;p&gt;[The second in an (at least) 6-part series, all code &lt;a href = "https://github.com/joelgrus/polyglot-twitter-bot"&gt;on GitHub&lt;/a&gt; as always.]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-1-nodejs/"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Node.js + AWS Lambda&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Python 2.7 + AWS Lambda&lt;/li&gt;
&lt;li&gt;Purescript&lt;/li&gt;
&lt;li&gt;Purescript + AWS Lambda&lt;/li&gt;
&lt;li&gt;Bonus: Purescript + Twitter Streaming&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;AWS has a recent-ish &lt;a href = "https://aws.amazon.com/lambda/"&gt;Lambda&lt;/a&gt; product,
which lets you upload functions and then have them run on …&lt;/p&gt;</summary><content type="html">&lt;p&gt;[The second in an (at least) 6-part series, all code &lt;a href = "https://github.com/joelgrus/polyglot-twitter-bot"&gt;on GitHub&lt;/a&gt; as always.]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-1-nodejs/"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Node.js + AWS Lambda&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Python 2.7 + AWS Lambda&lt;/li&gt;
&lt;li&gt;Purescript&lt;/li&gt;
&lt;li&gt;Purescript + AWS Lambda&lt;/li&gt;
&lt;li&gt;Bonus: Purescript + Twitter Streaming&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;AWS has a recent-ish &lt;a href = "https://aws.amazon.com/lambda/"&gt;Lambda&lt;/a&gt; product,
which lets you upload functions and then have them run on demand (or on a schedule),
without having to actually run a server. There's a lot of interesting things you
can do with this functionality, one of which is "run a Twitter bot".&lt;/p&gt;
&lt;p&gt;These days Lambda allows Node.js, Python, or Java functions, but for now we'll
modify and use the Node.js bot we built &lt;a href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-1-nodejs/"&gt;previously&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The only trick is that we need to fit it into the &lt;a href = "http://docs.aws.amazon.com/lambda/latest/dg/programming-model.html"&gt;Lambda execution model&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Lambda expects our Node module to export a &lt;code&gt;handler&lt;/code&gt; function with the following signature:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;// do something&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;event&lt;/code&gt; contains the parameters for the function invocation,
and &lt;code&gt;context&lt;/code&gt; contains Lambda-specific details. Our bot function
won't take any parameters, since it has everything it needs
(query, credentials, etc...) hardcoded in it. The &lt;code&gt;context&lt;/code&gt; object contains
&lt;code&gt;succeed&lt;/code&gt; and &lt;code&gt;fail&lt;/code&gt; methods that tell Lambda that the function is done running.
(This is why we included them last time.)&lt;/p&gt;
&lt;p&gt;So a handler that would work here is just&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;searchAndTweet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, if your function doesn't use any fancy libraries, you can simply type the
code into the AWS console. However, here we're using the &lt;code&gt;twitter&lt;/code&gt; library, so
we can't. Instead we have to create a deployment package, which is just a zip file.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;ls
credentials.js&lt;span class="w"&gt;  &lt;/span&gt;index.js&lt;span class="w"&gt;  &lt;/span&gt;node_modules&lt;span class="w"&gt;  &lt;/span&gt;package.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In particular, we need to include the &lt;code&gt;node_modules&lt;/code&gt; directory, which contains
the Twitter library and its dependencies.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;zip&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;twitter.zip&lt;span class="w"&gt; &lt;/span&gt;*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This gets me a ~1.3MB zip file. Now go to
&lt;a href="https://console.aws.amazon.com/lambda/"&gt;your AWS console&lt;/a&gt;
and click "Create a Lambda Function".&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/create_a_lambda_function.png"&gt;&lt;/p&gt;
&lt;p&gt;"Skip" the "select blueprint" step,
give your function a name, and upload the zip file.&lt;/p&gt;
&lt;p&gt;The default handler name &lt;code&gt;index.handler&lt;/code&gt; should work (&lt;code&gt;index&lt;/code&gt; means to look in
&lt;code&gt;index.js&lt;/code&gt;, &lt;code&gt;.handler&lt;/code&gt; means to use the function called that), and then create
or choose a "basic execution" role.&lt;/p&gt;
&lt;p&gt;The 128MB should be plenty of memory, but you might want to up the timeout to
10 seconds or so.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/configure_function.png"&gt;&lt;/p&gt;
&lt;p&gt;Finally, click "Create Function". At this point you want to
"Save and Test" to make sure it works.&lt;/p&gt;
&lt;p&gt;Finally, you probably want it to run on a schedule, so choose the "Event Sources"
tab and click "Add event source". Go to "scheduled event" and enable it to run
every 5 minutes (or every 15 if you want).&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/every-5-minutes.png"&gt;&lt;/p&gt;
&lt;p&gt;(I am not much of an &lt;a href="https://aws.amazon.com/lambda/pricing/"&gt;AWS pricing&lt;/a&gt; guru,
 but my best estimate is that this Lambda function should cost you approximately $0.)&lt;/p&gt;
&lt;p&gt;And that's it, your bot is done! Next time we'll do it in Python.&lt;/p&gt;</content><category term="Code, Twitter, Node, Javascript, AWS, Make_GreatAgain"></category></entry><entry><title>Polyglot Twitter Bot, Part 1: Node.js</title><link href="https://joelgrus.com/2015/12/29/polyglot-twitter-bot-part-1-nodejs/" rel="alternate"></link><published>2015-12-29T12:00:00+01:00</published><updated>2015-12-29T12:00:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2015-12-29:/2015/12/29/polyglot-twitter-bot-part-1-nodejs/</id><summary type="html">&lt;p&gt;[The first in an (at least) 6-part series, all code &lt;a href = "https://github.com/joelgrus/polyglot-twitter-bot"&gt;on GitHub&lt;/a&gt; as always.]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;Node.js&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Node.js + AWS Lambda&lt;/li&gt;
&lt;li&gt;Python 2.7 + AWS Lambda&lt;/li&gt;
&lt;li&gt;Purescript&lt;/li&gt;
&lt;li&gt;Purescript + AWS Lambda&lt;/li&gt;
&lt;li&gt;Bonus: Purescript + Twitter Streaming&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Like most of you, I've long dreamed of making a Twitter bot.
And also like most of …&lt;/p&gt;</summary><content type="html">&lt;p&gt;[The first in an (at least) 6-part series, all code &lt;a href = "https://github.com/joelgrus/polyglot-twitter-bot"&gt;on GitHub&lt;/a&gt; as always.]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;Node.js&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Node.js + AWS Lambda&lt;/li&gt;
&lt;li&gt;Python 2.7 + AWS Lambda&lt;/li&gt;
&lt;li&gt;Purescript&lt;/li&gt;
&lt;li&gt;Purescript + AWS Lambda&lt;/li&gt;
&lt;li&gt;Bonus: Purescript + Twitter Streaming&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Like most of you, I've long dreamed of making a Twitter bot.
And also like most of you, I've been doing a lot of
&lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; recently. So I thought I'd take the first stab
at writing my Twitter bot in Node. (Also, this will lay the groundwork for doing it in
Purescript later.)&lt;/p&gt;
&lt;p&gt;In particular, I wanted to create the &lt;a href = "https://twitter.com/make_greatagain"&gt;make_greatagain&lt;/a&gt;
bot, which would look for tweets containing "MAKE ___ GREAT AGAIN" constructions
and retweet them. (But which skips tweets containing "MAKE AMERICA GREAT AGAIN",
I'm looking for riffs on the original, not the original itself.)&lt;/p&gt;
&lt;p&gt;&lt;a class="twitter-timeline" href="https://twitter.com/make_greatagain" data-widget-id="681668898454765568"&gt;Tweets by @make_greatagain&lt;/a&gt;&lt;/p&gt;
&lt;script&gt;!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");&lt;/script&gt;

&lt;p&gt;To start with, you should probably have Node installed. (I'll wait.)
Then create a directory, and initialize a new project:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;twitter-bot-node
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;twitter-bot-node
npm&lt;span class="w"&gt; &lt;/span&gt;init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Just accept all the default options for &lt;code&gt;npm init&lt;/code&gt;, I don't know what they mean
either.&lt;/p&gt;
&lt;p&gt;Now, if we're going to talk to Twitter, we should install the
&lt;a href = "https://www.npmjs.com/package/twitter"&gt;Node Twitter module&lt;/a&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;npm&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;twitter&lt;span class="w"&gt; &lt;/span&gt;--save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point you should create a Twitter account for your bot and get its
credentials. After creating the account and logging in, go to &lt;a href = "https://apps.twitter.com"&gt;apps.twitter.com&lt;/a&gt;
and click on "Create New App". Give it a name and a description, and accept the terms of service.
Then go to the "Keys and Access Tokens" tab and click "Create My Access Token".
You should now have a consumer key, a consumer secret, an access token, and an
access token secret. We need those, but KEEP THEM SECRET.&lt;/p&gt;
&lt;p&gt;Now, we're ready to create our &lt;code&gt;index.js&lt;/code&gt;.  We start by loading the Twitter library
and initializing it with our credentials:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Twitter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;twitter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Twitter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;consumer_key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;consumer_secret&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;access_token_key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;access_token_secret&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;NOTE: if you are committing this code to GitHub, DO NOT CHECK IN THE CREDENTIALS.
One approach is to stick them in &lt;code&gt;credentials.js&lt;/code&gt;, like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;consumer_key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;consumer_secret&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;access_token_key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;access_token_secret&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and then in &lt;code&gt;index.js&lt;/code&gt; just do&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./credentials&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Twitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and then make sure to add &lt;code&gt;credentials.js&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, we want to find Tweets of the given form. For my example, that's&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;make &amp;quot;great again&amp;quot; -america -filter:retweets&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rgx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;/make .* great again/i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(Hopefully, your Twitter bot will do something different.)&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;query&lt;/code&gt; is the actual query we'll send to Twitter. It looks for Tweets that
contain both "make" and "great again" but not "america". And it ignores retweets.
Since that search could (in theory) return irrelevant tweets
(e.g. "great again doesn't make sense"), there's also a regex that we'll use
as a client-side check.&lt;/p&gt;
&lt;p&gt;Now, the Node model is asynchronous, which means we need to program with callbacks.
That is, to search, we need to do something like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;search/tweets&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;node.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screen_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code will kick off a search for "node.js" and then immediately go on to
whatever code comes next. Meanwhile, whenever the search returns,
the provided callback will be called, either logging the error or
printing out the returned tweets.&lt;/p&gt;
&lt;p&gt;Now in our code we want the callback to retweet each of the returned tweets.
However, if we try to retweet a tweet we've already retweeted, we'll get an error.
This means we either need to keep track of all the tweets we've already retweeted
or else handle those errors intelligently. The second is a lot easier.&lt;/p&gt;
&lt;p&gt;In order to retweet, we just need to post the tweet &lt;code&gt;id&lt;/code&gt; to the retweets endpoint.
If you inspect the returned tweets, they have both an &lt;code&gt;id&lt;/code&gt; field (which is a number)
and an &lt;code&gt;id_str&lt;/code&gt; field (which is a string). For precision-related reasons (I assume),
Javascript mangles the numeric ids, so we'll need to use the string version.&lt;/p&gt;
&lt;p&gt;All of which results in a function that looks like&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Runs a Twitter search for the specified `query` and retweets all the results.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;searchAndTweet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;search and tweet&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;search/tweets&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;15&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Make sure we match the regex.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rgx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweetId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id_str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;statuses/retweet/&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweetId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;// Will return an error if we try to retweet a tweet that we&amp;#39;ve already&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;// retweeted.&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// consider doing something for no match&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;success&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Why do we pass in the &lt;code&gt;succeed&lt;/code&gt; and &lt;code&gt;fail&lt;/code&gt; callbacks? That's a story for the next
post. (Spoiler: it involves AWS Lambda.) In the meantime, you can just pass in &lt;code&gt;console.log&lt;/code&gt; for both.&lt;/p&gt;
&lt;p&gt;Now, all that's left is to run your Twitter bot. We can use &lt;code&gt;setInterval&lt;/code&gt; to make
it run every 5 minutes:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;searchAndTweet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;60&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then if you simply&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;node&lt;span class="w"&gt; &lt;/span&gt;index.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;your bot will start running. Of course,
you probably don't want to keep it running locally on your computer all the time.
We'll deal with that in the next post.&lt;/p&gt;</content><category term="Code, Twitter, Node, Javascript, AWS, Make_GreatAgain"></category></entry><entry><title>Stupid Itertools Tricks for Data Science</title><link href="https://joelgrus.com/2015/08/23/stupid-itertools-tricks-for-data-science/" rel="alternate"></link><published>2015-08-23T12:00:00+02:00</published><updated>2015-08-23T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2015-08-23:/2015/08/23/stupid-itertools-tricks-for-data-science/</id><content type="html">&lt;p&gt;(This is a blog post version of my &lt;a href ="https://github.com/joelgrus/stupid-itertools-tricks-pydata"&gt;PyData Seattle talk&lt;/a&gt;, slides and code at the link.)&lt;/p&gt;
&lt;p&gt;In my &lt;a href="https://joelgrus.com/2015/07/07/haskell-style-fibonacci-in-python/"&gt;last post&lt;/a&gt;&lt;/p&gt;</content><category term="Mathematics, Code, Haskell, Python"></category></entry><entry><title>Haskell-Style Fibonacci in Python</title><link href="https://joelgrus.com/2015/07/07/haskell-style-fibonacci-in-python/" rel="alternate"></link><published>2015-07-07T12:00:00+02:00</published><updated>2015-07-07T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2015-07-07:/2015/07/07/haskell-style-fibonacci-in-python/</id><summary type="html">&lt;p&gt;If you've ever done a tech interview, you're probably familiar
with the Fibonacci sequence:&lt;/p&gt;
&lt;p&gt;1, 1, 2, 3, 5, 8, 13, ....&lt;/p&gt;
&lt;p&gt;where each number is the sum of the previous two.  A relatively simple
(and relatively overused)
interview problem is to write a function that returns the n-th
Fibonacci number …&lt;/p&gt;</summary><content type="html">&lt;p&gt;If you've ever done a tech interview, you're probably familiar
with the Fibonacci sequence:&lt;/p&gt;
&lt;p&gt;1, 1, 2, 3, 5, 8, 13, ....&lt;/p&gt;
&lt;p&gt;where each number is the sum of the previous two.  A relatively simple
(and relatively overused)
interview problem is to write a function that returns the n-th
Fibonacci number.&lt;/p&gt;
&lt;h2&gt;Recursive Python&lt;/h2&gt;

&lt;p&gt;The most intuitive implementation is recursive:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;the profoundly inefficient recursive implementation&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It works in a pretty obvious fashion.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, it's shockingly inefficient.
To compute &lt;code&gt;fib(10)&lt;/code&gt; we need to compute &lt;code&gt;fib(9)&lt;/code&gt; and &lt;code&gt;fib(8)&lt;/code&gt;.  To compute
&lt;code&gt;fib(9)&lt;/code&gt; we need to compute &lt;code&gt;fib(8)&lt;/code&gt; (again) and &lt;code&gt;fib(7)&lt;/code&gt;.  And so on.&lt;/p&gt;
&lt;p&gt;The moral of the story is that to compute &lt;code&gt;fib(10)&lt;/code&gt; we end up making 177
calls to &lt;code&gt;fib&lt;/code&gt;.  To compute &lt;code&gt;fib(20)&lt;/code&gt; we end up making 21,891 calls to &lt;code&gt;fib&lt;/code&gt;.
And to compute &lt;code&gt;fib(30)&lt;/code&gt; we end up making 2.7 million calls to &lt;code&gt;fib&lt;/code&gt;, which
ends up taking almost a full second on my laptop.&lt;/p&gt;
&lt;p&gt;One solution is &lt;a href="https://en.wikipedia.org/wiki/Memoization"&gt;memoization&lt;/a&gt;,
in which we remember previously computed values.  We won't get into that here.&lt;/p&gt;
&lt;h2&gt;Iterative Python&lt;/h2&gt;

&lt;p&gt;Another solution is an iterative approach:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;the iterative implementation&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is much more efficient -- to compute &lt;code&gt;fib(n)&lt;/code&gt; we just do O(n) operations.
At the same time, it's much less clear what it's actually computing.
If I didn't tell you it was &lt;code&gt;fib&lt;/code&gt;
you might have to stare at it for a while to figure out exactly what it was doing.
And even knowing that it's &lt;code&gt;fib&lt;/code&gt; it's probably not obvious that it's
implemented &lt;em&gt;correctly&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Haskell&lt;/h2&gt;

&lt;p&gt;Haskell, in case you don't know, is everyone's favorite pure functional
programming language.  In particular, it embraces
&lt;a href="https://en.wikipedia.org/wiki/Lazy_evaluation"&gt;laziness&lt;/a&gt;
in which values are computed only as needed.  &lt;/p&gt;
&lt;p&gt;This means we can compute &lt;a href="https://wiki.haskell.org/The_Fibonacci_sequence#Canonical_zipWith_implementation"&gt;the (infinite) sequence of Fibonacci numbers&lt;/a&gt; as&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zipWith&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kt"&gt;Prelude&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should understand this definition as "the sequence that starts with 1,
followed by 1,
followed by the sequence of numbers
obtained by "zipping" together the sequences &lt;code&gt;fibs&lt;/code&gt; and &lt;code&gt;tail fibs&lt;/code&gt;
(that is, all of the elements of &lt;code&gt;fibs&lt;/code&gt; after the first)
and adding together each corresponding pair of elements.  &lt;/p&gt;
&lt;p&gt;Which means the third element of the sequence is the first element (of &lt;code&gt;fibs&lt;/code&gt;) plus
the second element (which is the first element of &lt;code&gt;tail fibs&lt;/code&gt;).  The fourth element
is the second element (of &lt;code&gt;fibs&lt;/code&gt;) plus the third element (which is the second element
of &lt;code&gt;tail fibs&lt;/code&gt;).  And so on.  This is precisely the definition of Fibonacci.&lt;/p&gt;
&lt;p&gt;This definition may seem circular, since we're using &lt;code&gt;fibs&lt;/code&gt; to define &lt;code&gt;fibs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After all, the following sort of thing leads to infinite recursion:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
&lt;span class="ne"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;maximum&lt;/span&gt; &lt;span class="n"&gt;recursion&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="n"&gt;exceeded&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But in the Haskell version, because of laziness, the elements of &lt;code&gt;fibs&lt;/code&gt; will only be evaluated as needed.
To compute the third element, we only need to know the first two elements,
which we already do by the time we're trying to compute the third element, and so on.&lt;/p&gt;
&lt;p&gt;Appreciate its stark, mathematical beauty!  (Learn Haskell!)&lt;/p&gt;
&lt;h2&gt;Laziness in Python&lt;/h2&gt;

&lt;p&gt;We can achieve laziness in Python using generators.  One way is list comprehensions
in parentheses.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;genexpr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x7f5efc33bd70&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="ne"&gt;StopIteration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each element of &lt;code&gt;num&lt;/code&gt; is computed only on demand.&lt;/p&gt;
&lt;p&gt;We can also use functions with &lt;code&gt;yield&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;to3&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;   &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;   &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;   &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;to3&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is how we'll implement the Haskell-style Fibonacci.&lt;/p&gt;
&lt;h2&gt;itertools&lt;/h2&gt;

&lt;p&gt;The Haskell implementation used &lt;code&gt;tail&lt;/code&gt; (to get the elements after the first)
and &lt;code&gt;take&lt;/code&gt; (to get a certain number of elements from the front).  Python
doesn't have those, so we'll need
to implement our own versions.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;itertools&lt;/code&gt; module contains some helpers for working with laziness.
We'll need &lt;code&gt;islice&lt;/code&gt; which allows us to slice a new generator out of an old one.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itertools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;islice&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iterable&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;return elements from 1 to forever&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;islice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iterable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterable&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;return elements from 0 to n in a list&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;islice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iterable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For our Python 2.7 version we'll also need &lt;code&gt;imap&lt;/code&gt;, which is simply the lazy version of &lt;code&gt;map&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As a warmup, let's create an infinite sequence using the iterative approach:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which we can use like:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Haskell-style in Python&lt;/h2&gt;

&lt;p&gt;Recall the Haskell implementation:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zipWith&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's not hard to create the Python 2.7 equivalent&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itertools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;imap&lt;/span&gt;   &lt;span class="c1"&gt;# lazy map&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;operator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;     &lt;span class="c1"&gt;# add(x, y) = x + y&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;imap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;())):&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The logic is exactly the same.  The first two elements both equal 1.
After that we lazily add together the corresponding elements of &lt;code&gt;fibs()&lt;/code&gt;
and of &lt;code&gt;tail(fibs())&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In Python 3.3+, this gets even simpler.  First &lt;code&gt;map&lt;/code&gt; is already lazy,
so there's no reason to use &lt;code&gt;imap&lt;/code&gt;.  And second, 3.3 introduces
&lt;a href="https://docs.python.org/3/whatsnew/3.3.html#pep-380"&gt;&lt;code&gt;yield from&lt;/code&gt;&lt;/a&gt;
which allows us to replace the clunky&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;with&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means that the Python 3.3+ implementation is simply:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which we could one-line as&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;More on this in a second.&lt;/p&gt;
&lt;h2&gt;Warning!&lt;/h2&gt;

&lt;p&gt;Despite appearing Haskell-like, this version is basically as inefficient
as the original recursive version.  That's because every time &lt;code&gt;fibs()&lt;/code&gt;
is called recursively, Python redoes from scratch all the work to generate
the sequence.  &lt;/p&gt;
&lt;p&gt;Whereas in Haskell things are
&lt;a href="https://wiki.haskell.org/Functional_programming#Immutable_data"&gt;immutable&lt;/a&gt;,
which means that there's
only a single &lt;code&gt;fibs&lt;/code&gt; hanging around.  Once its first few elements have been
computed, they never have to be computed again.&lt;/p&gt;
&lt;p&gt;So while our Python version is clever, it's also impractical.&lt;/p&gt;
&lt;h2&gt;A Final Comparison&lt;/h2&gt;

&lt;p&gt;Compare again&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;with the Haskell&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zipWith&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's practically the same code!  (Yes, the one-line version is hideous Python,
I just did that so it would be more comparable with the Haskell version.)&lt;/p&gt;
&lt;p&gt;I showed it to one of my friends, who was so impressed that he said
"I have no idea what that Python code does".&lt;/p&gt;
&lt;p&gt;Next time you get asked Fibonacci as an interview question,
consider using this version, and let me know what happens!&lt;/p&gt;
&lt;h2&gt;Update!!!!!!&lt;/h2&gt;

&lt;p&gt;As was pointed out in the comments, one can use &lt;code&gt;itertools.tee&lt;/code&gt; to split a
generator into multiple (efficient) copies.  This means that the following
slight modification:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;a new fibs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;fibs1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fibs2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fibs&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fibs1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fibs2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;is incredibly efficient. &lt;/p&gt;</content><category term="Mathematics, Code, Haskell, Python"></category></entry><entry><title>On The Mathematics of Spot It!</title><link href="https://joelgrus.com/2015/06/12/on-the-mathematics-of-spot-it/" rel="alternate"></link><published>2015-06-12T12:00:00+02:00</published><updated>2015-06-12T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2015-06-12:/2015/06/12/on-the-mathematics-of-spot-it/</id><summary type="html">&lt;p&gt;Last weekend we went to a party where one of the other attendees brought
&lt;a href = "http://www.amazon.com/Spot-it-Disney-Frozen-Alphabet/dp/B00LK0N1ZK"&gt;Spot It! Frozen&lt;/a&gt;
for her kids. It's a simple game with circular cards, each of which has 8 pictures in it,
most of them &lt;i&gt;Frozen&lt;/i&gt;-themed.&lt;/p&gt;
&lt;p&gt;&lt;img alt="frozen" src="https://joelgrus.com/images/spot_it_frozen.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The setup is that any two cards in the deck …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Last weekend we went to a party where one of the other attendees brought
&lt;a href = "http://www.amazon.com/Spot-it-Disney-Frozen-Alphabet/dp/B00LK0N1ZK"&gt;Spot It! Frozen&lt;/a&gt;
for her kids. It's a simple game with circular cards, each of which has 8 pictures in it,
most of them &lt;i&gt;Frozen&lt;/i&gt;-themed.&lt;/p&gt;
&lt;p&gt;&lt;img alt="frozen" src="https://joelgrus.com/images/spot_it_frozen.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The setup is that any two cards in the deck have &lt;i&gt;exactly&lt;/i&gt; one picture in common.
There are various sets of rules, but they all boil down to some variation of
"deal two cards, try to spot the common element."&lt;/p&gt;
&lt;p&gt;To my surprise / delight, Madeline picked up the game very quickly and was quite good at it,
despite her not knowing any of the &lt;i&gt;Frozen&lt;/i&gt; characters on account of having
never seen the movie. [That's right, I am Dad of the Year, hold your applause until the end.]&lt;/p&gt;
&lt;p&gt;So I bought her her own set (&lt;a href="http://www.amazon.com/Blue-Orange-00411-Spot-It/dp/B0039S7NO6/"&gt;original edition&lt;/a&gt;)
and we started playing it at home.  The more I played it, the more I puzzled over its mathematics.
How would one go about designing such a deck?&lt;/p&gt;
&lt;p&gt;&lt;img alt="spotit" src="https://joelgrus.com/images/spot_it.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;I was unable to figure it out off the top of my head, so I Googled it.
Sure enough, I was not the first one to ask this question, although most of the
answers I found were not terrifically understandable.  I finally achieved enlightenment
through &lt;a href="http://math.stackexchange.com/questions/36798/what-is-the-math-behind-the-game-spot-it/36806#36806"&gt;this StackExchange answer&lt;/a&gt;
although it wasn't as clear as I liked.  Also, it didn't provide the code to generate the decks,
which I decided was a worthwhile exercise.  (As always,
&lt;a href="https://github.com/joelgrus/spot-it"&gt;the code is on GitHub&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;The mathematics involves &lt;a href="http://en.wikipedia.org/wiki/Projective_plane#Finite_projective_planes"&gt;finite projective planes&lt;/a&gt;,
which I learned about so that I could explain them to you.&lt;/p&gt;
&lt;h2&gt;Projective Planes&lt;/h2&gt;

&lt;p&gt;In run-of-the-mill planes (the kind you likely studied in geometry class),
every two distinct points determine exactly one line.
And every two distinct lines intersect in either zero points (if they are parallel)
or one point (if they are not parallel).&lt;/p&gt;
&lt;p&gt;A projective plane contains extra points at infinity where parallel lines can intersect.
This means that in a projective plane, any two distinct lines intersect in exactly one point.&lt;/p&gt;
&lt;p&gt;One way to make this happen is to -- for each slope -- add a point at infinity
where all the lines of that slope intersect.  That is, add an "infinity 0"
where all horizontal lines intersect, an "infinity 1" where all lines with slope 1 intersect,
an "infinity infinity" where all the vertical lines intersect,
and so on.  Then, so that any two distinct points have exactly one line between them,
add a new "line at infinity" that goes through all the infinities.&lt;/p&gt;
&lt;h2&gt;Finite Projective Planes&lt;/h2&gt;

&lt;p&gt;Although you are probably not used to thinking about finite "planes", we can do something similar for them.&lt;/p&gt;
&lt;p&gt;Choose some prime number n,
and consider the n x n grid of points:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ordinary_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(We'll explain why we chose n to be prime in a bit.)&lt;/p&gt;
&lt;p&gt;In this finite plane we do arithmetic &lt;a href="http://en.wikipedia.org/wiki/Modular_arithmetic"&gt;mod n&lt;/a&gt;,
so that (for example) the set of points
with y = 0 is in fact a horizontal line that "wraps around" from (n - 1, 0) to (0, 0).&lt;/p&gt;
&lt;p&gt;It turns out that there are n + 1 ordinary lines through (0, 0):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;slope 0: goes through (1, 0), (2, 0), ...&lt;/li&gt;
&lt;li&gt;slope 1: goes through (1, 1), (2, 2), ...&lt;/li&gt;
&lt;li&gt;slope 2: goes through (1, 2), (2, 4), ...&lt;/li&gt;
&lt;li&gt;slope 3: goes through (1, 3), (2, 6), ...&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;infinite slope: goes through (0, 1), (0, 2), ...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and every point that's not (0, 0) lies on exactly one of these lines.&lt;/p&gt;
&lt;blockquote&gt;This is one place where the prime-ness of n matters.  For instance,
if we'd chosen n = 4, then the line with slope 0

[(0, 0), (1, 0), (2, 0), (3, 0)]

and the line with slope 2

[(0, 0), (1, 2), (2, 0), (3, 2)]

both pass through (0, 0) and (2, 0), but clearly they're not the same line.
n being prime ensures the "two points lie on exactly one line" condition.&lt;/blockquote&gt;

&lt;p&gt;Each ordinary (non-vertical) line is defined by its slope and its intercept.  For example, there is a line through (0, 0) with slope 1,
which passes through (0, 0), (1, 1), (2, 2), and (3, 3).  And there is a line through (0, 1) with slope 1,
it passes through (0, 1), (1, 2), (2, 3), and (3, 0).  [Remember that we're doing arithmetic mod n.] Each vertical line is defined just by its x-coordinate.&lt;/p&gt;
&lt;p&gt;Again we have the problem that parallel lines don't intersect, and again we'll solve it by
adding "points at infinity", one for each slope.  We'll represent the "infinity" with slope 1
just as the number 1, and we'll represent the "vertical infinity" as the unicode &lt;code&gt;u"∞"&lt;/code&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;points_at_infinity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;infinite points are just the numbers 0 to n - 1&lt;/span&gt;
&lt;span class="sd"&gt;    (corresponding to the infinity where lines with that slope meet)&lt;/span&gt;
&lt;span class="sd"&gt;    and infinity infinity (where vertical lines meet)&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;∞&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we just need to make sure the correct infinity belongs to each line:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ordinary_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;returns the ordinary line through (0, b) with slope m&lt;/span&gt;
&lt;span class="sd"&gt;    in the finite projective plan of degree n&lt;/span&gt;
&lt;span class="sd"&gt;    includes &amp;#39;infinity m&amp;#39;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;vertical_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;returns the vertical line with the specified x-coordinate&lt;/span&gt;
&lt;span class="sd"&gt;    in the finite projective plane of degree n&lt;/span&gt;
&lt;span class="sd"&gt;    includes &amp;#39;infinity infinity&amp;#39;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;∞&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We also need to add another line that goes through the points at infinity:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;line_at_infinity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;the line at infinity just contains the points at infinity&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;points_at_infinity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Are You Sure About The "Two Lines One Point"?&lt;/h2&gt;

&lt;p&gt;I am.  But let's prove it.  Imagine we have two different lines.
We want to prove that they intersect in &lt;i&gt;exactly&lt;/i&gt; one point.
We have several types of lines, so we'll need to consider every possible combination of cases:&lt;/p&gt;
&lt;p&gt;&lt;b&gt;two ordinary lines with the same slope:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Say we have the lines (m1, b1) and (m1, b2).  They intersect at an ordinary point if
there is some x so that m1 * x + b1 = m1 * x + b2; that is, if b1 = b2.
But since they're different lines, necessarily b1 doesn't equal b2.
So these lines just intersect at "infinity m1".&lt;/p&gt;
&lt;p&gt;&lt;b&gt;two ordinary lines with different slope:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Say we have the lines (m1, b1) and (m2, b2). They clearly don't intersect at infinity,
since one passes through "infinity m1" and the other through "infinity m2".
They intersect at an ordinary point precisely if&lt;/p&gt;
&lt;p&gt;m1 * x + b1 = m2 * x + b2 (mod n)&lt;/p&gt;
&lt;p&gt;or if&lt;/p&gt;
&lt;p&gt;(m1 - m2) * x = b2 - b1 (mod n)&lt;/p&gt;
&lt;p&gt;Because &lt;a href="http://en.wikipedia.org/wiki/Finite_field#Definitions.2C_first_examples.2C_and_basic_properties"&gt;n is prime&lt;/a&gt;
(this is another place where the prime-ness is important),
it turns out there is a unique x for which this is true, so that's where they intersect:&lt;/p&gt;
&lt;p&gt;(x, (m1 * x + b1) mod n) = (x, (m2 * x + b2) mod n)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;ordinary line (m1, b1), vertical line through x&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;It's easy to see they intersect exactly at (x, m1 * x + b1)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;ordinary line (m1, b1), line at infinity&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;Again, it's easy to see that they intersect exactly at "infinity m1".&lt;/p&gt;
&lt;p&gt;&lt;b&gt;vertical line through x1, vertical line through x2&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;Intersect exactly at "infinity infinity"&lt;/p&gt;
&lt;p&gt;&lt;b&gt;vertical line through x, line at infinity&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;Intersect exactly at "infinity infinity"&lt;/p&gt;
&lt;h2&gt;What the Hell?&lt;/h2&gt;

&lt;p&gt;This has a point, I promise.  Let's pick a n, say 7.  The corresponding projective plane
has 57 points (7x's * 7y's + another 8 at infinity) and 57 lines (8 slopes * 7 intercepts + line at infinity).
Each line passes through 8 of the points, and any two lines intersect in exactly one point.  Now mentally translate:&lt;/p&gt;
&lt;p&gt;point &amp;lt;-&amp;gt; picture&lt;/p&gt;
&lt;p&gt;line &amp;lt;-&amp;gt; card&lt;/p&gt;
&lt;p&gt;passes through &amp;lt;-&amp;gt; contains&lt;/p&gt;
&lt;p&gt;projective plane &amp;lt;-&amp;gt; deck&lt;/p&gt;
&lt;p&gt;intersect in &amp;lt;-&amp;gt; have in common&lt;/p&gt;
&lt;p&gt;Translated, the deck has 57 cards and 57 (distinct) pictures.  Each card contains 8 pictures,
and any two cards have in common exactly one picture.  That's exactly the game.
(Actually, for reasons unknown to the Internet, the version of the game you buy only has 55 cards.
But our version has 57 cards.)&lt;/p&gt;
&lt;p&gt;How do we create a deck?
First, let's create a couple of functions to collect all of the points and lines:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;all_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ordinary_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;points_at_infinity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;all_lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;ordinary_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;vertical_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;line_at_infinity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This works, the only problem is the cards are not all that nice looking:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Indeed, they have the "picture" &lt;code&gt;(1, 5)&lt;/code&gt; in common.  We should probably
give the pictures nicer names.  The following function will use the names
you pass in:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;make_deck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pics&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;all_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# create a mapping from point to pic&lt;/span&gt;
    &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pic&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pic&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# and return the remapped cards&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And now it's easy to play your own game of Spot It! in the shell:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;play_game&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# make a copy so as not to muck with the original deck, then shuffle it&lt;/span&gt;
    &lt;span class="n"&gt;deck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt;
    &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# keep playing until fewer than 2 cards are left&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;card1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;card2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# shuffle the cards, too, to simulate that&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# they might face different directions&lt;/span&gt;

        &lt;span class="c1"&gt;# find the matching element&lt;/span&gt;
        &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pic&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pic&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;card1&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pic&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;card2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;card1&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;card2&lt;/span&gt;

        &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Match? &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;correct!&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;incorrect!&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For example, if I give it animal names, a play of the game looks like:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;play_game&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Crab&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Baboon&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Rat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Jackal&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Bear&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Buffalo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Llama&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Sheep&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Snake&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Yak&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Jackal&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Otter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Kouprey&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Wolf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Rook&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Ox&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;Match&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Jackal&lt;/span&gt;
&lt;span class="n"&gt;correct&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Narwhal&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Coyote&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Dog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Wolf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Dotterel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Rat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Mosquito&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Tarsier&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Gnu&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Dogfish&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Sheep&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Porcupine&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Mosquito&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Heron&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Tapir&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Rook&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;Match&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Narwhal&lt;/span&gt;
&lt;span class="n"&gt;incorrect&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and so on.  And now you know how the game works.  Maybe next time we'll
&lt;a href="https://github.com/joelgrus/spot-it/blob/master/haskell/SpotIt.hs"&gt;do it in Haskell&lt;/a&gt;!&lt;/p&gt;</content><category term="Mathematics, Code, Spot it, Madeline, Frozen"></category></entry><entry><title>Data Science From Scratch: First Principles with Python</title><link href="https://joelgrus.com/2015/04/26/data-science-from-scratch-first-principles-with-python/" rel="alternate"></link><published>2015-04-26T12:00:00+02:00</published><updated>2015-04-26T12:00:00+02:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2015-04-26:/2015/04/26/data-science-from-scratch-first-principles-with-python/</id><summary type="html">&lt;p&gt;I am super-excited to announce that the book I've been working on for more than the last year, &lt;i&gt;Data Science from Scratch: First Principles with Python&lt;/i&gt; is finally available! (buy from &lt;a href="http://shop.oreilly.com/product/0636920033400.do"&gt;O'Reilly&lt;/a&gt;, use discount code AUTHD to save some money) (buy from &lt;a href="http://www.amazon.com/Data-Science-Scratch-Principles-Python/dp/149190142X"&gt;Amazon&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;My experience learning and teaching data science …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I am super-excited to announce that the book I've been working on for more than the last year, &lt;i&gt;Data Science from Scratch: First Principles with Python&lt;/i&gt; is finally available! (buy from &lt;a href="http://shop.oreilly.com/product/0636920033400.do"&gt;O'Reilly&lt;/a&gt;, use discount code AUTHD to save some money) (buy from &lt;a href="http://www.amazon.com/Data-Science-Scratch-Principles-Python/dp/149190142X"&gt;Amazon&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;My experience learning and teaching data science was that there were two primary paths:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Math Path: "So you want to be a data scientist? Sure, the first thing you need to know is &lt;i&gt;matrix decompositions&lt;/i&gt;. How well do you remember your measure theory?"&lt;/li&gt;
&lt;li&gt;The Tools Path: "So you want to be a data scientist? Great, here's the most important libraries to know.  How well do you know R?"&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Although I am myself a "math person", the first approach never resonated with me.  The fun of data science for me has always been &lt;i&gt;working with data&lt;/i&gt;.  At the same time, I've never been thrilled with the second approach -- it's a good way to start doing data science without ever really understanding what you're doing.&lt;/p&gt;
&lt;p&gt;My ideal would be a "&lt;a href="http://en.wikipedia.org/wiki/Third_Way"&gt;third way&lt;/a&gt;" between these approaches:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;understanding the behavior of the most common tools by working through a solid-but-less-than-textbook-rigorous understanding of the math behind them, and&lt;/li&gt;
&lt;li&gt;implementing simplified versions of them &lt;i&gt;from scratch&lt;/i&gt; to understand exactly what it is they're doing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hence &lt;i&gt;Data Science from Scratch&lt;/i&gt;.  It's got math, but only as much as is totally necessary.  It's got scraping and cleaning and munging.  It's got machine learning.  It's got databases and MapReduce.  Necessarily it doesn't go &lt;i&gt;deep&lt;/i&gt; into any of these, but I like to think it establishes a broad, solid foundation for someone who knows some math and some programming but is not (necessarily) an expert at either.&lt;/p&gt;
&lt;p&gt;Many technical books (I won't name names) explain things in their text and then dump pages of hard-to-follow code at you that you are expected to puzzle through.  I spent a lot of time trying to write clean code that illuminated the concepts &lt;i&gt;on its own&lt;/i&gt; and that &lt;i&gt;reinforced&lt;/i&gt; the ideas from the text.  As is the current fashion these days, all of the code and data is &lt;a href = "https://github.com/joelgrus/data-science-from-scratch"&gt;on GitHub&lt;/a&gt;, if you'd like to get a sense of what the book is about.&lt;/p&gt;
&lt;p&gt;If you are interested in the topic, I encourage you to check it out, write a review, and let me know what you think!  (You can see the full table of contents on the &lt;a href="http://shop.oreilly.com/product/0636920033400.do"&gt;O'Reilly page&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/Data-Science-Scratch-Principles-Python/dp/149190142X"&gt;&lt;img alt="book cover" src="https://joelgrus.com/images/dsfs.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="Life, Data Science, Writing"></category></entry><entry><title>2014 Year in Review</title><link href="https://joelgrus.com/2015/01/15/2014-year-in-review/" rel="alternate"></link><published>2015-01-15T19:09:00+01:00</published><updated>2015-01-15T19:09:00+01:00</updated><author><name>Joel Grus</name></author><id>tag:joelgrus.com,2015-01-15:/2015/01/15/2014-year-in-review/</id><summary type="html">&lt;p&gt;2014 was a crazy year, mostly because I took on two very large projects either of which would have made for a pretty crazy year.&lt;/p&gt;
&lt;h2&gt;1. The Book&lt;/h2&gt;
&lt;p&gt;I am writing a book.  (Yes, another one.)  Actually, the first draft is done, and I am right now revising it based …&lt;/p&gt;</summary><content type="html">&lt;p&gt;2014 was a crazy year, mostly because I took on two very large projects either of which would have made for a pretty crazy year.&lt;/p&gt;
&lt;h2&gt;1. The Book&lt;/h2&gt;
&lt;p&gt;I am writing a book.  (Yes, another one.)  Actually, the first draft is done, and I am right now revising it based on the final set of technical reviews.  This one has a real publisher and everything, which means (I hope) that for a change I won't have to sell it out of the trunk of my car in the parking lots at gun shows.&lt;/p&gt;
&lt;p&gt;The book will (likely) be called &lt;em&gt;Data Science from Scratch: First Principles with Python&lt;/em&gt;, which is a pretty good description of what the book is about. It covers everything from statistics to probability to linear algebra to regression to machine learning, all from first principles.  That means no numpy, no scipy, no scikit-learn, no nothing.  Every time we need an algorithm, we implement the algorithm.&lt;/p&gt;
&lt;p&gt;As a recovering mathematician&lt;sup&gt;1&lt;/sup&gt;, I have a natural inclination toward "from scratch" approaches&lt;sup&gt;2&lt;/sup&gt;, which was only complicated by the fact that I didn't actually know how to do most of the things in the book "from scratch", which meant that I had to learn them well enough not only to use them but to &lt;em&gt;teach&lt;/em&gt; them.&lt;/p&gt;
&lt;p&gt;There is an old joke (or possibly I made it up and it’s a new joke, I'm not sure):&lt;/p&gt;
&lt;p&gt;"How I hate writing!"&lt;br /&gt;
"I thought you were a writer."&lt;br /&gt;
"Indeed, I think you have to be a writer to hate writing as much as I do."&lt;/p&gt;
&lt;p&gt;I am here to tell you that this joke is completely true.  (I'm also here to tell you that as soon as this book is done I'll probably start on the next one.)&lt;/p&gt;
&lt;p&gt;As the book gets closer to publication I will have plenty more to say about it.  To that end I've "rebooted" my website / blog and hope to use it more frequently going forward.&lt;/p&gt;
&lt;h2&gt;2. Google&lt;/h2&gt;
&lt;p&gt;In early 2014 I got a call from a Google recruiter who wanted to know whether I'd like to interview for an engineering job at Google.  Despite the fact that I was in no shape to pass a Google interview, I eventually agreed and spent two months worth of free time practicing whiteboard coding and teaching myself computer science.  (Two months worth of free time that I should really have spent working on my book, or possibly paying attention to my family.)&lt;/p&gt;
&lt;p&gt;Interestingly, writing a [good technical] book requires you to put a lot of thought into how to most clearly communicate ideas through code, which also makes for a valuable technical interview skill, and I think I probably interviewed much better on account of the writing I'd done at that point.&lt;/p&gt;
&lt;p&gt;I probably have a lot more to say about the interview process at some point in the future, but the moral of the story is that I got the job and started at Google in late August, at which point I had to frantically learn both C++&lt;sup&gt;3&lt;/sup&gt; and the Google way of writing software, all the while struggling to finish the book.&lt;/p&gt;
&lt;p&gt;My hope is that 2015 will be slightly less crazy, but probably it won't be.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;1. You never actually recover from mathematics.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;2. I once took a math course where on the first day the professor stated how pleased he was that we'd proven &lt;a href = "http://en.wikipedia.org/wiki/Tychonoff%27s_theorem"&gt;a certain theorem&lt;/a&gt; the previous semester, since that meant we would be able to [in a "moral" sense] use it in his class.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;3. My resume did have C++ listed on it, but under the "programming languages that I have heard of" section.&lt;/p&gt;</content><category term="Life"></category></entry><entry><title>T-Shirts, Feminism, Parenting, and Data Science, Part 2: Eigenshirts</title><link href="https://joelgrus.com/2013/06/24/t-shirts-feminism-parenting-and-data-science-part-2-eigenshirts/" rel="alternate"></link><published>2013-06-24T20:37:00+02:00</published><updated>2013-06-24T20:37:00+02:00</updated><author><name>joelgrus</name></author><id>tag:joelgrus.com,2013-06-24:/2013/06/24/t-shirts-feminism-parenting-and-data-science-part-2-eigenshirts/</id><summary type="html">&lt;p&gt;(You might want to read &lt;a href="https://joelgrus.com/2013/06/19/t-shirts-feminism-parenting-and-data-science-part-1-colors/"&gt;Part
1&lt;/a&gt;
first.)&lt;/p&gt;
&lt;p&gt;When last we left off, we'd built a model using &lt;em&gt;shirt colors&lt;/em&gt; to
predict boy-ness / girl-ness.&lt;/p&gt;
&lt;p&gt;Our second attempt will involve the shirt images themselves (sort of).
For our purposes, computer images are made up of
&lt;a href="https://en.wikipedia.org/wiki/Pixel"&gt;pixels&lt;/a&gt;, each of whose color is …&lt;/p&gt;</summary><content type="html">&lt;p&gt;(You might want to read &lt;a href="https://joelgrus.com/2013/06/19/t-shirts-feminism-parenting-and-data-science-part-1-colors/"&gt;Part
1&lt;/a&gt;
first.)&lt;/p&gt;
&lt;p&gt;When last we left off, we'd built a model using &lt;em&gt;shirt colors&lt;/em&gt; to
predict boy-ness / girl-ness.&lt;/p&gt;
&lt;p&gt;Our second attempt will involve the shirt images themselves (sort of).
For our purposes, computer images are made up of
&lt;a href="https://en.wikipedia.org/wiki/Pixel"&gt;pixels&lt;/a&gt;, each of whose color is
determined by specifying &lt;a href="https://en.wikipedia.org/wiki/Rgb#Numeric_representations"&gt;red, green, and blue
values&lt;/a&gt;
between 0 and 255. So if you have an image with &lt;em&gt;N&lt;/em&gt; pixels, you can
think of it as a point in 3&lt;em&gt;N&lt;/em&gt;-dimensional space, all of whose
coordinates lie between 0 and 255.&lt;/p&gt;
&lt;p&gt;And as before, we can build a linear model to classify points in space
using logistic regression. The trick here is that the images have
different sizes (and hence different numbers of pixels). So as a first
step, we'll rescale every image to 138 pixels x 138 pixels = 19,044
pixels. (A lot of our images are this size, and the rest are mostly
larger, which is why I chose it.) This will give us a representation of
each t-shirt image as a point in 57,132-dimensional space. (Visualizing
57,132-dimensional space is tricky, so don't feel bad if you can't do
it.)&lt;/p&gt;
&lt;p&gt;Our dataset only contains about 1,000 shirts, which means that a
57,000-dimensional classifier would learn to &lt;em&gt;identify&lt;/em&gt; every shirt in
the test dataset rather than figure out what distinguishes the boys
shirts from the girls shirts. This means we need to do some sort of
&lt;a href="https://en.wikipedia.org/wiki/Dimensionality_reduction"&gt;dimensionality
reduction&lt;/a&gt; to
get our t-shirt images into a much lower-dimensional space.&lt;/p&gt;
&lt;p&gt;Here we'll use &lt;a href="https://en.wikipedia.org/wiki/Principal_component_analysis"&gt;Principal Component
Analysis&lt;/a&gt;,
which finds the direction (in 57,132-dimensional space) that accounts
for the largest amount of variance in the dataset. It then subtracts out
this direction, finds the most-variant-direction of the new dataset, and
so on, until it has enough components.&lt;/p&gt;
&lt;p&gt;(As always, code is on &lt;a href="https://github.com/joelgrus/shirts"&gt;GitHub&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;I ended up using 10 components, which gives a representation of each
t-shirt as just 10 numbers, representing the projection of the
(57,132-dimensional representation of the) shirt onto the first 10
principal components, each of which is itself a vector in
57,132-dimensional space. For instance, the first principal component is
the 57,132-element vector&lt;/p&gt;
&lt;p&gt;[0.0002334, 0.00029256, 0.00042805, ... , 0.00051605]&lt;/p&gt;
&lt;p&gt;By thinking of this as a vector of 19,044 rgb triplets, and by rescaling
it so that its smallest component is 0 and its largest component 255, we
can convert it into an image of an &lt;em&gt;eigenshirt&lt;/em&gt; representing the
"essence" of this component. Shirts with a large value for the first
component will tend to be "similar" to this eigenshirt. Shirts with a
large negative value for the first component will tend to be "similar"
to its color-inverted "anti-eigenshirt". [We could have just as easily
picked the "anti-eigenshirt" as the eigenshirt and flipped the signs of
the components.]&lt;/p&gt;
&lt;p&gt;The below table shows, for each of the 10 principal components, the
eigenshirt, the shirt with the largest component value, the shirt with
the closest-to-zero value, the shirt with the largest &lt;em&gt;negative&lt;/em&gt;
component value, and the "anti-eigenshirt".&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Eigenshirt&lt;/th&gt;&lt;th&gt;Most Eigenshirty&lt;/th&gt;&lt;th&gt;Not Eigenshirty&lt;/th&gt;&lt;th&gt;Most Anti-Eigenshirty&lt;/th&gt;&lt;th&gt;Anti-Eigenshirt&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/0_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/0_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/0_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/0_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/0_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;https://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.com
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/1_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/1_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/1_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/1_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/1_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;https://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.com
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/2_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/2_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/2_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/2_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/2_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;https://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.com
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/3_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/3_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/3_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/3_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/3_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;https://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.com
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/4_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/4_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/4_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/4_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/4_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;https://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.comhttps://joelgrus.com
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/5_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/5_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/5_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/5_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/5_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/6_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/6_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/6_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/6_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/6_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/7_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/7_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/7_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/7_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/7_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/8_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/8_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/8_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/8_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/8_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/9_eigenshirt.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/9_most.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/9_none.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/9_least.png"&gt;&lt;/td&gt;&lt;td&gt;&lt;img width="140" src="https://joelgrus.com/wp-content/uploads/2013/06/9_inverted_eigenshirt.png"&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;If I were to try to give qualitative descriptions of these ten
components, I guess they would be something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Component 0: White -\&gt; Black&lt;/li&gt;
&lt;li&gt;Component 1: Orange -\&gt; Blue&lt;/li&gt;
&lt;li&gt;Component 2: Dark sleeved / white sleeveless -\&gt; White sleeved / dark
sleeveless&lt;/li&gt;
&lt;li&gt;Component 3: Wide dark / narrow white -\&gt; Narrow dark / wide white&lt;/li&gt;
&lt;li&gt;Component 4: ?&lt;/li&gt;
&lt;li&gt;Component 5: Green -\&gt; Purple&lt;/li&gt;
&lt;li&gt;Component 6: White trim / dark shirt -\&gt; Dark trim / white shirt&lt;/li&gt;
&lt;li&gt;Component 7: https://joelgrus.comwhite sleeveless -\&gt; White long sleeve
/ dark sleeveless&lt;/li&gt;
&lt;li&gt;Component 8: White shirt / dark print -\&gt; Dark shirt / white print&lt;/li&gt;
&lt;li&gt;Component 9: ?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Principal Component representation of each shirt is a 10-dimensional
vector representing (roughly) where it fits on each of these spectra.
For instance, the monkey shirt&lt;/p&gt;
&lt;p&gt;&lt;img alt="monkey_shirt" src="https://joelgrus.com/wp-content/uploads/2013/06/monkey_shirt.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;is represented by the vectorhttps://joelgrus.com&lt;/p&gt;
&lt;p&gt;[ -9313, 10067, -149, -4013, -2147, 1574, -296, -954, 1729, -196]&lt;/p&gt;
&lt;p&gt;the biggest components of which are "orange" (eigenshirt #1), "dark"
(anti-eigenshirt 0), and "narrow" (anti-eigenshirt 3).&lt;/p&gt;
&lt;p&gt;If we try to reconstruct the image using just these ten components, we
get&lt;/p&gt;
&lt;p&gt;&lt;img alt="monkey_shirt_reconstructed" src="https://joelgrus.com/wp-content/uploads/2013/06/monkey_shirt_reconstructed.png" /&gt;&lt;/p&gt;
&lt;p&gt;which seems to have captured &lt;em&gt;orange&lt;/em&gt;, &lt;em&gt;short sleeve&lt;/em&gt;, and &lt;em&gt;dark
graphic&lt;/em&gt;. You certainly can't tell it's a monkey, though.&lt;/p&gt;
&lt;h2&gt;Predicting&lt;/h2&gt;
&lt;p&gt;If we try to predict "boy shirt or girl shirt" using just these 10
components, we get a model that's 93% accurate on the test set. The
coefficients (multiplied by 10,000, since they're small) look like:&lt;/p&gt;
&lt;p&gt;Component 0: -2.71 (eigenshirt is girlish)\
 Component 1: -2.56 (girlish)\
 Component 2: 3.55 (boyish)\
 Component 3: 0.53 (weakly boyish)\
 Component 4: -0.56 (https://joelgrus.com
 Component 5: 5.43 (boyish)\
 Component 6: -15.9 (very girlish)\
 Component 7: -4.68 (girlish)\
 Component 8: 2.73 (boyish)\
 Component 9: -2.14 (girlish)&lt;/p&gt;
&lt;p&gt;As before, we can look at how the shirts are distributed as a function
of the score they get from the model:&lt;/p&gt;
&lt;p&gt;![shirts_by_scorhttps://joelgrus.comcom/wp-content/uploads/2013/06/shirts_by_score1.png)&lt;/p&gt;
&lt;p&gt;The miscategorized shirts generally have low (close to 0) scores, except
for one particularly "girly" boys shirt that we'll see below.
https://joelgrus.com
Superlatives&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;*&lt;em&gt;Girliest Girl&lt;/em&gt;https://joelgrus.comsed on shape and colors)&lt;/p&gt;
&lt;p&gt;&lt;img alt="girliest_girl" src="https://joelgrus.com/wp-content/uploads/2013/06/girliest_girl.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Girliest Boy&lt;/strong&gt; https://joelgrus.comgain)&lt;/p&gt;
&lt;p&gt;&lt;img alt="girliest_boy" src="https://joelgrus.com/wp-content/uploads/2013/06/girliest_boy.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Boyiest Boy&lt;/strong&gt; (da Bears)&lt;/p&gt;
&lt;p&gt;&lt;img alt="boyiest_boy" src="https://joelgrus.com/wp-content/uploads/2013/06/boyiest_boy.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Boyiest Girl&lt;/strong&gt; (same one as last time!)&lt;/p&gt;
&lt;p&gt;&lt;img alt="boyiest_girl" src="https://joelgrus.com/wp-content/uploads/2013/06/boyiest_girl.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;This is all very interesting and hints at &lt;a href="https://en.wikipedia.org/wiki/Platonic_idealism"&gt;Platonic
ideal&lt;/a&gt; shirts (the
philosophical details of which are out of scope for this blog). And
clearly it does a much better job of predicting "boy shirt or girl
shirt" than our previous color-based attempt. But whereas everyone knows
about colors (except for the color-blind, of course), most people are
unfamiliar with "eigenshirts" and will accuse you of having made them up
just in order to have something to blog about. In particular, the girl
who works at Gap Kids was entirely unimpressed with this model, and said
that I needed to either buy something or leave the store.&lt;/p&gt;
&lt;p&gt;Were I really committed to this model, I'd probably do more work to get
the images comparable to each other so that not only were they the same
size but the shirts were oriented as closely as possible and all had the
same background color. Alas, I'm sort of principal-componented-out, and
am eager to get back to writing my blog post about "the only correct way
to interview engineers", the punch-line of which is that you should only
ask questions that involve golf balls, piano tuners, counterfeit coins,
airplanes, treadmills, or piano tuners.&lt;/p&gt;
&lt;p&gt;And so we leave things until part 3, "Shirt Language Processing", which
will be forthcoming at some point after I muster up the motivation to
either transcribe the shirt images or find an intern to do it for me.&lt;/p&gt;</content><category term="old posts"></category><category term="Uncategorized"></category></entry><entry><title>T-Shirts, Feminism, Parenting, and Data Science, Part 1: Colors</title><link href="https://joelgrus.com/2013/06/19/t-shirts-feminism-parenting-and-data-science-part-1-colors/" rel="alternate"></link><published>2013-06-19T06:20:00+02:00</published><updated>2013-06-19T06:20:00+02:00</updated><author><name>joelgrus</name></author><id>tag:joelgrus.com,2013-06-19:/2013/06/19/t-shirts-feminism-parenting-and-data-science-part-1-colors/</id><summary type="html">&lt;p&gt;Before I was a parent I never gave much thought to children's clothing,
other than to covet a few of the &lt;a href="http://www.tshirthell.com/funny-shirts/now-that-im-safe-im-pro-choice/"&gt;baby
shirts&lt;/a&gt;
at T-Shirt Hell. Now that I have a two-year-old daughter, I have trouble
thinking of anything &lt;em&gt;but&lt;/em&gt; children's clothing. (Don't tell my boss!)&lt;/p&gt;
&lt;p&gt;What I have discovered …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Before I was a parent I never gave much thought to children's clothing,
other than to covet a few of the &lt;a href="http://www.tshirthell.com/funny-shirts/now-that-im-safe-im-pro-choice/"&gt;baby
shirts&lt;/a&gt;
at T-Shirt Hell. Now that I have a two-year-old daughter, I have trouble
thinking of anything &lt;em&gt;but&lt;/em&gt; children's clothing. (Don't tell my boss!)&lt;/p&gt;
&lt;p&gt;What I have discovered over the last couple of years, is that clothing
intended for boys is
&lt;a href="http://www.gap.com/browse/category.do?cid=35792#department=166"&gt;fun&lt;/a&gt;,
whereas clothing intended for girls &lt;a href="http://www.gap.com/browse/category.do?cid=35932"&gt;kind of
sucks&lt;/a&gt;. There's nothing
inherently two-year-old-boy-ish about dinosaurs, surfing ninjas,
skateboarding globes, or "become-a-robot" solicitations, just as there's
nothing inherently two-year-old-girl-ish about pastel-colored balloons,
or cats wearing bows, or dogs wearing bows, or ruffles. Forget about
gender, I want Madeline to grow up to be a "surfing ninja" kind of kid,
not a "cats wearing bows" kind of kid. An "angry skateboarding dog" kind
of kid, not a "shoes with pretty ribbons" kind of kid.&lt;/p&gt;
&lt;p&gt;Accordingly, I have taken to buying all of Madeline's shirts in the boys
section, the result of (her boy-ish haircut and) which is that half the
time people refer to her as "he". This doesn't terribly bother me,
especially if she ends up getting the gender wage premium that people
are always yammering about on Facebook, but it makes me wonder why such
a stark divide between toddler boy shirts and toddler girl shirts. And,
of course, it makes me wonder if the divide is so stark that I can build
a model to predict it!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Dataset&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I downloaded images of every "toddler boys" and "toddler girls" t-shirt
from Carters, Children's Place, Crazy 8, Gap Kids, Gymboree, Old Navy,
and Target. Because each one had their shirts at a different (random)
website location, I decided that using an &lt;a href="https://chrome.google.com/webstore/detail/image-downloader/cnpniohnfphhjihaiiggeabnkjhpaldj"&gt;Image
Downloader&lt;/a&gt;
Chrome extension would be quicker and easier than writing a scraping
script that worked with all the different sites.&lt;/p&gt;
&lt;p&gt;I ended up with 616 images of boys shirts and 446 images of girls
shirts. My lawyer has advised me against redistributing the dataset,
although I might if you ask nicely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attempt #1: Colors&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;(As always, the code is on &lt;a href="https://github.com/joelgrus/shirts"&gt;my
GitHub&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;A quick glance at the shirts revealed that boys shirts tend toward
boy-ish colors, girls shirts toward girl-ish colors. So a simple model
could just take into account the colors in the image. I've never done
much image processing before, so the
&lt;a href="https://pypi.python.org/pypi/Pillow/2.0.0"&gt;Pillow&lt;/a&gt; Python library
seemed like a friendly place to start. (In retrospect, a library that
made at least a half-hearted attempt at documentation would probably
have been friendlier.)&lt;/p&gt;
&lt;p&gt;The PIL library has a
&lt;a href="http://www.pythonware.com/library/pil/handbook/image.htm"&gt;getcolors&lt;/a&gt;
function, that returns a list of&lt;/p&gt;
&lt;p&gt;(# of pixels, (red, green, blue))&lt;/p&gt;
&lt;p&gt;for each rgb color in the image. This gives 256 * 256 * 256 = almost
17 million possible colors, which is probably too many, so I &lt;em&gt;quantized&lt;/em&gt;
the colors by bucketing each of red, green, and blue into either [0,85),
[85,170) or [170,255]. This gives 3 * 3 * 3 = 27 possible colors.&lt;/p&gt;
&lt;p&gt;To make things even simpler, I only cared about whether an image
contained at least one pixel of a given color [bucket] or whether it
contained none. This allowed me to convert each image into an array of
length 27 consisting only of 0's and 1's.&lt;/p&gt;
&lt;p&gt;Finally, I trained a logistic regression model to predict, based solely
on the presence or absence of the 27 colors, whether a shirt was a boys
shirt or a girls shirt. Without getting too mathematical, we end up with
a weight (positive or negative) for each of the 27 colors. Then for any
shirt, we add up the weights for all the colors in the shirt, and if
that total is positive, we predict "boys shirt", and if that total is
negative, we predict "girls shirt".&lt;/p&gt;
&lt;p&gt;I trained the model on 80% of the data and measured its performance on
the other 20%. This (pretty stupid) model predicted correctly about 77%
of the time.&lt;/p&gt;
&lt;p&gt;Plotted below is the number of boys shirts (blue) and girls shirts
(pink) in the test set by the score assigned them in the model. Without
getting into &lt;a href="https://en.wikipedia.org/wiki/Logistic_regression#As_a_generalized_linear_model"&gt;gory
details&lt;/a&gt;,
a score of 0 means the model thinks it's equally likely to be a boys
shirt or a girls shirt, with more positive scores meaning more likely
boys shirt and more negative scores meaning more likely girls shirt. You
can see that while there's a muddled middle, when the model is really
confident (in either direction), it's &lt;em&gt;always&lt;/em&gt; right.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://joelgrus.com/wp-content/uploads/2013/06/shirts_by_score.png"&gt;&lt;img alt="shirts_by_score" src="https://joelgrus.com/wp-content/uploads/2013/06/shirts_by_score-300x190.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If we dig into precision and recall, we see&lt;/p&gt;
&lt;p&gt;P(is actually girl shirt | prediction is "girl shirt") = 75%\
 P(is actually boy shirt | prediction is "boy shirt") = 77%\
 P(prediction is "girl shirt" | is actually girl shirt) = 63%\
 P(prediction is "boy shirt" | is actually boy shirt) = 86%&lt;/p&gt;
&lt;p&gt;One way of interpreting the recall discrepancy is that it's much more
likely for girls shirts to have "boy colors" than for boys shirts to
have "girl colors", which indeed appears to be the case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Superlatives&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Given this model, we can identify&lt;/p&gt;
&lt;p&gt;The Girliest Girls Shirt (no argument from me):&lt;/p&gt;
&lt;p&gt;&lt;img alt="girliest_girl_shirt" src="https://joelgrus.com/wp-content/uploads/2013/06/girliest_girl_shirt.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The Boyiest Girls Shirt (must be the black-and-white and lack of
color?):&lt;/p&gt;
&lt;p&gt;&lt;img alt="boyiest_girl_shirt" src="https://joelgrus.com/wp-content/uploads/2013/06/boyiest_girl_shirt.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The Girliest Boys Shirt (I can see that if you just look at colors):&lt;/p&gt;
&lt;p&gt;&lt;img alt="girliest_boy_shirt" src="https://joelgrus.com/wp-content/uploads/2013/06/girliest_boy_shirt.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The Boyiest Boys Shirt (a slightly odd choice, but I guess those are all
boy-ish colors?):&lt;/p&gt;
&lt;p&gt;&lt;img alt="boyiest_boy_shirt" src="https://joelgrus.com/wp-content/uploads/2013/06/boyiest_boy_shirt.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The Most Androgynous Shirt (this one is most likely some kind of image
compression artifact, the main colors are boyish but the image also has
some girlish purple pixels in it that cancel those out):&lt;/p&gt;
&lt;p&gt;&lt;img alt="most_androgynous" src="https://joelgrus.com/wp-content/uploads/2013/06/most_androgynous.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The Blandest Shirt (for sure!):&lt;/p&gt;
&lt;p&gt;&lt;img alt="blandest" src="https://joelgrus.com/wp-content/uploads/2013/06/blandest.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The Most Colorful Shirt (no argument with this one either!):&lt;/p&gt;
&lt;p&gt;&lt;img alt="coloriest" src="https://joelgrus.com/wp-content/uploads/2013/06/coloriest.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scores for Colors&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;By looking at the
&lt;a href="https://en.wikipedia.org/wiki/Logistic_regression#Coefficients"&gt;coefficients&lt;/a&gt;
of the model, we can see precisely which colors are the most "boyish"
and which are the most "girlish". The results are not wholly unexpected:&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:151.71px;background-color:rgb(42,42,42)"&gt;

151.71
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:80.68px;background-color:rgb(42,42,127)"&gt;

80.68
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:69.35px;background-color:rgb(212,42,42)"&gt;

69.35
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:49.69px;background-color:rgb(42,127,42)"&gt;

49.69
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:43.83px;background-color:rgb(42,42,212)"&gt;

43.83
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:40.99px;background-color:rgb(127,127,42)"&gt;

40.99
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:35.94px;background-color:rgb(127,42,42)"&gt;

35.94
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:30.56px;background-color:rgb(127,127,212)"&gt;

30.56
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:26.08px;background-color:rgb(212,212,127)"&gt;

26.08
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:24.06px;background-color:rgb(42,127,212)"&gt;

24.06
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:20.89px;background-color:rgb(127,212,127)"&gt;

20.89
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:20.49px;background-color:rgb(212,127,42)"&gt;

20.49
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:18.89px;background-color:rgb(42,127,127)"&gt;

18.89
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:17.67px;background-color:rgb(127,42,127)"&gt;

17.67
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;td width="50%"&gt;
&lt;div style="width:1.29px;background-color:rgb(127,127,127)"&gt;

1.29
&lt;/td&gt;

&lt;/div&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:17.37px;background-color:rgb(212,212,212)"&gt;

-17.37
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:21.77px;background-color:rgb(212,127,127)"&gt;

-21.77
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:29.95px;background-color:rgb(42,212,42)"&gt;

-29.95
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:49.91px;background-color:rgb(42,212,212)"&gt;

-49.91
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:56.4px;background-color:rgb(127,212,212)"&gt;

-56.4
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:66.77px;background-color:rgb(212,212,42)"&gt;

-66.77
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:69.52px;background-color:rgb(42,212,127)"&gt;

-69.52
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:70.15px;background-color:rgb(127,212,42)"&gt;

-70.15
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:82.17px;background-color:rgb(212,127,212)"&gt;

-82.17
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:119.1px;background-color:rgb(127,42,212)"&gt;

-119.1
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:175.2px;background-color:rgb(212,42,212)"&gt;

-175.2
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;
&lt;div
style="text-align:right;float:right;width:224.74px;background-color:rgb(212,42,127)"&gt;

-224.74
&lt;/td&gt;

&lt;/div&gt;

&lt;td width="50%"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;In Conclusion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In conclusion, by looking only at &lt;em&gt;which of 27 colors are present in a
toddler t-shirt&lt;/em&gt;, we can do a surprisingly good job of predicting
whether it's a boys shirt or a girls shirt. And that pretty clearly
involves throwing away lots of information. What if we were to take more
of the actual image into account?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Coming soon, Part 2: EIGENSHIRTS&lt;/strong&gt;&lt;/p&gt;</content><category term="old posts"></category><category term="Data"></category><category term="Joelene"></category><category term="Life"></category><category term="Parenting"></category><category term="Science"></category></entry></feed>