<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-07-18T02:26:05+00:00</updated><id>/feed.xml</id><title type="html">A Free Education</title><subtitle>Thoughts from a free education in CS.</subtitle><entry><title type="html">Vibe Coding</title><link href="/llm/2025/07/14/coding-with-agents.html" rel="alternate" type="text/html" title="Vibe Coding" /><published>2025-07-14T05:00:30+00:00</published><updated>2025-07-14T05:00:30+00:00</updated><id>/llm/2025/07/14/coding-with-agents</id><content type="html" xml:base="/llm/2025/07/14/coding-with-agents.html"><![CDATA[<p>As far as I know, the term “vibe coding” was coined by Karpathy, referring to the practice of relying on LLM-based AI agents to code on one’s behalf. The folks at <a href="https://changelog.com/podcast">Changelog</a> have done a number of great podcast episodes on the topic. in my experience working on personal projects, the latest generation of agents like Claude Code, Gemini CLI, and Sourcegraph Amp deliver amply on the hype.</p>

<p>Before I forget, some observations / speculations below.</p>

<h1 id="human-friendly--agent-friendly">Human-Friendly == Agent-Friendly?</h1>

<p>I’ve been working on a client-server project with Elm and Rust. Elm features, famously, one of the friendliest compilers around, from which the Rust compiler drew inspiration (at least as far as compiler output goes). Although Elm’s training corpus is relatively small compared to other languages, Claude Code has no problems with it. In part, this seems to be because the compiler helps debug issues after each round of edits.</p>

<p>Will compiler output become even more important in the era of agents? One can imagine an interesting A/B test here, e.g. by suppressing all output other than build status for the case while preserving full output for the control. On a related note, will the static vs dynamic typing pendulum be swung by agents?</p>

<h1 id="business-analysts-ftw">Business Analysts FTW</h1>

<p>At some point, I was a business analyst (BA) in the technical job sense, collecting business inputs and writing requirements specifications. “The system shall do X but not Y… for the avoidance of doubt, it should not do Z.”</p>

<p>Good BAs, armed with a healthy degree of skepticism and neuroticism to consider all the possible ambiguities of business ideas and language, produce reasonably complete specs. The thought process is similar to a developer seeking to ensure pattern matching captures all possible inputs. Presumably, good BAs are also good vibe coders?</p>

<p>Interestingly, I find Claude takes my paragraphs of specs and produces very good, concise summaries of the task at hand. It feels like the agent understands…</p>

<h1 id="tight-feedback-loops-matter-mode">Tight Feedback Loops Matter (Mode)</h1>

<p>As the code base grows, the agent tends to write more than it deletes. I’ve found it useful to give it feedback (or force it to introspect) in every possible way, including:</p>

<ul>
  <li>prompt the agent to review and refactor code for clarity and simplicity</li>
  <li>if you have any intuition about problematic areas, ask the agent about it</li>
  <li>have the agent develop some scripts for humans to verify system functionality (e.g. trace a path through the system); then ask the agent to use the scripts</li>
  <li>run tests, hand-written or agent-written</li>
  <li>make it build frequently and with standard linting and review tools like <code class="language-plaintext highlighter-rouge">clippy</code> in Rust</li>
</ul>

<p>Although vibe coding can feel scary, the fundamental problem of determining if things are working correctly is no different from that of a sufficiently complex codebase. Since you can’t read all the code or hold it in memory, what do you do?</p>

<h1 id="what-doesnt-work">What Doesn’t Work?</h1>

<p>UI development remains tricky. Despite tools like <code class="language-plaintext highlighter-rouge">puppeteer</code> that enable agents to examine the browser visually, I’ve found that hand-crafting is often more productive for designing and reasoning about layouts.</p>

<p>The only time all agents got stuck was when a previously generated long block of CSS was overriding layouts specified elsewhere. No matter what the agent did, the output remained incorrect and unchanging. I’m not sure whether to attribute this to UI per se. The human intuition goes something like “since we’ve tried everything including simple changes, the problem must lie somewhere else”… which seems like logic well within reach of the current generation of LLMs. Practically, the agent wasn’t searching for the problematic CSS, which was in a file rarely touched.</p>

<p>As we gain more collective experience in developing and managing agents, what kind of new best practices should we expect to emerge?</p>]]></content><author><name></name></author><category term="llm" /><category term="ai" /><category term="llm" /><category term="agents" /><summary type="html"><![CDATA[As far as I know, the term “vibe coding” was coined by Karpathy, referring to the practice of relying on LLM-based AI agents to code on one’s behalf. The folks at Changelog have done a number of great podcast episodes on the topic. in my experience working on personal projects, the latest generation of agents like Claude Code, Gemini CLI, and Sourcegraph Amp deliver amply on the hype.]]></summary></entry><entry><title type="html">Say Scripting with LLMs</title><link href="/accessibility/2025/02/23/say-scripting_with_llms.html" rel="alternate" type="text/html" title="Say Scripting with LLMs" /><published>2025-02-23T05:00:30+00:00</published><updated>2025-02-23T05:00:30+00:00</updated><id>/accessibility/2025/02/23/say-scripting_with_llms</id><content type="html" xml:base="/accessibility/2025/02/23/say-scripting_with_llms.html"><![CDATA[<p>How does one get the terminal to speak output?</p>

<p>There are various ways to achieve the goal with accessibility tools (e.g. screen readers like VoiceOver on macOS, or libraries like <a href="https://github.com/tvraman/emacspeak">emacspeak</a>). But suppose we want something simple…</p>

<h2 id="say">Say</h2>

<p>On macOS, the built-in text-to-speech (TTS) utility is <code class="language-plaintext highlighter-rouge">say</code>:</p>

<p><code class="language-plaintext highlighter-rouge">say hello</code></p>

<p>… says hello. There are a few simple flags to control the speech rate, voice, etc.</p>

<p>To print command results to the terminal while also calling <code class="language-plaintext highlighter-rouge">say</code>, we can use <code class="language-plaintext highlighter-rouge">tee</code> to duplicate the input:</p>

<p><code class="language-plaintext highlighter-rouge">date | tee /dev/tty | say</code></p>

<p>An alias like <code class="language-plaintext highlighter-rouge">alias dates='date | tee /dev/tty | say'</code> can be convenient, but it doesn’t take additional arguments to the original command. Alternatively, we can define multiple aliases as functions (e.g. as a script to call from <code class="language-plaintext highlighter-rouge">~/.zshrc</code>).</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/zsh</span>

<span class="nb">typeset </span><span class="nv">SPEECH_RATE</span><span class="o">=</span>300
<span class="nb">typeset</span> <span class="nt">-A</span> saycommands
<span class="nv">saycommands</span><span class="o">=(</span>
    lss <span class="s2">"ls -1"</span>
    pwds <span class="s2">"pwd"</span>
    dates <span class="s2">"date"</span>
<span class="o">)</span>

<span class="k">for </span>key <span class="k">in</span> <span class="k">${</span><span class="p">(k)saycommands</span><span class="k">}</span><span class="p">;</span> <span class="k">do
    </span><span class="nb">command</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">saycommands</span><span class="p">[</span><span class="nv">$key</span><span class="p">]</span><span class="k">}</span><span class="s2">"</span>
    <span class="nb">eval</span> <span class="s2">"
        function </span><span class="k">${</span><span class="nv">key</span><span class="k">}</span><span class="s2"> {
            </span><span class="k">${</span><span class="nv">command</span><span class="k">}</span><span class="s2"> </span><span class="se">\"\$</span><span class="s2">@</span><span class="se">\"</span><span class="s2"> | tee /dev/tty | say -r </span><span class="se">\$</span><span class="s2">{SPEECH_RATE}
        }
    "</span>
<span class="k">done

</span><span class="nb">echo</span> <span class="s2">"Say-enabled commands: </span><span class="k">${</span><span class="p">(k)saycommands</span><span class="k">}</span><span class="s2">"</span>
</code></pre></div></div>

<h2 id="say-mode-asking-an-llm">Say Mode? Asking an LLM</h2>

<p>For a more general solution, maybe we want the terminal to always speak output. And for non-experts in zshell, maybe we want help from an LLM.</p>

<p>The LLMs tried are: (1) ChatGPT 4o in the macOS desktop app: (2) Mistral’s default model in the web UI. In both cases, the initial prompt is simple: “In MacOS ZSH, I want to intercept every shell output and also speak it aloud with <code class="language-plaintext highlighter-rouge">say -r 300</code>”.</p>

<p>Interestingly, ChatGPT didn’t generate working code even after a few iterations, while Mistral quickly got something working, albeit a bit buggy. This is Mistral’s initial working version, which speaks the output once but prints it three times: <a href="https://gist.github.com/tkuriyama/ef28d12e496b8670d5bee74f787f4f9a">link to gist</a>.</p>

<p>I didn’t spend too much time engineering the prompts, but toggling back and forth between ChatGPT and Mistral, I found that:</p>
<ul>
  <li>neither model could fully and independently resolve the triple-printing issue</li>
  <li>both models are better at debugging than generating code</li>
  <li>the differential diagnosis between models (i.e. comparing their outputs) is useful</li>
</ul>

<p>Overall, the results were roughly on par with expectations. For a modest task, the LLMs can do a good draft, allowing the human programmer to focus on refining the results. (Also, the time-savings were meaningful in this instance, since I know very little about shell/zshell scripting). And although by no means a rigorous comparison, I preferred the speed and quality of Mistral.</p>

<p>This is the final, working version, which allows say mode to be toggled in the terminal with <code class="language-plaintext highlighter-rouge">saymode on</code> and <code class="language-plaintext highlighter-rouge">saymode off</code> and includes a prompt indicator. It’s not particularly elegant and lacks some obvious usability features, but it works as a proof of concept.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/zsh</span>

<span class="nb">typeset</span> <span class="nt">-g</span> <span class="nv">SAYMODE_ENABLED</span><span class="o">=</span><span class="nb">false
typeset</span> <span class="nt">-g</span> <span class="nv">LAST_COMMAND_OUTPUT</span><span class="o">=</span><span class="s2">""</span>
<span class="nb">typeset</span> <span class="nt">-g</span> <span class="nv">DEFAULT_PROMPT</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PROMPT</span><span class="s2">"</span>

<span class="c">################################################################################</span>


<span class="k">function </span>speak_last_output<span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="nv">$SAYMODE_ENABLED</span> <span class="o">==</span> <span class="nb">true</span> <span class="o">&amp;&amp;</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$LAST_COMMAND_OUTPUT</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$LAST_COMMAND_OUTPUT</span><span class="s2">    "</span> | say <span class="nt">-r</span> 300
        <span class="nv">LAST_COMMAND_OUTPUT</span><span class="o">=</span><span class="s2">""</span>
    <span class="k">fi</span>
<span class="o">}</span>

<span class="k">function </span>capture_output<span class="o">()</span> <span class="o">{</span>
    <span class="nv">LAST_COMMAND_OUTPUT</span><span class="o">=</span><span class="si">$(</span><span class="nb">eval</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> 2&gt;&amp;1<span class="si">)</span>
    <span class="k">return</span> <span class="k">${</span><span class="nv">PIPESTATUS</span><span class="p">[0]</span><span class="k">}</span>
<span class="o">}</span>

<span class="c">################################################################################</span>

<span class="k">function </span>preexec<span class="o">()</span> <span class="o">{</span>
    <span class="c"># Check if the command is not empty</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> <span class="nv">$SAYMODE_ENABLED</span> <span class="o">==</span> <span class="nb">true</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>capture_output <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
        <span class="c"># Prevent the original command from being executed</span>
        <span class="k">return </span>1
    <span class="k">fi</span>
<span class="o">}</span>

preexec_functions+<span class="o">=(</span>preexec<span class="o">)</span>
precmd_functions+<span class="o">=(</span>speak_last_output<span class="o">)</span>

<span class="c">################################################################################</span>

<span class="k">function </span>saymode_on<span class="o">()</span> <span class="o">{</span>
    <span class="nv">SAYMODE_ENABLED</span><span class="o">=</span><span class="nb">true
    </span><span class="nv">PROMPT</span><span class="o">=</span><span class="s2">"%K{green}SAYMODE%k </span><span class="nv">$DEFAULT_PROMPT</span><span class="s2">"</span>
    <span class="nb">echo</span> <span class="s2">"Say mode enabled."</span>
<span class="o">}</span>

<span class="c"># Define the vmode off command</span>
<span class="k">function </span>saymode_off<span class="o">()</span> <span class="o">{</span>
    <span class="nv">PROMPT</span><span class="o">=</span><span class="s2">"</span><span class="nv">$DEFAULT_PROMPT</span><span class="s2">"</span>
    <span class="nb">echo</span> <span class="s2">"Say mode disabled."</span>
    <span class="nv">SAYMODE_ENABLED</span><span class="o">=</span><span class="nb">false</span>
<span class="o">}</span>

<span class="k">function </span>saymode<span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"on"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>saymode_on
    <span class="k">elif</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"off"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>saymode_off
    <span class="k">else
        </span><span class="nb">echo</span> <span class="s2">"Usage: vmode {on|off}"</span>
    <span class="k">fi</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="can-you-do-better">Can You Do Better?</h2>

<p>Some usability improvements that come to mind:</p>

<ul>
  <li>output can be automatically Limited in length</li>
  <li>commands can be whitelisted or blacklisted, since e.g. “vi” or “men” that start a new program or terminal paging mode are not compatible</li>
</ul>

<p>What does a different model like Grok 3 think?</p>

<p><code class="language-plaintext highlighter-rouge">I want you to comment on this Zsh Voice mode program. What are some good usability or feature enhancements to consider?</code></p>

<p>Given a simple prompt like the above, Grok 3 yields an impressive answer.</p>

<ul>
  <li>It identifies and solves both the above points without any specific prompting</li>
  <li>It provides several more suggestions and implements them with lucid explanations</li>
  <li>It identifies several nuances and gotchas, providing clear explanations</li>
</ul>

<p>Overall, it generates a more complex and feature-rich program (<a href="https://gist.github.com/tkuriyama/bac9fb086bb5d56be7f736641af70a1d">copy-paste of the interactive session</a>). The revised script runs immediately without any modifications!</p>

<p>There are some oddities in the shell experience introduced by the more advanced functionality, which Grok 3 couldn’t fully debug. so in the end I reverted to the original, simpler script and asked it to implement only a subset of improvements.</p>

<p>Here is the revised, working script, verbatim from Grok 3 (except for the whitelist of commands).</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Global Variables</span>
<span class="nb">typeset</span> <span class="nt">-g</span> <span class="nv">SAYMODE_ENABLED</span><span class="o">=</span><span class="nb">false</span>              <span class="c"># Tracks whether Say Mode is enabled</span>
<span class="nb">typeset</span> <span class="nt">-g</span> <span class="nv">LAST_COMMAND_OUTPUT</span><span class="o">=</span><span class="s2">""</span>             <span class="c"># Stores the last command's output</span>
<span class="nb">typeset</span> <span class="nt">-g</span> <span class="nv">DEFAULT_PROMPT</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PROMPT</span><span class="s2">"</span>           <span class="c"># Stores the default prompt for restoration</span>
<span class="nb">typeset</span> <span class="nt">-gi</span> <span class="nv">SAYMODE_OUTPUT_LIMIT</span><span class="o">=</span><span class="k">${</span><span class="nv">SAYMODE_OUTPUT_LIMIT</span><span class="k">:-</span><span class="nv">500</span><span class="k">}</span>  <span class="c"># Output limit in characters, default 100</span>
<span class="nb">typeset</span> <span class="nt">-g</span> <span class="nt">-A</span> <span class="nv">SAYMODE_COMMANDS</span><span class="o">=(</span>
    <span class="nb">ls </span>1 <span class="nb">pwd </span>1 <span class="nb">echo </span>1 <span class="nb">date </span>1
    <span class="nb">whoami </span>1 <span class="nb">hostname </span>1 <span class="nb">uname </span>1 which 1 where 1
    <span class="nb">type </span>1 <span class="nb">env </span>1 <span class="nb">printenv </span>1 <span class="nb">alias </span>1
    <span class="nb">du </span>1 <span class="nb">df </span>1 ll 1 la 1 lh 1 
<span class="o">)</span>  <span class="c"># Whitelist of safe commands</span>

<span class="c"># Function to speak the last command output</span>
<span class="k">function </span>speak_last_output<span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="nv">$SAYMODE_ENABLED</span> <span class="o">==</span> <span class="nb">true</span> <span class="o">&amp;&amp;</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$LAST_COMMAND_OUTPUT</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span><span class="nb">local </span>output_to_speak
        <span class="k">if</span> <span class="o">[[</span> <span class="nv">$SAYMODE_OUTPUT_LIMIT</span> <span class="nt">-gt</span> 0 <span class="o">]]</span><span class="p">;</span> <span class="k">then
            </span><span class="nv">output_to_speak</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">LAST_COMMAND_OUTPUT</span>:0:<span class="nv">$SAYMODE_OUTPUT_LIMIT</span><span class="k">}</span><span class="s2">"</span>
        <span class="k">else
            </span><span class="nv">output_to_speak</span><span class="o">=</span><span class="s2">"</span><span class="nv">$LAST_COMMAND_OUTPUT</span><span class="s2">"</span>
        <span class="k">fi
        </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$output_to_speak</span><span class="s2">    "</span> | say <span class="nt">-r</span> 300  <span class="c"># Preserve original rate of 300 wpm</span>
        <span class="nv">LAST_COMMAND_OUTPUT</span><span class="o">=</span><span class="s2">""</span>
    <span class="k">fi</span>
<span class="o">}</span>

<span class="c"># Function to capture command output</span>
<span class="k">function </span>capture_output<span class="o">()</span> <span class="o">{</span>
    <span class="nv">LAST_COMMAND_OUTPUT</span><span class="o">=</span><span class="si">$(</span><span class="nb">eval</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> 2&gt;&amp;1<span class="si">)</span>  <span class="c"># Capture stdout and stderr</span>
    <span class="k">return</span> <span class="k">${</span><span class="nv">PIPESTATUS</span><span class="p">[0]</span><span class="k">}</span>  <span class="c"># Return the exit status of the command</span>
<span class="o">}</span>

<span class="c"># Pre-execution hook to process whitelisted commands</span>
<span class="k">function </span>preexec<span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="nv">$SAYMODE_ENABLED</span> <span class="o">==</span> <span class="nb">true</span> <span class="o">&amp;&amp;</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span><span class="nb">local </span><span class="nv">cmd_name</span><span class="o">=</span><span class="k">${</span><span class="nv">1</span><span class="p">%%[[</span>:space:]]<span class="p">*</span><span class="k">}</span>  <span class="c"># Extract the first word of the command</span>
        <span class="k">if</span> <span class="o">[[</span> <span class="nt">-n</span> <span class="s2">"</span><span class="k">${</span><span class="nv">SAYMODE_COMMANDS</span><span class="p">[</span><span class="nv">$cmd_name</span><span class="p">]</span><span class="k">}</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>  <span class="c"># Check if command is in whitelist</span>
            capture_output <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
            <span class="k">return </span>1  <span class="c"># Prevent original command execution</span>
        <span class="k">fi
    fi</span>
<span class="o">}</span>

<span class="c"># Set up Zsh hooks</span>
preexec_functions+<span class="o">=(</span>preexec<span class="o">)</span>      <span class="c"># Run preexec before each command</span>
precmd_functions+<span class="o">=(</span>speak_last_output<span class="o">)</span>  <span class="c"># Run speak_last_output after each command</span>

<span class="c"># Function to enable Say Mode</span>
<span class="k">function </span>saymode_on<span class="o">()</span> <span class="o">{</span>
    <span class="nv">SAYMODE_ENABLED</span><span class="o">=</span><span class="nb">true
    </span><span class="nv">PROMPT</span><span class="o">=</span><span class="s2">"%K{green}SAYMODE%k </span><span class="nv">$DEFAULT_PROMPT</span><span class="s2">"</span>  <span class="c"># Update prompt to indicate Say Mode</span>
    <span class="nb">echo</span> <span class="s2">"Say mode enabled."</span>
<span class="o">}</span>

<span class="c"># Function to disable Say Mode</span>
<span class="k">function </span>saymode_off<span class="o">()</span> <span class="o">{</span>
    <span class="nv">PROMPT</span><span class="o">=</span><span class="s2">"</span><span class="nv">$DEFAULT_PROMPT</span><span class="s2">"</span>  <span class="c"># Restore original prompt</span>
    <span class="nv">SAYMODE_ENABLED</span><span class="o">=</span><span class="nb">false
    echo</span> <span class="s2">"Say mode disabled."</span>
<span class="o">}</span>

<span class="c"># Main Say Mode control function with toggle</span>
<span class="k">function </span>saymode<span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"on"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>saymode_on
    <span class="k">elif</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"off"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>saymode_off
    <span class="k">elif</span> <span class="o">[[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>  <span class="c"># Toggle if no argument is provided</span>
        <span class="k">if</span> <span class="o">[[</span> <span class="nv">$SAYMODE_ENABLED</span> <span class="o">==</span> <span class="nb">true</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
            </span>saymode_off
        <span class="k">else
            </span>saymode_on
        <span class="k">fi
    else
        </span><span class="nb">echo</span> <span class="s2">"Usage: saymode {on|off}"</span>  <span class="c"># Updated usage message</span>
    <span class="k">fi</span>
<span class="o">}</span>


</code></pre></div></div>]]></content><author><name></name></author><category term="accessibility" /><category term="accessibility" /><category term="llm" /><category term="terminal" /><summary type="html"><![CDATA[How does one get the terminal to speak output?]]></summary></entry><entry><title type="html">Better Search for Elm Packages</title><link href="/general/2021/12/03/elm-package-index.html" rel="alternate" type="text/html" title="Better Search for Elm Packages" /><published>2021-12-03T14:00:00+00:00</published><updated>2021-12-03T14:00:00+00:00</updated><id>/general/2021/12/03/elm-package-index</id><content type="html" xml:base="/general/2021/12/03/elm-package-index.html"><![CDATA[<p>The search function in the <a href="https://package.elm-lang.org/">Elm package index</a> is relatively limited. Most importantly, documentation such as READMEs do not appear to be indexed, limiting the power of keyword search.</p>

<p>Similar to Haskell (perhaps most prominently, Hoogle), there is some ability to <a href="https://klaftertief.github.io/elm-search/">search by types</a>. There has also <a href="https://elm.dmy.fr/">been work done</a> to associated metrics such as downloads to packages. I haven’t seen another project to build a deeper search index, though, so this experiment follows.</p>

<p>The steps to building the better search engine are:</p>

<ol>
  <li>Scrape the relevant data (Python with Selenium)</li>
  <li>Build the index (vector space index with Python generating Elm data files)</li>
  <li>Implement the UI and query logic (Elm)</li>
</ol>

<p>TLDR: <a href="https://tarokuriyama.com/elmsearch/">try the app</a>!</p>

<h2 id="scraping">Scraping</h2>

<p>Since the Elm package index is a single-page application that manipulates the DOM with JavaScript, simple HTTP GETs with tools such as <code class="language-plaintext highlighter-rouge">requests</code> don’t work. (More specifically, they just return the bare-bones HTML page that underlies every Elm application, without any of the actual application data that are rendered by the browser after processing the JavaScript.)</p>

<p>0Selenium is one tool to solve this problem. The <a href="https://selenium-python.readthedocs.io/getting-started.html">basic instructions</a> for installing and getting started with Python 3.x are all that’s required for this use case.</p>

<p>To open a page with the Chrome web driver and capture the browser-rendered HTML source:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">selenium</span> <span class="kn">import</span> <span class="n">webdriver</span>

<span class="n">driver</span> <span class="o">=</span> <span class="n">webdriver</span><span class="p">.</span><span class="n">Chrome</span><span class="p">()</span>
<span class="n">driver</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="n">source</span> <span class="o">=</span> <span class="n">driver</span><span class="p">.</span><span class="n">page_source</span>
<span class="n">driver</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div></div>

<p>Since the index generation will likely require several rounds of iteration, and network IO is expensive (in time, at least), the first step is to take a current snapshot of the package index.</p>

<p><a href="https://github.com/tkuriyama/elm-package-search/tree/master/python-indexer">This Python code</a> follows each link from the Elm package index. If the GitHub source is missing, it skips. Otherwise, it downloads the <code class="language-plaintext highlighter-rouge">README</code>, <code class="language-plaintext highlighter-rouge">About</code>, and each of the exposed module documentation pages into local HTML files.</p>

<p><strong>Scrape Results</strong></p>

<p>THe scraping took a few hours and resulted in ~134MB of data on disk.</p>

<p>Some summary statistics (see <a href="https://github.com/tkuriyama/elm-package-search/blob/master/python-indexer/analysis/Analysis_of_Package_Index.ipynb">Jupyter notebook</a>):</p>

<ul>
  <li>There are 1457 packages, of which 48 (3.3%) are missing GitHub sources</li>
  <li>There are 623 distinct authors; the top 40 authors account for 32% of packages</li>
  <li>Most authors publish 1 or 2 packages (histogram of packages count per author below)</li>
</ul>

<p><img src="/assets/img/elm_packages.png" alt="Elm Package Count by Author Histogram Screenshot" class="img-responsive" /></p>

<p>A few lessons lessons learned along the way:</p>

<ul>
  <li>Even if the code is correct, there can be a variety of reasons for runtime crashes (e.g. computer falling asleep, unexpected interaction between the driver and other browser instances, etc). It’s good to save state aggressively, follow a reproducible (stable) path for getting collections of data, and log progress clearly – so that it’s easier to figure out where to resume from in case of a crash. Network IO is expensive for a single machine, so it’s good to avoid unnecessary repetition of work.</li>
  <li>In my experience, calling <code class="language-plaintext highlighter-rouge">driver.page_source</code> would sometimes yield incomplete pages. Whether a matter of timing or not, sleeping for a second after calling <code class="language-plaintext highlighter-rouge">driver.get</code> made such issues go away. (It looks like there is a <a href="https://stackoverflow.com/questions/60824679/time-sleep-on-chromedriver">better, more explicit way</a> to solve this problem.)</li>
</ul>

<h2 id="building-the-index">Building the Index</h2>

<p>Not having any prior knowledge of the space… the <a href="https://en.wikipedia.org/wiki/Vector_space_model">vector space model</a> appears to provide a simple, classic algorithm for building a search index.</p>

<p>The term-frequency, document-inverse-frequency factor weighting can be tweaked for the domain of packages. Since package dependencies are listed (both in the source <code class="language-plaintext highlighter-rouge">elm.json</code> files as well as the <code class="language-plaintext highlighter-rouge">About</code> page), we can add a term that boosts more widely used packages (i.e. rewards high frequency of dependency).</p>

<p>After extracting and processing all the text from all the downloaded HTML files, the core index implementation looks like the below. <code class="language-plaintext highlighter-rouge">gen_index</code> generates an index for a single document (package) by scoring each tokenized and stemmed word in the document.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">gen_index</span><span class="p">(</span><span class="n">doc_term_map</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="n">PT</span><span class="p">.</span><span class="n">Word</span><span class="p">,</span> <span class="n">Set</span><span class="p">[</span><span class="n">PT</span><span class="p">.</span><span class="n">IndexNum</span><span class="p">]],</span>
              <span class="n">dependency_map</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="n">PT</span><span class="p">.</span><span class="n">IndexNum</span><span class="p">,</span> <span class="n">Count</span><span class="p">],</span>
              <span class="n">i</span><span class="p">:</span> <span class="n">PT</span><span class="p">.</span><span class="n">IndexNum</span><span class="p">,</span>
              <span class="n">words</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">PT</span><span class="p">.</span><span class="n">Word</span><span class="p">]</span>
              <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">PT</span><span class="p">.</span><span class="n">PkgIndex</span><span class="p">:</span>
    <span class="s">"""Generate package index by scoring each word."""</span>

    <span class="n">word_freq</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="n">PT</span><span class="p">.</span><span class="n">Word</span><span class="p">,</span> <span class="n">Count</span><span class="p">]</span> <span class="o">=</span> <span class="n">utils</span><span class="p">.</span><span class="n">count_freq</span><span class="p">(</span><span class="n">words</span><span class="p">)</span>
    <span class="n">total_docs</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">doc_term_map</span><span class="p">)</span>
    <span class="n">pkg_index</span><span class="p">:</span> <span class="n">PT</span><span class="p">.</span><span class="n">PkgIndex</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>

    <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">word_freq</span><span class="p">:</span>
        <span class="n">doc_inverse_freq</span> <span class="o">=</span> <span class="n">get_doc_inverse_freq</span><span class="p">(</span><span class="n">total_docs</span><span class="p">,</span>
                                                <span class="nb">len</span><span class="p">(</span><span class="n">doc_term_map</span><span class="p">[</span><span class="n">word</span><span class="p">]))</span>
        <span class="n">dependency_freq</span> <span class="o">=</span> <span class="n">get_dependency_freq</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">dependency_map</span><span class="p">)</span>
        <span class="n">pkg_index</span><span class="p">[</span><span class="n">word</span><span class="p">]</span> <span class="o">=</span> <span class="n">math</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="n">word_freq</span><span class="p">[</span><span class="n">word</span><span class="p">]</span> <span class="o">*</span>
                                   <span class="n">doc_inverse_freq</span> <span class="o">*</span>
                                   <span class="n">dependency_freq</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">pkg_index</span>


<span class="k">def</span> <span class="nf">get_doc_inverse_freq</span><span class="p">(</span><span class="n">total_docs</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">term_doc_count</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span>
    <span class="s">"""Generate inverse doc frequency score with min of 1.0."""</span>
    <span class="k">return</span> <span class="nb">max</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">math</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="n">total_docs</span> <span class="o">/</span> <span class="n">term_doc_count</span><span class="p">))</span>


<span class="k">def</span> <span class="nf">get_dependency_freq</span><span class="p">(</span><span class="n">i</span><span class="p">:</span> <span class="n">PT</span><span class="p">.</span><span class="n">IndexNum</span><span class="p">,</span>
                        <span class="n">dependency_map</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="n">PT</span><span class="p">.</span><span class="n">IndexNum</span><span class="p">,</span> <span class="n">Count</span><span class="p">]</span>
                        <span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span>
    <span class="s">"""Generate dependency score as log of dependency count with min of 1.0.
    Packages that are used by other packages score higher.
    """</span>
    <span class="k">return</span> <span class="p">(</span><span class="mf">1.0</span> <span class="k">if</span> <span class="n">i</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">dependency_map</span> <span class="k">else</span>
            <span class="nb">max</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">math</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="n">dependency_map</span><span class="p">[</span><span class="n">i</span><span class="p">])))</span>
</code></pre></div></div>

<p><strong>To Stem, or Not To Stem?</strong></p>

<p>The input words to be indexed are pre-processed with tokenization and the <code class="language-plaintext highlighter-rouge">PorterStemmer</code> from <a href="https://www.nltk.org/index.html">NLTK</a>. For plaintext, it makes sense. How about for code?</p>

<p>On the one hand, the code being included in the index consists mainly of function and type names – so it seems like they should perhaps be included literally. On the other hand, the query terms will be stemmed to match the stemmed plaintext, so unstemmed words in the index would decrease the search power. A cursory google search didn’t return insights on the topic. So, without much deeper consideration for whether it’s good or bad to stem code, all words are stemmed.</p>

<p><strong>The Build Pipeline</strong></p>

<p>Given data to index on disk, the build pipeline is as follows:</p>

<ol>
  <li>first pass through all documents to build global data (scoring based on document-occurrences and package dependencies</li>
  <li>second pass through all documents to score each document, taking into account the global data</li>
  <li><a href="https://docs.python.org/3/library/pickle.html">pickle</a> the index to facilitate testing</li>
  <li>perform validation on the generated index</li>
  <li>generate Elm modules that contian the package reference and index data</li>
</ol>

<p>I typically prefer code gen over having the client make a resource request, as it is quick to generate Elm files with simple data structures, and it obviates the need for JSON decoders etc on the Elm client end.</p>

<p>The genarated files are not small, but not too large. The 16MB Elm module containing the index data is eventually gzip’ed as a single JavaScript file, yielding a ~1.8MB file (including the Elm application logic).</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% <span class="nb">du</span> <span class="nt">-h</span> package_data/<span class="k">*</span>.elm
 16M	package_data/PackageIndex.elm
356K	package_data/PackageListing.elm
4.0K	package_data/PackageTimestamp.elm
% <span class="nb">du</span> <span class="nt">-h</span> package_data/<span class="k">*</span>.pkl
5.7M	package_data/package_index.pkl
</code></pre></div></div>

<h2 id="querying-the-index">Querying the Index</h2>

<p>Testing the generated index using the Python pickle, we see that search returns resonable results (note that the official Elm package index returns nothing for the query term “median”):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="n">validate_index</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">pkg_ref_dict</span><span class="p">,</span> <span class="n">index_maps</span><span class="p">,</span> <span class="s">'median'</span><span class="p">)</span>

<span class="p">[(</span><span class="mi">89</span><span class="p">,</span> <span class="p">(</span><span class="s">'gampleman'</span><span class="p">,</span> <span class="s">'elm-visualization'</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">),</span>
 <span class="p">(</span><span class="mi">94</span><span class="p">,</span> <span class="p">(</span><span class="s">'ianmackenzie'</span><span class="p">,</span> <span class="s">'elm-geometry'</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">),</span>
 <span class="p">(</span><span class="mi">96</span><span class="p">,</span> <span class="p">(</span><span class="s">'ianmackenzie'</span><span class="p">,</span> <span class="s">'elm-geometry-prerelease'</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">),</span>
 <span class="p">(</span><span class="mi">113</span><span class="p">,</span> <span class="p">(</span><span class="s">'jxxcarlson'</span><span class="p">,</span> <span class="s">'elm-stat'</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">),</span>
 <span class="p">(</span><span class="mi">704</span><span class="p">,</span> <span class="p">(</span><span class="s">'f0i'</span><span class="p">,</span> <span class="s">'statistics'</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">),</span>
 <span class="p">(</span><span class="mi">725</span><span class="p">,</span> <span class="p">(</span><span class="s">'folkertdev'</span><span class="p">,</span> <span class="s">'elm-kmeans'</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">),</span>
 <span class="p">(</span><span class="mi">767</span><span class="p">,</span> <span class="p">(</span><span class="s">'gicentre'</span><span class="p">,</span> <span class="s">'elm-vega'</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">),</span>
 <span class="p">(</span><span class="mi">768</span><span class="p">,</span> <span class="p">(</span><span class="s">'gicentre'</span><span class="p">,</span> <span class="s">'elm-vegalite'</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">)]</span>
</code></pre></div></div>

<p>In this case, there is a single query term, so all of the search results contain the query term exactly, yielding a score of 1.0.</p>

<p>The validation stage uses the same <code class="language-plaintext highlighter-rouge">query</code> function to implement some basic checks, like ensuring that querying by author and package name always returns the package as the first result.</p>

<p>On the Elm client side, it is mainly setting up a UI to allow user input and display results. Fortunately, there is a PorterStemmer library in Elm as well – in fact, more than one, as shown by an index query:</p>

<p><img src="/assets/img/elm_query_stemmer.png" alt="Elm Package Search for Porter Stemmer Screenshot" class="img-responsive" /></p>

<p>As with the Python implementation, the Elm <code class="language-plaintext highlighter-rouge">query</code> function scores each document (package) in the index against the query terms and returns all matches in descending order of similarity score.</p>

<div class="language-elm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">query</span> <span class="p">:</span> <span class="kt">PkgRefMap</span> <span class="o">-&gt;</span> <span class="kt">List</span> <span class="p">(</span> <span class="kt">Int</span><span class="o">,</span> <span class="kt">PkgIndexMap</span> <span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">List</span> <span class="kt">Word</span> <span class="o">-&gt;</span> <span class="kt">List</span> <span class="kt">QueryResult</span>
<span class="n">query</span> <span class="n">pkgRefDict</span> <span class="n">indexMaps</span> <span class="n">queryTokens</span> <span class="o">=</span>
    <span class="k">let</span>
        <span class="n">scoredQuery</span> <span class="o">=</span>
            <span class="kt">Utils</span><span class="o">.</span><span class="n">countFreq</span> <span class="n">queryTokens</span>
                <span class="o">|&gt;</span> <span class="kt">List</span><span class="o">.</span><span class="n">map</span> <span class="p">(</span><span class="kt">Tuple</span><span class="o">.</span><span class="n">mapSecond</span> <span class="n">toFloat</span><span class="p">)</span>
    <span class="k">in</span>
    <span class="kt">List</span><span class="o">.</span><span class="n">map</span> <span class="p">(</span><span class="n">cosineScore</span> <span class="n">scoredQuery</span><span class="p">)</span> <span class="n">indexMaps</span>
        <span class="o">|&gt;</span> <span class="kt">List</span><span class="o">.</span><span class="n">map</span> <span class="p">(</span><span class="n">toResult</span> <span class="n">pkgRefDict</span><span class="p">)</span>
        <span class="o">|&gt;</span> <span class="kt">List</span><span class="o">.</span><span class="n">filter</span> <span class="p">(</span><span class="o">\</span><span class="p">(</span> <span class="n">a</span><span class="o">,</span> <span class="n">_</span><span class="o">,</span> <span class="n">_</span> <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
        <span class="o">|&gt;</span> <span class="kt">List</span><span class="o">.</span><span class="n">sortBy</span> <span class="kt">Utils</span><span class="o">.</span><span class="n">first</span>
        <span class="o">|&gt;</span> <span class="kt">List</span><span class="o">.</span><span class="n">reverse</span>
</code></pre></div></div>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>I added some diff’ing logic to the Python scraper, so it can incrementally retrieve version updates, as well as new packages that are added to the official Elm package index.</p>

<p>As a naive implementation of a keyword search engine… it seems to be reasonably useful – at least enough so for me to use personally in Elm projects.</p>

<p>At least two improvements come ot mind for future exploration:</p>

<ul>
  <li>incorporate additional metrics for tie-breaking results of the same score, such as count of downloads from <code class="language-plaintext highlighter-rouge">npm</code> (<a href="https://discourse.elm-lang.org/t/most-popular-elm-related-npm-packages-by-usage/4189">some prior discussion on this</a> on Elm discourse)</li>
  <li>identifying specific modules within packages, where relevant, and including them in query results (harder to do and may inflate the index daata quite a bit?)</li>
</ul>

<p><strong>Links</strong></p>

<ul>
  <li><a href="https://tarokuriyama.com/elmsearch/">Try the query app</a></li>
  <li><a href="https://github.com/tkuriyama/elm-package-search/tree/master/elm-search">GitHub repo</a></li>
</ul>]]></content><author><name></name></author><category term="general" /><category term="elm" /><summary type="html"><![CDATA[The search function in the Elm package index is relatively limited. Most importantly, documentation such as READMEs do not appear to be indexed, limiting the power of keyword search.]]></summary></entry><entry><title type="html">Genuine Sieve of Eratosthenes with Elm Generators</title><link href="/general/2021/10/30/eratosthenes-elm-generator.html" rel="alternate" type="text/html" title="Genuine Sieve of Eratosthenes with Elm Generators" /><published>2021-10-30T14:00:00+00:00</published><updated>2021-10-30T14:00:00+00:00</updated><id>/general/2021/10/30/eratosthenes-elm-generator</id><content type="html" xml:base="/general/2021/10/30/eratosthenes-elm-generator.html"><![CDATA[<p>I recently worked on a generator library for Elm (to simulate laziness and work with streams). Since I ended up publishing it <a href="https://package.elm-lang.org/packages/tkuriyama/elm-generator/latest/">as a package</a>, I needed some examples and dug up the Sieve of Eratosthenes.</p>

<h2 id="sieve-of-eratosthenes">Sieve of Eratosthenes</h2>

<p>The Ancient Greek algorithm provides a simple way to eliminate composite numbers (or sieve them out), leaving only the primes. Given the natural numbers &gt; 1…</p>

<ul>
  <li>two is the first prime number; cross out multiples of two [2, 4, 6…]</li>
  <li>the next number that is not crossed out – three – must be a prime; cross out multiples of three [3, 6, 9…]</li>
  <li>etc</li>
</ul>

<h2 id="implementation-considerations">Implementation Considerations</h2>

<p>How should the algorithm be implemented? In an imperative language with efficient side effects (e.g. array read writes), multiples can be crossed off all at once (though a finite upper bound must be specified).</p>

<p>In purely functional contexts, a typical solution uses laziness with infinite recursion, e.g. from the <a href="https://wiki.haskell.org/Prime_numbers#Sieve_of_Eratosthenes">Haskell Wiki</a>:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">import</span> <span class="nn">Data.List.Ordered</span> <span class="p">(</span><span class="nf">minus</span><span class="p">,</span> <span class="nf">union</span><span class="p">,</span> <span class="nf">unionAll</span><span class="p">)</span>

<span class="n">primes</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">:</span> <span class="mi">3</span> <span class="o">:</span> <span class="n">minus</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span><span class="mi">7</span><span class="o">..</span><span class="p">]</span> <span class="p">(</span><span class="n">unionAll</span> <span class="p">[[</span><span class="n">p</span><span class="o">*</span><span class="n">p</span><span class="p">,</span> <span class="n">p</span><span class="o">*</span><span class="n">p</span><span class="o">+</span><span class="mi">2</span><span class="o">*</span><span class="n">p</span><span class="o">..</span><span class="p">]</span> <span class="o">|</span> <span class="n">p</span> <span class="o">&lt;-</span> <span class="n">tail</span> <span class="n">primes</span><span class="p">]</span>
</code></pre></div></div>

<p>In Elm 0.19, as far as I can tell, such infinitely recursive formulations are impossible with the strict compiler. Using generators, though, we can still implement a genuine, infinite sieve.</p>

<h2 id="algorithm">Algorithm</h2>

<p>An algorithm for a purely functional, incremental sieve is described in section 3 of Melissa O’Neill’s paper, <a href="https://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf">The Genuine Sieve of Eratosthenes</a>.</p>

<p>The idea is store the next known composite in a map. As larger candidates are explored and the known composites are found, the map is updated to reflect the next known composites (in a “just-in-time” manner). (A step-by-step illustration follows later in this post.)</p>

<p>The Haskell implementation from the article for an initial version is relatively clear:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sieve</span> <span class="n">xs</span> <span class="o">=</span> <span class="n">sieve</span><span class="err">’</span> <span class="n">xs</span> <span class="kt">Map</span><span class="o">.</span><span class="n">empty</span>
    <span class="kr">where</span>
      <span class="n">sieve</span><span class="err">’</span> <span class="kt">[]</span> <span class="n">table</span> <span class="o">=</span> <span class="kt">[]</span>
      <span class="n">sieve</span><span class="err">’</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="n">table</span> <span class="o">=</span>
          <span class="kr">case</span> <span class="kt">Map</span><span class="o">.</span><span class="n">lookup</span> <span class="n">x</span> <span class="n">table</span> <span class="kr">of</span>
              <span class="kt">Nothing</span> <span class="err">−</span><span class="o">&gt;</span> <span class="n">x</span> <span class="o">:</span> <span class="n">sieve</span><span class="err">’</span> <span class="n">xs</span> <span class="p">(</span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span> <span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">x</span><span class="p">)</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="n">table</span><span class="p">)</span>
              <span class="kt">Just</span> <span class="n">facts</span> <span class="err">−</span><span class="o">&gt;</span> <span class="n">sieve</span><span class="err">’</span> <span class="n">xs</span> <span class="p">(</span><span class="n">foldl</span> <span class="n">reinsert</span> <span class="p">(</span><span class="kt">Map</span><span class="o">.</span><span class="n">delete</span> <span class="n">x</span> <span class="n">table</span><span class="p">)</span> <span class="n">facts</span><span class="p">)</span>
            <span class="kr">where</span>
                <span class="n">reinsert</span> <span class="n">table</span> <span class="n">prime</span> <span class="o">=</span> <span class="kt">Map</span><span class="o">.</span><span class="n">insertWith</span> <span class="p">(</span><span class="o">++</span><span class="p">)</span> <span class="p">(</span><span class="n">x</span><span class="o">+</span><span class="n">prime</span><span class="p">)</span> <span class="p">[</span><span class="n">prime</span><span class="p">]</span> <span class="n">table</span>
</code></pre></div></div>

<p>The <a href="https://gist.github.com/tkuriyama/5cb269e89c6cfadf3969ea0fbc629aeb">Elm version</a> incorporates a few optimizations noted in the article, including the fact that different “wheels” may be used to generate candidates.</p>

<ul>
  <li>trivially, start with 2 and only try odd numbers [3, 5, 7…], thereby excluding composites involving 2 (referred to as “wheel2”)</li>
  <li>similarly, start with the first primes 2, 3, 5 and only try numbers that are not their composites [7, 11, 13…] (referred to as “wheel2357”)</li>
</ul>

<p>O’Neill’s paper provides a cycle that can be used to implement a wheel that excludes multiples of 2, 3, 5, and 7.</p>

<p>The core of the <a href="https://gist.github.com/tkuriyama/5cb269e89c6cfadf3969ea0fbc629aeb">Elm version</a> is the recursive function that advances the prime number generator one step (thereby emitting the next prime and updating the map of composites). The logic is the same as the Haskell version above. Here, <code class="language-plaintext highlighter-rouge">wheel</code> is itself a generator, emitting candidates to be verified.</p>

<div class="language-elm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sieveNext</span> <span class="p">:</span> <span class="kt">SieveState</span> <span class="n">b</span> <span class="o">-&gt;</span> <span class="kt">Maybe</span> <span class="p">(</span> <span class="kt">Int</span><span class="o">,</span> <span class="kt">SieveState</span> <span class="n">b</span> <span class="p">)</span>
<span class="n">sieveNext</span> <span class="p">(</span> <span class="n">map</span><span class="o">,</span> <span class="n">wheel</span> <span class="p">)</span> <span class="o">=</span>
    <span class="k">let</span>
        <span class="p">(</span> <span class="n">guess</span><span class="o">,</span> <span class="n">wheel_</span> <span class="p">)</span> <span class="o">=</span>
            <span class="n">safeAdvance1</span> <span class="n">wheel</span>
    <span class="k">in</span>
    <span class="k">case</span> <span class="kt">Dict</span><span class="o">.</span><span class="n">get</span> <span class="n">guess</span> <span class="n">map</span> <span class="k">of</span>
        <span class="kt">Nothing</span> <span class="o">-&gt;</span>
            <span class="kt">Just</span>
                <span class="p">(</span> <span class="n">guess</span><span class="o">,</span> <span class="p">(</span> <span class="n">insertNext</span> <span class="n">guess</span> <span class="n">wheel_</span> <span class="n">map</span><span class="o">,</span> <span class="n">wheel_</span> <span class="p">)</span> <span class="p">)</span>

        <span class="kt">Just</span> <span class="n">composites</span> <span class="o">-&gt;</span>
            <span class="n">sieveNext</span>
                <span class="p">(</span> <span class="n">updateMap</span> <span class="n">guess</span> <span class="n">composites</span> <span class="n">map</span><span class="o">,</span> <span class="n">wheel_</span> <span class="p">)</span>
</code></pre></div></div>

<p>Running <code class="language-plaintext highlighter-rouge">elm repl</code> on a modern dual-core laptop, the millionth prime can be found in under a minute.</p>

<div class="language-elm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">import</span> <span class="kt">Generator</span> <span class="k">as</span> <span class="kt">G</span>

<span class="o">&gt;</span> <span class="k">import</span> <span class="kt">Examples</span><span class="o">.</span><span class="kt">Eratosthenes</span> <span class="k">as</span> <span class="kt">E</span>

<span class="o">&gt;</span> <span class="kt">E</span><span class="o">.</span><span class="n">wheel2357Init</span> <span class="o">|&gt;</span> <span class="kt">E</span><span class="o">.</span><span class="n">sieve</span> <span class="o">|&gt;</span> <span class="kt">G</span><span class="o">.</span><span class="n">take</span> <span class="mi">10</span>
<span class="p">[</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">7</span><span class="o">,</span><span class="mi">11</span><span class="o">,</span><span class="mi">13</span><span class="o">,</span><span class="mi">17</span><span class="o">,</span><span class="mi">19</span><span class="o">,</span><span class="mi">23</span><span class="o">,</span><span class="mi">29</span><span class="p">]</span>
    <span class="p">:</span> <span class="kt">List</span> <span class="kt">Int</span>

<span class="o">&gt;</span> <span class="kt">E</span><span class="o">.</span><span class="n">wheel2357Init</span> <span class="o">|&gt;</span> <span class="kt">E</span><span class="o">.</span><span class="n">sieve</span> <span class="o">|&gt;</span> <span class="kt">G</span><span class="o">.</span><span class="n">drop</span> <span class="mi">999999</span> <span class="o">|&gt;</span> <span class="kt">G</span><span class="o">.</span><span class="n">take</span> <span class="mi">1</span>
<span class="p">[</span><span class="mi">15485863</span><span class="p">]</span> <span class="p">:</span> <span class="kt">List</span> <span class="kt">Int</span>
</code></pre></div></div>

<h2 id="observing-the-algorithm">Observing the Algorithm</h2>

<p>A benefit of using a generator is the ease of observability of internal state at each step.</p>

<p>Using the wheel of odd numbers only, the first two steps yields the first prime, 2, and the second prime, 3, at which point the composite map has been initialized. For the prime 3, the map is keyed at <code class="language-plaintext highlighter-rouge">3 * 3</code> (since smaller composites will have been previously checked), with a value being another generator for <code class="language-plaintext highlighter-rouge">3 * the wheel candidates</code>, i.e. only the multiples of 3 that will be checked by the algorithm.</p>

<div class="language-elm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="kt">E</span><span class="o">.</span><span class="n">wheel2Init</span> <span class="o">|&gt;</span> <span class="kt">E</span><span class="o">.</span><span class="n">sieve</span> <span class="o">|&gt;</span> <span class="kt">G</span><span class="o">.</span><span class="n">advance</span> <span class="mi">2</span>

<span class="p">(</span> <span class="p">[</span><span class="mi">2</span><span class="o">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="o">,</span> <span class="kt">Active</span>
    <span class="p">{</span> <span class="n">next</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;</span>
    <span class="o">,</span> <span class="n">state</span> <span class="o">=</span> <span class="p">(</span> <span class="p">(</span> <span class="kt">Dict</span><span class="o">.</span><span class="n">fromList</span> <span class="p">[</span> <span class="p">(</span><span class="mi">9</span><span class="o">,</span> <span class="p">[</span><span class="kt">Active</span> <span class="p">{</span> <span class="n">next</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;,</span> <span class="n">state</span> <span class="o">=</span> <span class="p">(()</span><span class="o">,</span><span class="mi">3</span><span class="p">)</span> <span class="p">}])</span>
                                <span class="p">]</span>
                <span class="o">,</span> <span class="kt">Active</span> <span class="p">{</span> <span class="n">next</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;,</span> <span class="n">state</span> <span class="o">=</span> <span class="p">(()</span><span class="o">,</span><span class="mi">3</span><span class="p">)</span> <span class="p">}</span>
                <span class="p">)</span>
              <span class="o">,</span> <span class="p">[</span><span class="mi">3</span><span class="p">]</span>
              <span class="p">)</span>
     <span class="p">}</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Advancing a few steps, we see that the algorithm has moved past 9, the first composite in the map. Accordingly, the first key in the map has been updated to 15, to reflect the next multiple of 3 to be verified (<code class="language-plaintext highlighter-rouge">3 * 5</code>). Other squares of found primes (5, 7, 11) have also been added to the map, with their respective composite generators.</p>

<div class="language-elm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="kt">E</span><span class="o">.</span><span class="n">wheel2Init</span> <span class="o">|&gt;</span> <span class="kt">E</span><span class="o">.</span><span class="n">sieve</span> <span class="o">|&gt;</span> <span class="kt">G</span><span class="o">.</span><span class="n">advance</span> <span class="mi">5</span>
<span class="p">(</span> <span class="p">[</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">7</span><span class="o">,</span><span class="mi">11</span><span class="p">]</span>
<span class="o">,</span> <span class="kt">Active</span> 
    <span class="p">{</span> <span class="n">next</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;</span>
    <span class="o">,</span> <span class="n">state</span> <span class="o">=</span> <span class="p">(</span> <span class="p">(</span> <span class="kt">Dict</span><span class="o">.</span><span class="n">fromList</span> <span class="p">[</span> <span class="p">(</span><span class="mi">15</span><span class="o">,</span><span class="p">[</span><span class="kt">Active</span> <span class="p">{</span> <span class="n">next</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;,</span> <span class="n">state</span> <span class="o">=</span> <span class="p">(()</span><span class="o">,</span><span class="mi">5</span><span class="p">)</span> <span class="p">}])</span>
                                <span class="o">,</span> <span class="p">(</span><span class="mi">25</span><span class="o">,</span><span class="p">[</span><span class="kt">Active</span> <span class="p">{</span> <span class="n">next</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;,</span> <span class="n">state</span> <span class="o">=</span> <span class="p">(()</span><span class="o">,</span><span class="mi">5</span><span class="p">)</span> <span class="p">}])</span>
                                <span class="o">,</span> <span class="p">(</span><span class="mi">49</span><span class="o">,</span><span class="p">[</span><span class="kt">Active</span> <span class="p">{</span> <span class="n">next</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;,</span> <span class="n">state</span> <span class="o">=</span> <span class="p">(()</span><span class="o">,</span><span class="mi">7</span><span class="p">)</span> <span class="p">}])</span>
                                <span class="o">,</span> <span class="p">(</span><span class="mi">121</span><span class="o">,</span><span class="p">[</span><span class="kt">Active</span> <span class="p">{</span> <span class="n">next</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;,</span> <span class="n">state</span> <span class="o">=</span> <span class="p">(()</span><span class="o">,</span><span class="mi">11</span><span class="p">)</span> <span class="p">}])</span>
                                <span class="p">]</span>
                <span class="o">,</span> <span class="kt">Active</span> <span class="p">{</span> <span class="n">next</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;,</span> <span class="n">state</span> <span class="o">=</span> <span class="p">(()</span><span class="o">,</span><span class="mi">11</span><span class="p">)</span> <span class="p">})</span>
              <span class="o">,</span> <span class="p">[]</span>
              <span class="p">)</span>
    <span class="p">}</span>
<span class="p">)</span>
</code></pre></div></div>

<p>But we can do better. Since we’re already in Elm, why not feed the data directly into a visualization? (No need to bother with cumbersome serialization / deserialization to and from some non-native data structure.)</p>

<h2 id="visualizing-the-algorithm">Visualizing the Algorithm</h2>

<p><a href="https://tarokuriyama.com/eratosthenes/">A simple visualization is hosted here</a>.</p>

<p>Since the goai is to facilitate the understanding of the sieve and composite map state at each step, the information is presented directly with tables, with highlighting to denote changes from the previous step.</p>

<p>In the below screenshot, we see that with wheel2, 105 has multiple prime factors 3, 5, 7 – all of which have just been updated in the map to their next composites, highlighted in green.</p>

<p><img src="/assets/img/sieve.png" alt="Sieve Visualization Screenshot" class="img-responsive" /></p>]]></content><author><name></name></author><category term="general" /><category term="elm" /><category term="haskell" /><summary type="html"><![CDATA[I recently worked on a generator library for Elm (to simulate laziness and work with streams). Since I ended up publishing it as a package, I needed some examples and dug up the Sieve of Eratosthenes.]]></summary></entry><entry><title type="html">JavaScript in 10 Days: Day 10</title><link href="/javascript/2021/09/09/js-in-10days-day10.html" rel="alternate" type="text/html" title="JavaScript in 10 Days: Day 10" /><published>2021-09-09T14:00:00+00:00</published><updated>2021-09-09T14:00:00+00:00</updated><id>/javascript/2021/09/09/js-in-10days-day10</id><content type="html" xml:base="/javascript/2021/09/09/js-in-10days-day10.html"><![CDATA[<p>Today’s plan is to finish up some functional additions to the shopping list tracker and write a review post of the past ten days.</p>

<h2 id="functional-additions-to-react-app">Functional Additions to React App</h2>

<p><a href="https://github.com/tkuriyama/learn-js/tree/master/moz-todo-react">Day 10 Code</a></p>

<p><strong>Edit Ergonomics</strong></p>

<p>When editing a text input field, there should be a placeolder for hte current value, which can be auto-filled for editing by pressing tab. Otherwise, the user inputs a complete new value.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">function</span> <span class="nx">tryAutofill</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span> <span class="o">===</span> <span class="dl">""</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">===</span> <span class="mi">9</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
            <span class="kd">const</span> <span class="nx">text</span> <span class="o">=</span>
                <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">placeholder</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2"> (press</span><span class="dl">"</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
            <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">text</span><span class="p">;</span>
            <span class="nx">setNewName</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">placeholder</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p><strong>Status Toggle</strong></p>

<p>Instead of <code class="language-plaintext highlighter-rouge">completed</code> boolean task status, each item in the shopping list should be associated with a status name (string, logically of union type though not implemented as such here):</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="c1">// checkbox</span>
    <span class="kd">function</span> <span class="nx">toggleTaskStatus</span><span class="p">(</span><span class="nx">id</span><span class="p">,</span> <span class="nx">statusName</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">updatedTasks</span> <span class="o">=</span> <span class="nx">tasks</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">task</span> <span class="o">=&gt;</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">task</span><span class="p">.</span><span class="nx">id</span> <span class="o">!==</span> <span class="nx">id</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">return</span> <span class="nx">task</span><span class="p">;</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">return</span> <span class="p">{...</span><span class="nx">task</span><span class="p">,</span> <span class="na">statusName</span><span class="p">:</span> <span class="nx">statusName</span><span class="p">};</span>
            <span class="p">}</span>
        <span class="p">});</span>
        <span class="nx">setTasks</span><span class="p">(</span><span class="nx">updatedTasks</span><span class="p">);</span>
        <span class="p">}</span>
</code></pre></div></div>

<p><strong>Cosmetic Updates</strong></p>

<p>After some minor cosmetic updates to reduce unnecessary text, the app looks like this:</p>

<p><img src="/assets/img/mdn_todo_list2.png" alt="Todo App" class="img-responsive" /></p>

<p>I started to add a URL input component, then spent a long time unsuccessfully trying to understand how the CSS width and element positioning works in the <code class="language-plaintext highlighter-rouge">index.css</code> stylesheet. It seems silly to get stuck on the “simple” presentation layer, but I concluded that without a deeper undertanding of modern CSS, the point of dimishing return was reached as far as experimenting blindly with the obvious elements like <code class="language-plaintext highlighter-rouge">width</code>, <code class="language-plaintext highlighter-rouge">max-width</code>, <code class="language-plaintext highlighter-rouge">display</code>, etc.</p>

<p>While I’m adding a CSS primer to my future todo list, it also really emphasized to me just how nice the Elm UI library <code class="language-plaintext highlighter-rouge">elm-ui</code> is, in terms of abstracting away the HTML layout and CSS layers, and presenting the user a pure Elm API that is intuitive and “just works” (for the most part).</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>While I feel like I learned a decent amount from the React tutorail and toy app, especially in terms of IO like localStorage and keyboard interactions, I still feel quite frustrated with the sluggish pace of developemnt and debugging.</p>

<p>To become more proficient on the front-end, it seems like a few things are imperative:</p>

<ul>
  <li>reasonable proficiency in CSS</li>
  <li>solid understanding of the framework tooling, especially for testing, error handling, and generally getting helpful messages out of the compiler</li>
</ul>

<p>This concludes my ten day journey learning JavaScript… a review post to follow.</p>

<h2 id="todo--to-read">Todo / To Read</h2>

<ul>
  <li>Using flycheck with React / web-mode / .jsx in emacs</li>
  <li>MDN accessibility: <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA">ARIA</a></li>
  <li>Figure out how to use <code class="language-plaintext highlighter-rouge">outDir</code>(?) to compile <code class="language-plaintext highlighter-rouge">.js</code> files to separate subdirectory</li>
  <li>Compiling with both TypeScript with Babel</li>
  <li>Kyle Simpson, <a href="https://github.com/getify/You-Dont-Know-JS/tree/1st-ed">You Don’t Know JS</a></li>
  <li>Composing Software, by Eric Elliott (https:// leanpub.com/composingsoftware)</li>
  <li>A CSS primer</li>
</ul>]]></content><author><name></name></author><category term="javascript" /><category term="javascript" /><category term="typescript" /><category term="archive" /><summary type="html"><![CDATA[Today’s plan is to finish up some functional additions to the shopping list tracker and write a review post of the past ten days.]]></summary></entry><entry><title type="html">JavaScript in 10 Days: Review</title><link href="/javascript/2021/09/09/js-in-10-days-review.html" rel="alternate" type="text/html" title="JavaScript in 10 Days: Review" /><published>2021-09-09T14:00:00+00:00</published><updated>2021-09-09T14:00:00+00:00</updated><id>/javascript/2021/09/09/js-in-10-days-review</id><content type="html" xml:base="/javascript/2021/09/09/js-in-10-days-review.html"><![CDATA[<p>TLDR: there’s a lot to learn in the JavaScript ecosystem, and ten days is not nearly enough. As Norvig said, it’s more about <a href="https://norvig.com/21-days.html">teaching yourself to program in ten years</a>.</p>

<p>I recently set out to learn as much JavaScript as possible in ten days. I’ve filtered the individual days’ posts from the blog home, but they’re all linked below.</p>

<table>
  <tbody>
    <tr>
      <td><a href="https://tkuriyama.github.io/javascript/2021/08/31/js-in-7days-day1.html">Day 1</a></td>
      <td><a href="https://tkuriyama.github.io/javascript/2021/09/01/js-in-7days-day2.html">Day2</a></td>
      <td><a href="https://tkuriyama.github.io/javascript/2021/09/02/js-in-10days-day3.html">Day 3</a></td>
      <td><a href="https://tkuriyama.github.io/javascript/2021/09/03/js-in-10days-day4.html">Day 4</a></td>
      <td><a href="https://tkuriyama.github.io/javascript/2021/09/04/js-in-10days-day5.html">Day 5</a></td>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
      <td><a href="https://tkuriyama.github.io/javascript/2021/09/05/js-in-10days-day6.html">Day 6</a></td>
      <td><a href="https://tkuriyama.github.io/javascript/2021/09/06/js-in-10days-day7.html">Day 7</a></td>
      <td><a href="https://tkuriyama.github.io/javascript/2021/09/07/js-in-10days-day8.html">Day 8</a></td>
      <td><a href="https://tkuriyama.github.io/javascript/2021/09/08/js-in-10days-day9.html">Day 9</a></td>
      <td><a href="https://tkuriyama.github.io/javascript/2021/09/09/js-in-10days-day10.html">Day 10</a></td>
    </tr>
  </tbody>
</table>

<h2 id="overview">Overview</h2>

<p>At the end of ten days, I feel like I know less JavaScript than I expected. This might be because I have a bit more appreciation for: (a) the extent of new language features and the speed of change; and (b) the scale of the ecosystem.</p>

<p>The meandering path that I ended up taking in ten days is maybe indicative of the nature of JavaScript today. It included:</p>

<ul>
  <li>reading a classic book where a good amount of material was no longer applicable</li>
  <li>struggling with version compatibitliy of new language features</li>
  <li>working out instance vs prorotype issues in the TypeScript compiler</li>
  <li>using a mature framework like React to build a toy app (and getting stuck on CSS)</li>
</ul>

<p>I guess those are all part of “learning JavaScript”, because JavaScript today is comprised of so many different aspects. So really, it makes sense to have more specific learning goal.</p>

<p>As with studies in general, though, it’s often hard to know upfront what to study, or what yields the greatest personal interest and utility. So some amount of sumbling around is usually unavoidable, if not always helpful. Though there was a fair amount of hair-pulling, I’m glad I did the past ten days, and the blog posts helped keep me on track with some personal accountability.</p>

<h2 id="key-materials-covered">Key Materials Covered</h2>

<p>The main materials covered were as follows, excluding miscellaneous articles and documentation.</p>

<p><strong>Books</strong></p>

<ul>
  <li>Douglas Crockford, <a href="https://www.oreilly.com/library/view/javascript-the-good/9780596517748/">JavaScript, the Good Parts</a> (2008)</li>
  <li>Luis Atencio, <a href="https://www.manning.com/books/the-joy-of-javascript?gclid=Cj0KCQjwm9yJBhDTARIsABKIcGba98tCJcy9VP5gCWyvsv8TlDeOGFtxaeGUgbPsTUPGn5fmDHeslZQaApWAEALw_wcB">The Joy of JavaScript</a> (2021)</li>
  <li>Marijn Haverbeke <a href="https://www.manning.com/books/the-joy-of-javascript?gclid=Cj0KCQjwm9yJBhDTARIsABKIcGba98tCJcy9VP5gCWyvsv8TlDeOGFtxaeGUgbPsTUPGn5fmDHeslZQaApWAEALw_wcB">Eloquent Javascript</a> (2018)</li>
</ul>

<p><strong>Videos</strong></p>

<ul>
  <li>Brandon Eich, <a href="https://www.youtube.com/watch?v=GxouWy-ZE80">A Brief History of JavaScript</a></li>
</ul>

<p><strong>Tutorials / References</strong></p>

<ul>
  <li><a href="https://www.typescriptlang.org/docs/handbook/intro.html">The TypeScript Handbook</a></li>
  <li>Eric Elliott, <a href="https://medium.com/javascript-scene/the-missing-introduction-to-react-62837cb2fd76">The Missing Introduction to React</a></li>
  <li>MDN, <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started">Getting Started with React</a></li>
</ul>

<h2 id="thoughts-on-materials">Thoughts on Materials</h2>

<p>My perspective remains quite limited, but…</p>

<ul>
  <li>
    <p>I’d recommend unequivocally the MDN tutorail on React, as well as the other materials in their <a href="https://developer.mozilla.org/en-US/docs/Learn">Learn Web Development</a> series.</p>
  </li>
  <li>
    <p>The TypeScript handbook is quite good as far as official documentation goes, though it’s probably worth finding longer material dedicated to learning TypeScript.</p>
  </li>
  <li>
    <p>As far as books, I’d look for the latest recommendations, given how fast the language is evolving. I’d probably not recommend the Crockford, given that it’s become relatively dated. For more advanced programmers, the Atencio is interesting and will likely remain relevant for a while.</p>
  </li>
</ul>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>I suspect the impact of the ten days on futures studies is:</p>

<ul>
  <li>I’ll do at least some more basic tutorials on Vue, CSS, etc</li>
  <li>it will be much less daunting to figure out, say, working with JavaScript ports in Elm</li>
</ul>

<p>I’d also like to find a really good book on TypeScript, for which perhaps the Recurse Center community has a good recommendation.</p>

<h2 id="future-todos">Future Todos</h2>

<ul>
  <li>Tooling: working with TypeScript, Babel, React, etc in emacs, as well as the general config and build options</li>
  <li>MDN docs: <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA">ARIA</a>, <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started">Vue tutorial</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Tutorials">CSS Tutorials</a></li>
  <li>Kyle Simpson, <a href="https://github.com/getify/You-Dont-Know-JS/tree/1st-ed">You Don’t Know JS</a></li>
  <li>Eric Elliott, <a href="https://leanpub.com/composingsoftware">Composing Software</a></li>
</ul>]]></content><author><name></name></author><category term="javascript" /><category term="javascript" /><category term="typescript" /><summary type="html"><![CDATA[TLDR: there’s a lot to learn in the JavaScript ecosystem, and ten days is not nearly enough. As Norvig said, it’s more about teaching yourself to program in ten years.]]></summary></entry><entry><title type="html">JavaScript in 10 Days: Day 9</title><link href="/javascript/2021/09/08/js-in-10days-day9.html" rel="alternate" type="text/html" title="JavaScript in 10 Days: Day 9" /><published>2021-09-08T14:00:00+00:00</published><updated>2021-09-08T14:00:00+00:00</updated><id>/javascript/2021/09/08/js-in-10days-day9</id><content type="html" xml:base="/javascript/2021/09/08/js-in-10days-day9.html"><![CDATA[<p>Today’s plan is to complete the <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started">MDN tutorial</a> on creating a Todo app in React, and start adding some features. My intent is to create more of a shopping list that can track wishlist and purchased items with urls, persisted in local storage.</p>

<h2 id="mdn-tutorial-todo-app-in-react">MDN Tutorial: Todo App in React</h2>

<p><a href="https://github.com/tkuriyama/learn-js/tree/master/moz-todo-react">Day 9 Code</a></p>

<p>As of <a href="https://github.com/tkuriyama/learn-js/commit/26d312370f63db95e52f3ea5342c9d170484d3b9">this commit</a>, the tutorial is complete.</p>

<p>Again, not much to note as everything is clearly explained and works. IT looks like this:</p>

<p><img src="/assets/img/mdn_todo_list.png" alt="Todo App" class="img-responsive" /></p>

<p>(The one part of the tutorial I didn’t read closely was the CSS, which seems like it’s developed into a language in itself, with some powerful features.)</p>

<h2 id="functional-additions">Functional Additions</h2>

<p><strong>Local Storage</strong></p>

<p>Changing the React app to use <code class="language-plaintext highlighter-rouge">localStorage</code> instead of a hard-coded initial state turns out to be straightforward. <a href="https://usehooks.com/useLocalStorage/">This article</a> provides a nice function that nicely wraps the <code class="language-plaintext highlighter-rouge">useState</code> hook.</p>

<p>Since I don’t plan to store much data in the app, and it’s for unimportant data even if I end up using it regularly, I’m not concerned with the potential issues with using the browser’s local storage.</p>

<p><strong>Minor UI / UX Changes</strong></p>

<ul>
  <li>style updates</li>
  <li>pre-load existing value when editing</li>
  <li>add placeholder for main item input</li>
</ul>

<p><strong>Change Item State Set</strong></p>

<p>Rather than a boolean <code class="language-plaintext highlighter-rouge">completed</code> state, the app should use [<code class="language-plaintext highlighter-rouge">wishlist</code>, <code class="language-plaintext highlighter-rouge">purchased</code>, <code class="language-plaintext highlighter-rouge">archivefd</code>], with default showing <code class="language-plaintext highlighter-rouge">wishlist</code>.</p>

<p>I got stuck here… the code compiles but adding a task yields cryptic erros. React suggests using <a href="https://reactjs.org/docs/error-boundaries.html">error boundaries</a>, which I’ll try tomorrow.</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>Coming from Elm, it’s a bit frustrating to find that neither the compiler nor the run-time provides helpful debugging messages. Undoubtedly, the error is trviail as I’m making incremental changes… I’m probably not following recommended debugging procedures, though, and for that matter have no tests.</p>

<p>Tomorrow – the last of ten days! – I’ll try to debug nad finish adding some functionality to the shopping list app.</p>

<h2 id="todo--to-read">Todo / To Read</h2>

<ul>
  <li>Using flycheck with React / web-mode / .jsx in emacs</li>
  <li>MDN accessibility: <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA">ARIA</a></li>
  <li>Figure out how to use <code class="language-plaintext highlighter-rouge">outDir</code>(?) to compile <code class="language-plaintext highlighter-rouge">.js</code> files to separate subdirectory</li>
  <li>Compiling with both TypeScript with Babel</li>
  <li>Kyle Simpson, <a href="https://github.com/getify/You-Dont-Know-JS/tree/1st-ed">You Don’t Know JS</a></li>
  <li>Composing Software, by Eric Elliott (https:// leanpub.com/composingsoftware)</li>
</ul>]]></content><author><name></name></author><category term="javascript" /><category term="javascript" /><category term="typescript" /><category term="archive" /><summary type="html"><![CDATA[Today’s plan is to complete the MDN tutorial on creating a Todo app in React, and start adding some features. My intent is to create more of a shopping list that can track wishlist and purchased items with urls, persisted in local storage.]]></summary></entry><entry><title type="html">JavaScript in 10 Days: Day 8</title><link href="/javascript/2021/09/07/js-in-10days-day8.html" rel="alternate" type="text/html" title="JavaScript in 10 Days: Day 8" /><published>2021-09-07T14:00:00+00:00</published><updated>2021-09-07T14:00:00+00:00</updated><id>/javascript/2021/09/07/js-in-10days-day8</id><content type="html" xml:base="/javascript/2021/09/07/js-in-10days-day8.html"><![CDATA[<p>Today’s plan is to follow the MDN tutorial on building a simple <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started">tudo app in React</a> – a classic hello world exercise for the web.</p>

<p>MDN docs have been consistently the most clear as a JavaScript reference, and I’ve been interested in React since reading about it a few days prior.</p>

<h2 id="mdn-tutorial-todo-app-in-react">MDN Tutorial: Todo App in React</h2>

<p><a href="https://github.com/tkuriyama/learn-js/tree/master/moz-todo-react">Day 8 Code</a></p>

<p>Today’s code includes the chapters from the begining through the first chapter on React interactivity. There’s not much to add in terms of notes, as the tutorial is comprehensive and all the code works.</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>The MDN tutorial is <strong>excellent</strong> – it is very clear and everything actually works (!). React is relatively appealing so far, as it seems to embrace purity in ways similar to Elm, making it easier to reason about code. The React (Node?) hot-reloading compiler is also helpful, though it is not as fast as Elm and doesn’t seem to catch all potential run-time issues.</p>

<p>I’m curious about using TypeScript with React. With the remaining two days, my plan is to complete the tutorial and learn by adding more features (e.g. using the browser’s local storage, adding more UI features, etc).</p>

<h2 id="todo--to-read">Todo / To Read</h2>

<ul>
  <li><del><a href="https://eloquentjavascript.net/">Eloquent JavaScript</a> has exercises at the end of chapters, which might be worth exploring</del></li>
  <li>Using flycheck with React / web-mode / .jsx in emacs</li>
  <li>MDN accessibility: <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA">ARIA</a></li>
  <li>Figure out how to use <code class="language-plaintext highlighter-rouge">outDir</code>(?) to compile <code class="language-plaintext highlighter-rouge">.js</code> files to separate subdirectory</li>
  <li>Compiling with both TypeScript with Babel</li>
  <li>Kyle Simpson, <a href="https://github.com/getify/You-Dont-Know-JS/tree/1st-ed">You Don’t Know JS</a></li>
  <li>Composing Software, by Eric Elliott (https:// leanpub.com/composingsoftware)</li>
</ul>]]></content><author><name></name></author><category term="javascript" /><category term="javascript" /><category term="typescript" /><category term="archive" /><summary type="html"><![CDATA[Today’s plan is to follow the MDN tutorial on building a simple tudo app in React – a classic hello world exercise for the web.]]></summary></entry><entry><title type="html">JavaScript in 10 Days: Day 7</title><link href="/javascript/2021/09/06/js-in-10days-day7.html" rel="alternate" type="text/html" title="JavaScript in 10 Days: Day 7" /><published>2021-09-06T14:00:00+00:00</published><updated>2021-09-06T14:00:00+00:00</updated><id>/javascript/2021/09/06/js-in-10days-day7</id><content type="html" xml:base="/javascript/2021/09/06/js-in-10days-day7.html"><![CDATA[<p>Today’s plan is to read the chapters in Eloquent JavaScript leading up to and including the project of building a simple platform game in the browser (for which I plan to translate the code to TypeScript and solve some of the exercises).</p>

<h2 id="eloquent-javascript">Eloquent JavaScript</h2>

<p><a href="https://github.com/tkuriyama/learn-js/tree/master/snippets/day7">Day 7 Code</a></p>

<p><strong>Chapters 13 - 16</strong></p>

<ul>
  <li>Intro to the browser, DOM, etc</li>
  <li>Project: a Platform Game</li>
</ul>

<p><strong>Project: Platform Game</strong></p>

<p>Adding type annotations to the basic game setup code from the book took quite a bit of debugging, due to a few issues.</p>

<p>First, TypeScript / ECMA script versions and features took some trial and error. I ended up updating the <a href="https://github.com/tkuriyama/learn-js/blob/master/snippets/day7/tsconfig.json">tsconfig file</a> to have compile flags as follows:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">    </span><span class="nl">"compilerOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"lib"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"dom"</span><span class="p">,</span><span class="w"> </span><span class="s2">"es2017"</span><span class="p">],</span><span class="w">
        </span><span class="nl">"target"</span><span class="p">:</span><span class="w"> </span><span class="s2">"esnext"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"module"</span><span class="p">:</span><span class="w"> </span><span class="s2">"es2020"</span><span class="p">,</span><span class="w">
        </span><span class="err">...</span><span class="w">
</span></code></pre></div></div>

<p>This silenced most, but not all errors pulled in by flycheck. For example, it still complains about <code class="language-plaintext highlighter-rouge">find</code> not being a proeprty of an array, even though, as far as I can tell, it’s part of the language spec (since what version?).</p>

<p>And even though the <code class="language-plaintext highlighter-rouge">target</code> is set to <code class="language-plaintext highlighter-rouge">esnext</code>, the compiler still shows a few errors when <code class="language-plaintext highlighter-rouge">esnext</code> is not set as a command-line option.</p>

<p><code class="language-plaintext highlighter-rouge">tsc game.ts -&amp;&amp; node game.js</code> doesn’t work, but <code class="language-plaintext highlighter-rouge">tsc game.ts -t esnext &amp;&amp; node game.js</code> does work.</p>

<p>I’ll chalk that up to unfamiliarity with TypeScript configs, but it’s a bit baffling how a new-ish user is supposed to work out the myriad of langauge spec / config / build flag options.</p>

<p>Next, I couldn’t figure out how to get TypeScript to typecheck the lookup used in the board cell constructor.</p>

<p>In the level initialization, the function to create row cells looks up the input character (<code class="language-plaintext highlighter-rouge">ch</code>) from the <code class="language-plaintext highlighter-rouge">levelChars</code> object, and either returns the resulting string, or uses the resulting class’s method to create an actor (and return the string “empty”).</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="p">...</span>
        <span class="kd">let</span> <span class="kd">type</span> <span class="o">=</span> <span class="nx">levelChars</span><span class="p">[</span><span class="nx">ch</span><span class="p">];</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="kd">type</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">)</span> <span class="k">return</span> <span class="kd">type</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">startActors</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span>
          <span class="kd">type</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="k">new</span> <span class="nx">Vec</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">),</span> <span class="nx">ch</span><span class="p">));</span>
        <span class="k">return</span> <span class="dl">"</span><span class="s2">empty</span><span class="dl">"</span><span class="p">;</span>
    <span class="p">...</span>

<span class="kd">const</span> <span class="nx">levelChars</span> <span class="o">=</span> <span class="p">{</span>
  <span class="dl">"</span><span class="s2">.</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">empty</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">#</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">wall</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">+</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">lava</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">@</span><span class="dl">"</span><span class="p">:</span> <span class="nx">Player</span><span class="p">,</span> <span class="dl">"</span><span class="s2">o</span><span class="dl">"</span><span class="p">:</span> <span class="nx">Coin</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">=</span><span class="dl">"</span><span class="p">:</span> <span class="nx">Lava</span><span class="p">,</span> <span class="dl">"</span><span class="s2">|</span><span class="dl">"</span><span class="p">:</span> <span class="nx">Lava</span><span class="p">,</span> <span class="dl">"</span><span class="s2">v</span><span class="dl">"</span><span class="p">:</span> <span class="nx">Lava</span>
<span class="p">};</span>
</code></pre></div></div>

<p>What is the type of the values of <code class="language-plaintext highlighter-rouge">levelChars</code>?</p>

<p>Since <code class="language-plaintext highlighter-rouge">Player</code>, <code class="language-plaintext highlighter-rouge">Coin</code>, <code class="language-plaintext highlighter-rouge">Lava</code> are classes, I thought perhaps <code class="language-plaintext highlighter-rouge">string | Person | Coin | Laval</code>, but that doesn’t typecheck. (Perhaps because the class prototype needs to be distinguished somehow from the class’s type?) In any case, since <code class="language-plaintext highlighter-rouge">levelChars</code> isn’t used elsewhere, I just pattern matched manually:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nx">ch</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">.</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="dl">"</span><span class="s2">empty</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">ch</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">#</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="dl">"</span><span class="s2">wall</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">ch</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">+</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="dl">"</span><span class="s2">lava</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">v</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vec</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">ch</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">@</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">startActors</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">Player</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">v</span><span class="p">));</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">ch</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">0</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">startActors</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">Coin</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">v</span><span class="p">));</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">ch</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">=</span><span class="dl">"</span> <span class="o">||</span> <span class="nx">ch</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">|</span><span class="dl">"</span> <span class="o">||</span> <span class="nx">ch</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">v</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">startActors</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">Lava</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">v</span><span class="p">,</span> <span class="nx">ch</span><span class="p">));</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="dl">"</span><span class="s2">empty</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>.. which is a bit more verbose, but the slightly different shapes of <code class="language-plaintext highlighter-rouge">create</code> also woulnd’t have type checked in the original code.</p>

<p>As of <a href="https://github.com/tkuriyama/learn-js/commit/b71091a69c30b726d2ce06a5ed8a925e53124680">this commit</a>, the code compiles with the example board test in the book (before the “Encapsulation as a border” section).</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">%</span> <span class="nx">tsc</span> <span class="o">-</span><span class="nx">t</span> <span class="nx">esnext</span> <span class="nx">game</span><span class="p">.</span><span class="nx">ts</span> <span class="o">&amp;&amp;</span> <span class="nx">node</span> <span class="nx">game</span><span class="p">.</span><span class="nx">js</span>
<span class="mi">22</span> <span class="nx">by</span> <span class="mi">9</span> 
</code></pre></div></div>

<p><strong>Rendering</strong></p>

<p>Hmm… there are so many TypeScript issues with the book code.</p>

<p>The static HTML game map (as a stylized table) looks like this:</p>

<p><img src="/assets/img/js_game_home.png" alt="Game Home Statuc" class="img-responsive" /></p>

<p>I find the pattern of not declaring class methods (because they are class prototype methods) very confusing as a reader of code, and especially so when they are presented out of order in the book.</p>

<p>If I recall correctly from Atencio, assigning to the class prototype avoids instantiating the data or method per class instance, yielding a more efficient runtime. That seems logical, but I’m not sure how to get TypeScript to accept it when compiling strictly.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">A</span><span class="p">:</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">foo</span><span class="p">;</span>

    <span class="kd">constructor</span><span class="p">(</span><span class="nx">foo</span><span class="p">:</span> <span class="nx">Foo</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">Foo</span> <span class="o">=</span> <span class="nx">Foo</span><span class="p">;</span>
    <span class="p">};</span>
<span class="p">}</span>

<span class="nx">A</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">someMethod</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>TypeScript notes that class A doesn’t have method <code class="language-plaintext highlighter-rouge">someMethod</code>. The method can be declared in class A, but TypeScript comaplains that it <code class="language-plaintext highlighter-rouge">has no initializer and is not definitely assigned...</code>, which seems fair. The error can be ignored in this case, but there are so many errors that the benefit of having a live, static type checker has been negated!</p>

<p>Interestingly, implenting the prototypal method declarations as interfaces doesn’t type check at all – <code class="language-plaintext highlighter-rouge">class A incorrectly implements Interface SomeMethod... property someMethod is missing...</code></p>

<p>Separately, it took me a while to figure out that HTML elements have type <code class="language-plaintext highlighter-rouge">HTMLElement</code>. I’m still not sure what the most specific type is for HTML attribrutes (<code class="language-plaintext highlighter-rouge">{ [key: string]: string }</code>?)</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>This was the most frustrating day so far, primarily because I’ve self-imposed the additional complication of TypeScript. It’s possible that it’s better to stick with pure JavaScript and embrace it as such, adding in TypeScript later. Either way, I’ll finish the game project in some form tomorrow, or possibly just move on to m another project given the limited time remaining. Early on in learning a new domain, it’s often better to keep moving. It’s more productive to figure out all the details with a bit more context and experience.</p>

<h2 id="todo--to-read">Todo / To Read</h2>

<ul>
  <li><a href="https://eloquentjavascript.net/">Eloquent JavaScript</a> has exercises at the end of chapters, which might be worth exploring</li>
  <li>Figure out how to use <code class="language-plaintext highlighter-rouge">outDir</code>(?) to compile <code class="language-plaintext highlighter-rouge">.js</code> files to separate subdirectory</li>
  <li>Compiling with both TypeScript with Babel</li>
  <li>Kyle Simpson, <a href="https://github.com/getify/You-Dont-Know-JS/tree/1st-ed">You Don’t Know JS</a></li>
  <li>Composing Software, by Eric Elliott (https:// leanpub.com/composingsoftware)</li>
</ul>]]></content><author><name></name></author><category term="javascript" /><category term="javascript" /><category term="typescript" /><category term="archive" /><summary type="html"><![CDATA[Today’s plan is to read the chapters in Eloquent JavaScript leading up to and including the project of building a simple platform game in the browser (for which I plan to translate the code to TypeScript and solve some of the exercises).]]></summary></entry><entry><title type="html">JavaScript in 10 Days: Day 6</title><link href="/javascript/2021/09/05/js-in-10days-day6.html" rel="alternate" type="text/html" title="JavaScript in 10 Days: Day 6" /><published>2021-09-05T14:00:00+00:00</published><updated>2021-09-05T14:00:00+00:00</updated><id>/javascript/2021/09/05/js-in-10days-day6</id><content type="html" xml:base="/javascript/2021/09/05/js-in-10days-day6.html"><![CDATA[<p>Today’s plan is to work through an Eloquent JavaScript chapter with the accompanying exercises.</p>

<p>There are also some browser-based chapters in the same book, which look like they might have good exercises for building small apps.</p>

<h2 id="eloquent-javascript">Eloquent JavaScript</h2>

<p><a href="https://github.com/tkuriyama/learn-js/tree/master/snippets/day6">Day 6 Code</a></p>

<p>The <a href="https://eloquentjavascript.net/12_language.html">Egg Programing Language exercise chapter</a> from Eloquent JavaScript involves writing a parser and interpreter for a toy, Lisp-like programming language called Egg.</p>

<p>I used TypeScript, for which the initial work was mainly setting up the custom types / type aliases.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">type</span> <span class="nx">ParseResult</span> <span class="o">=</span>
    <span class="p">{</span>
        <span class="na">expr</span><span class="p">:</span> <span class="nx">EggExpr</span><span class="p">,</span>
        <span class="na">rest</span><span class="p">:</span> <span class="kr">string</span>
    <span class="p">}</span>

<span class="kd">type</span> <span class="nx">EggExpr</span>
    <span class="o">=</span> <span class="nx">ValueToken</span>
    <span class="o">|</span> <span class="nx">WordToken</span>
    <span class="o">|</span> <span class="nx">ApplyExpr</span>


<span class="kd">type</span> <span class="nx">ValueToken</span>
    <span class="o">=</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nx">ValueType</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="nx">Value</span> <span class="p">}</span>

<span class="kd">type</span> <span class="nx">WordToken</span>
    <span class="o">=</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nx">WordType</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="kr">string</span> <span class="p">}</span>

<span class="kd">type</span> <span class="nx">ApplyExpr</span>
    <span class="o">=</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nx">ApplyType</span><span class="p">,</span> <span class="na">operator</span><span class="p">:</span> <span class="nx">WordToken</span><span class="p">,</span> <span class="na">args</span><span class="p">:</span> <span class="nb">Array</span><span class="o">&lt;</span><span class="nx">EggExpr</span><span class="o">&gt;</span> <span class="p">}</span>


<span class="kd">type</span> <span class="nx">ApplyType</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">apply</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">type</span> <span class="nx">ValueType</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">value</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">type</span> <span class="nx">WordType</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">word</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">type</span> <span class="nx">Value</span> <span class="o">=</span> <span class="kr">number</span> <span class="o">|</span> <span class="kr">string</span>
</code></pre></div></div>

<p>The type literals are maybe unnecessary, but I wanted to try it as it’s not a feature I’ve used in other statically typed languages.</p>

<p><strong>Exercises: Arrays</strong></p>

<p>The implementation of array is straightforward:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">topScope</span><span class="p">.</span><span class="nx">array</span> <span class="o">=</span> <span class="p">(...</span><span class="nx">vals</span><span class="p">:</span> <span class="nb">Array</span><span class="o">&lt;</span><span class="nx">Value</span><span class="o">&gt;</span><span class="p">):</span> <span class="nb">Array</span><span class="o">&lt;</span><span class="nx">Value</span><span class="o">&gt;</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">vals</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">topScope</span><span class="p">.</span><span class="nx">length</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arr</span><span class="p">:</span> <span class="nb">Array</span><span class="o">&lt;</span><span class="nx">Value</span><span class="o">&gt;</span><span class="p">):</span> <span class="kr">number</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">topScope</span><span class="p">.</span><span class="nx">element</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arr</span><span class="p">:</span> <span class="nb">Array</span><span class="o">&lt;</span><span class="nx">Value</span><span class="o">&gt;</span><span class="p">,</span> <span class="nx">i</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">Value</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">val</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">val</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nx">RangeError</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">i</span><span class="p">}</span><span class="s2"> is out of range of array </span><span class="p">${</span><span class="nx">arr</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nx">val</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Since there is no error or undefined in the Egg language, attempting to index out of range throws an error.</p>

<p><strong>Exercise: Comments</strong></p>

<p>Following the hint, single-line comments starting with <code class="language-plaintext highlighter-rouge">#</code> are ignored. In this case, there is a recursive call required after consuming a comment line. Maybe there is a simpler, regex-based way to avoid the additiomal recursive call, but this seems simple enough.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">skipSpace</span><span class="p">(</span><span class="nx">s</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="kr">string</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">firstChar</span> <span class="o">=</span> <span class="nx">s</span><span class="p">.</span><span class="nx">search</span><span class="p">(</span><span class="sr">/</span><span class="se">\S</span><span class="sr">/</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">firstChar</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="dl">""</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">s_</span> <span class="o">=</span> <span class="nx">s</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">firstChar</span><span class="p">)</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">s_</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">#</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nx">skipSpace</span><span class="p">(</span><span class="nx">s_</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">s_</span><span class="p">.</span><span class="nx">search</span><span class="p">(</span><span class="sr">/</span><span class="se">\n</span><span class="sr">/</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">));</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nx">s_</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p><strong>Exercise: Fixing Scope and Adding a <code class="language-plaintext highlighter-rouge">set</code> operator</strong></p>

<p><code class="language-plaintext highlighter-rouge">set</code> traverses scope upwards in search of a variable name that should exist (as opposed to <code class="language-plaintext highlighter-rouge">define</code>, which is intended for new variables.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nx">specialForms</span><span class="p">.</span><span class="kd">set</span> <span class="o">=</span> <span class="p">(</span><span class="nx">args</span><span class="p">:</span> <span class="nb">Array</span><span class="o">&lt;</span><span class="nx">EggExpr</span><span class="o">&gt;</span><span class="p">,</span> <span class="nx">scope</span><span class="p">:</span> <span class="nx">object</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">args</span><span class="p">.</span><span class="nx">length</span> <span class="o">!=</span> <span class="mi">2</span> <span class="o">||</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">type</span> <span class="o">!=</span> <span class="dl">"</span><span class="s2">word</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nx">SyntaxError</span><span class="p">(</span><span class="dl">"</span><span class="s2">Incorrect use of set</span><span class="dl">"</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">name</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">;</span>
        <span class="kd">const</span> <span class="nx">val</span> <span class="o">=</span> <span class="nx">evaluate</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="nx">scope</span><span class="p">);</span>

        <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">scope</span><span class="p">,</span> <span class="nx">name</span><span class="p">))</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">getPrototypeOf</span><span class="p">(</span><span class="nx">scope</span><span class="p">)</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nx">ReferenceError</span><span class="p">(</span><span class="s2">`Could not find property </span><span class="p">${</span><span class="nx">name</span><span class="p">}</span><span class="s2"> to set with </span><span class="p">${</span><span class="nx">val</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="nx">scope</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getPrototypeOf</span><span class="p">(</span><span class="nx">scope</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="nx">scope</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">=</span> <span class="nx">val</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>Note that there’s no test framework, so I’m just compiling and testing with a series of <code class="language-plaintext highlighter-rouge">console.log</code> statements (and the <code class="language-plaintext highlighter-rouge">print</code> operator defined in Egg):</p>

<p><code class="language-plaintext highlighter-rouge">tsc egg.ts &amp;&amp; node egg.js</code></p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>Today was another short day, given long weekend activities. The Eloquent JavaScript is on the simpler side (certainly compared to Atencio), but it’s very clear and the exercises are helpful for my current level of JS. TypeScript didn’t get in the way today, and was quite helpful in forcing some upfront thought as to the exact types being manipulated. It’s hard to imagine coding without a live, reloading type checker…</p>

<p>In the remaining four days, I’m planning to work on the Eloquent Javascript browser-based and Node exercises, and maybe build something very simple like a todo list (by hand? with React or Vue?).</p>

<h2 id="todo--to-read">Todo / To Read</h2>

<ul>
  <li><del>More chapters of  Atencio</del></li>
  <li><a href="https://eloquentjavascript.net/">Eloquent JavaScript</a> has exercises at the end of chapters, which might be worth exploring</li>
  <li>Figure out how to use <code class="language-plaintext highlighter-rouge">outDir</code>(?) to compile <code class="language-plaintext highlighter-rouge">.js</code> files to separate subdirectory</li>
  <li>Compiling with both TypeScript with Babel</li>
  <li>Kyle Simpson, <a href="https://github.com/getify/You-Dont-Know-JS/tree/1st-ed">You Don’t Know JS</a></li>
  <li>Composing Software, by Eric Elliott (https:// leanpub.com/composingsoftware)</li>
</ul>]]></content><author><name></name></author><category term="javascript" /><category term="javascript" /><category term="typescript" /><category term="archive" /><summary type="html"><![CDATA[Today’s plan is to work through an Eloquent JavaScript chapter with the accompanying exercises.]]></summary></entry></feed>