
<feed xmlns="http://www.w3.org/2005/Atom">
    <generator>Hugo -- gohugo.io</generator>
    <title>
                Posts on
            
        
        The Neo-Babbage Files</title>
        <link href="https://babbagefiles.xyz/posts/atom.xml" rel="self" type="application/atom+xml" /><link href="https://babbagefiles.xyz/posts/"/>
    <updated>2025-12-29T11:00:32+00:00</updated>
    <author>
            <name>Benjamin Slade</name>
            
                <email>slade@lambda-y.net</email>
            </author>
    <id>https://babbagefiles.xyz/posts/</id>
        
        <entry>
            <title type="html"><![CDATA[Auto-sizing images in Kitty (if it fits it sits?)]]></title>
            <link href="https://babbagefiles.xyz/auto-sizing-images-in-kitty-terminal/"/>
            <id>https://babbagefiles.xyz/auto-sizing-images-in-kitty-terminal/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2025-11-08T21:50:00-06:00</published>
            <updated>2025-11-21T13:33:59-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>I&rsquo;ve been playing with <a href="https://babbagefiles.xyz/categories/terminals/">terminal emulators</a> again for a while. More to
write up at some point, but here a quick note on some customisations
for displaying images in the terminal&hellip;.</p>
<p>There are a couple of terminal emulators which allow for display of
arbitrary images in the terminal (I mean, jpegs and pngs and the like,
not (just) <a href="https://en.wikipedia.org/wiki/Sixel">sixel</a>), notably <a href="https://sw.kovidgoyal.net/kitty">Kitty</a> and <a href="https://wezterm.org">WezTerm</a>.</p>
<p>WezTerm with iTerm2 compatible image protocol support, and built-in
<a href="https://wezterm.org/cli/imgcat.html"><code>imgcat</code> command</a>, <strong>alongside of (experimental but seemingly working)</strong>
support for <a href="https://sw.kovidgoyal.net/kitty/kittens/icat/">Kitty&rsquo;s image display protocol</a> (enable this in WezTerm by
adding to your <code>wezterm.lua</code> config the line
<code>enable_kitty_graphics=true</code>).</p>
<p>I mainly use <a href="https://fishshell.com">Fish shell</a>, and here I record a couple of notes on
convenience customisations in Fish for working with in-terminal image
display in Kitty and WezTerm. But, I have a POSIX-compatible
(including Bash) version (less well tested) included below as well.</p>
<p>WezTerm&rsquo;s command to display an image in the terminal is <code>wezterm imgcat &lt;/path/to/image/file&gt;</code>, and Kitty&rsquo;s is <code>kitten icat &lt;/path/to/image/file&gt;</code>.</p>
<p>I&rsquo;m playing with both terminals, and have a hard time remembering
things, so one thing I&rsquo;ve done is to alias these commands in fish to
just <code>icat</code>.</p>
<p>More significantly, while WezTerm seems to have a smart auto-sizing of
the image to downsize the rendering to make sure the image fits fully
within the current terminal window, Kitty only seems to guarantee
downsized display to make sure the image fits the current width of the
terminal window, but does not downsize to fit the current height of
the terminal window.</p>
<p>Thus, in Kitty, we get things like this:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/auto-sizing-images-in-kitty-terminal/../ox-hugo/kitty-not-autosizing-image.png"/> </figure>

<p><strong>(&hellip;.hmm, boots aren&rsquo;t a kitty&hellip;)</strong></p>
<p>While in WezTerm, we get:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/auto-sizing-images-in-kitty-terminal/../ox-hugo/wezterm-auto-sizing-image.png"/> </figure>

<p>And with appropriate configuration from below added, the new
(easy-to-remember) <code>icat</code> command works to produce in-terminal image
display as in WezTerm:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/auto-sizing-images-in-kitty-terminal/../ox-hugo/kitty-with-new-icat-auto-sizing-image.png"/> </figure>

<p><strong>(&hellip;and there&rsquo;s the kitty in Kitty!)</strong></p>
<p>Here are the relevant lines that can be added to your shell
configuration file to both make the image display command in both
Kitty and WezTerm both simply <code>icat</code>, and also resize the display of the
image in Kitty to make sure it fits lengthwise in the current terminal
window:</p>
<h2 id="fish-shell">Fish shell</h2>
<div class="highlight"><pre class="chroma"><code class="language-fish" data-lang="fish"><span class="c">#################### BEGIN image display things ######################
</span><span class="c"></span>
<span class="c"># image display things for kitty [https://sw.kovidgoyal.net/kitty/]
</span><span class="c">#                        &amp; wezterm [https://wezfurlong.org/wezterm/]
</span><span class="c">#
</span><span class="c"># - use &#39;icat $path/to/image&#39; (in either) to display image in terminal
</span><span class="c"># - on inner workings of the image display protocols, see:
</span><span class="c">#   - for kitty:    https://sw.kovidgoyal.net/kitty/kittens/icat/
</span><span class="c">#   - for wezterm:  https://wezterm.org/cli/imgcat.html
</span><span class="c"></span>
<span class="c"># if we&#39;re in kitty:
</span><span class="c"></span><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$TERM</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;xterm-kitty&#34;</span> <span class="o">]</span>
  <span class="nb">alias </span>icat-full <span class="s2">&#34;kitten icat&#34;</span> <span class="c"># alias the default kitty icat behaviour to `icat-full&#39;
</span><span class="c"></span>
  <span class="c"># function icat-auto
</span><span class="c"></span>  <span class="k">function</span> icat  <span class="c"># define auto-resizing version of kitty&#39;s `icat&#39;
</span><span class="c"></span>    <span class="c"># calculate kitty window size parameters:
</span><span class="c"></span>    <span class="k">set</span> -l kittysize <span class="o">(</span>kitten icat --print-window-size<span class="o">)</span> <span class="c"># get kitty window size
</span><span class="c"></span>    <span class="k">set</span> -l kittywidth <span class="o">(</span><span class="k">echo</span> <span class="nv">$kittysize</span> <span class="o">|</span> cut -dx -f1<span class="o">)</span> <span class="c"># extract width of kitty window
</span><span class="c"></span>    <span class="k">set</span> -l kittyheight <span class="o">(</span><span class="k">echo</span> <span class="nv">$kittysize</span> <span class="o">|</span> cut -dx -f2<span class="o">)</span> <span class="c"># extract height of kitty window
</span><span class="c"></span>    <span class="k">set</span> -l argcount <span class="o">(</span><span class="k">count</span> <span class="nv">$argv</span><span class="o">)</span>    <span class="c"># count how argumnets to icat there are
</span><span class="c"></span>
    <span class="c"># calculate target image size parameters: [requires ImageMagick!]
</span><span class="c"></span>    <span class="k">set</span> -l imagesize <span class="o">(</span>identify -format <span class="s1">&#39;%wx%h&#39;</span> <span class="s2">&#34;</span><span class="nv">$argv</span><span class="s2">[</span><span class="nv">$argcount</span><span class="s2">]&#34;</span><span class="o">)</span>  <span class="c"># `identify&#39; is part of ImageMagick
</span><span class="c"></span>    <span class="k">set</span> -l imagewidth <span class="o">(</span><span class="k">echo</span> <span class="nv">$imagesize</span> <span class="o">|</span> cut -dx -f1<span class="o">)</span> <span class="c"># extract width of image
</span><span class="c"></span>    <span class="k">set</span> -l imageheight <span class="o">(</span><span class="k">echo</span> <span class="nv">$imagesize</span> <span class="o">|</span> cut -dx -f2<span class="o">)</span> <span class="c"># extract height of image
</span><span class="c"></span>    <span class="k">set</span> -l reducebyy 1.0  <span class="c"># initialise and set to &#39;100%&#39; [no change] y-axis reduction percentage
</span><span class="c"></span>    <span class="k">set</span> -l reducebyx 1.0  <span class="c"># initialise and set to &#39;100%&#39; [no change] x-axis reduction percentage
</span><span class="c"></span>
    <span class="c"># test if target image is bigger than kitty window
</span><span class="c"></span>    <span class="k">if</span> <span class="o">[</span> <span class="nv">$imageheight</span> -gt <span class="nv">$kittyheight</span> <span class="o">]</span> <span class="c"># if it&#39;s taller than the terminal window
</span><span class="c"></span>                                         <span class="c"># divide terminal window height by image height
</span><span class="c"></span>      <span class="k">set</span> reducebyy <span class="o">(</span><span class="nb">math</span> <span class="nv">$kittyheight</span> / <span class="nv">$imageheight</span><span class="o">)</span>
    <span class="k">end</span>
    <span class="k">if</span> <span class="o">[</span> <span class="nv">$imagewidth</span> -gt <span class="nv">$kittywidth</span> <span class="o">]</span>  <span class="c"># if it&#39;s widre than the terminal window
</span><span class="c"></span>                                         <span class="c"># divide terminal window width by image width
</span><span class="c"></span>      <span class="k">set</span> reducebyx <span class="o">(</span><span class="nb">math</span> <span class="nv">$kittywidth</span> / <span class="nv">$imagewidth</span><span class="o">)</span>
    <span class="k">end</span>
    <span class="k">if</span> <span class="o">[</span> <span class="nv">$reducebyy</span> -lt <span class="nv">$reducebyx</span> <span class="o">]</span>  <span class="c"># set both reducebyx, reducebyy to smallest of 2 values
</span><span class="c"></span>      <span class="k">set</span> reducebyx <span class="nv">$reducebyy</span>
    <span class="k">else</span>
      <span class="k">set</span> reducebyy <span class="nv">$reducebyx</span>
    <span class="k">end</span>
    <span class="c"># calculate target image height and width to fit fully in terminal window
</span><span class="c"></span>    <span class="k">set</span> imageheight <span class="o">(</span><span class="nb">math </span>floor <span class="o">(</span><span class="nb">math</span> <span class="nv">$reducebyy</span> x <span class="nv">$imageheight</span><span class="o">))</span>
    <span class="k">set</span> imagewidth <span class="o">(</span><span class="nb">math </span>floor <span class="o">(</span><span class="nb">math</span> <span class="nv">$reducebyx</span> x <span class="nv">$imagewidth</span><span class="o">))</span>

    <span class="c"># call the `kitten icat&#39; kitty command using the calculated image size
</span><span class="c"></span>    <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$argcount</span><span class="s2">&#34;</span> -gt <span class="m">1</span> <span class="o">]</span> <span class="c"># if the user has passed more args than just the target image filepath
</span><span class="c"></span>                             <span class="c"># then pass all of those separately along with `--use-window-size&#39; parameter
</span><span class="c"></span>      <span class="nb">command </span>kitten icat --use-window-size <span class="nv">$COLUMNS</span>,<span class="nv">$LINES</span>,<span class="s2">&#34;</span><span class="nv">$imagewidth</span><span class="s2">&#34;</span>,<span class="s2">&#34;</span><span class="nv">$imageheight</span><span class="s2">&#34;</span> <span class="nv">$argv</span><span class="o">[</span>1..<span class="o">(</span><span class="nb">math</span> <span class="o">(</span><span class="k">count</span> <span class="nv">$argv</span><span class="o">)</span> - 1<span class="o">)]</span> <span class="nv">$argv</span><span class="o">[(</span><span class="k">count</span> <span class="nv">$argv</span><span class="o">)]</span>
    <span class="k">else</span>
      <span class="nb">command </span>kitten icat --use-window-size <span class="nv">$COLUMNS</span>,<span class="nv">$LINES</span>,<span class="s2">&#34;</span><span class="nv">$imagewidth</span><span class="s2">&#34;</span>,<span class="s2">&#34;</span><span class="nv">$imageheight</span><span class="s2">&#34;</span> <span class="nv">$argv</span><span class="o">[</span>1<span class="o">]</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c"># if we&#39;re in WezTerm, alias `icat&#39; to wezterm&#39;s imgcat
</span><span class="c"></span>  <span class="k">else</span> <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$TERM_PROGRAM</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;WezTerm&#34;</span> <span class="o">]</span>
    <span class="nb">alias </span>icat <span class="s2">&#34;wezterm imgcat&#34;</span>
    <span class="c"># NOTE: currently not sure best way of emulating kitty&#39;s default image display behaviour
</span><span class="c"></span>    <span class="c">#      - mainly because I don&#39;t know how to get the WezTerm window size in a
</span><span class="c"></span>    <span class="c">#        fashion that&#39;s window-manager neutral and neutral between
</span><span class="c"></span>    <span class="c">#        graphical display protocol (i.e., X11 vs Wayland)
</span><span class="c"></span>
    <span class="c"># draft version of default kitty icat-like behaviour for `imgcat&#39;
</span><span class="c"></span>    <span class="c"># function icat-full
</span><span class="c"></span>    <span class="c">#   set -l imagesize (identify -format &#39;%w&#39; &#34;$argv[$argcount]&#34;)
</span><span class="c"></span>    <span class="c"># end
</span><span class="c"></span>  <span class="c"># alias icat-full &#34;wezterm imgcat --width 100%&#34;
</span><span class="c"></span><span class="k">end</span>

<span class="c">#################### END image display things ######################
</span></code></pre></div><h2 id="posix-shell--including-bash">POSIX shell (including Bash)</h2>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="c1">#################### BEGIN image display things ######################</span>

<span class="c1"># image display things for kitty [https://sw.kovidgoyal.net/kitty/]</span>
<span class="c1">#                        &amp; wezterm [https://wezfurlong.org/wezterm/]</span>
#
<span class="c1"># - use &#39;icat $path/to/image&#39; (in either) to display image in terminal</span>
<span class="c1"># - on inner workings of the image display protocols, see:</span>
<span class="c1">#   - for kitty:    https://sw.kovidgoyal.net/kitty/kittens/icat/</span>
<span class="c1">#   - for wezterm:  https://wezterm.org/cli/imgcat.html</span>

<span class="c1"># if we&#39;re in kitty:</span>
<span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$TERM</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;xterm-kitty&#34;</span> <span class="o">]</span>
<span class="k">then</span>
    <span class="nb">alias</span> <span class="nv">icat_full</span><span class="o">=</span><span class="s2">&#34;kitten icat&#34;</span> <span class="c1"># alias the default kitty icat behaviour to `icat-full&#39;</span>

    icat <span class="o">()</span> <span class="o">{</span> <span class="c1"># define auto-resizing version of kitty&#39;s `icat&#39;</span>
        <span class="c1"># calculate kitty window size parameters:</span>
        <span class="nv">kittysize</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>kitten icat --print-window-size<span class="k">)</span><span class="s2">&#34;</span> <span class="c1"># get kitty window size</span>
        <span class="nv">kittywidth</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">echo</span> <span class="nv">$kittysize</span> <span class="p">|</span> cut -dx -f1<span class="k">)</span><span class="s2">&#34;</span> <span class="c1"># extract width of kitty window</span>
        <span class="nv">kittyheight</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">echo</span> <span class="nv">$kittysize</span> <span class="p">|</span> cut -dx -f2<span class="k">)</span><span class="s2">&#34;</span> <span class="c1"># extract height of kitty window</span>

        <span class="c1"># calculate target image size parameters: [requires ImageMagick!]</span>
        <span class="nv">icat_last_arg_pos</span><span class="o">=</span><span class="s2">&#34;\${</span><span class="nv">$#</span><span class="s2">}&#34;</span>
        <span class="nv">icat_last_arg_content</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">eval</span> <span class="nb">echo</span> <span class="se">\&#34;</span><span class="nv">$icat_last_arg_pos</span><span class="se">\&#34;</span><span class="k">)</span><span class="s2">&#34;</span>
        <span class="nv">identify_command</span><span class="o">=</span><span class="s2">&#34;identify -format &#39;%wx%h&#39; \&#34;</span><span class="nv">$icat_last_arg_content</span><span class="s2">\&#34;&#34;</span> <span class="c1"># `identify&#39; is part of ImageMagick</span>
        <span class="nv">imagesize</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">eval</span> <span class="nv">$identify_command</span><span class="k">)</span><span class="s2">&#34;</span>
        <span class="nv">imagewidth</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">echo</span> <span class="nv">$imagesize</span> <span class="p">|</span> cut -dx -f1<span class="k">)</span><span class="s2">&#34;</span> <span class="c1"># extract width of image</span>
        <span class="nv">imageheight</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">echo</span> <span class="nv">$imagesize</span> <span class="p">|</span> cut -dx -f2<span class="k">)</span><span class="s2">&#34;</span> <span class="c1"># extract height of image</span>
        <span class="nv">reducebyy</span><span class="o">=</span><span class="m">100</span>  <span class="c1"># initialise and set to &#39;100%&#39; [no change] y-axis reduction percentage</span>
        <span class="nv">reducebyx</span><span class="o">=</span><span class="m">100</span>  <span class="c1"># initialise and set to &#39;100%&#39; [no change] x-axis reduction percentage</span>

        <span class="c1"># test if target image is bigger than kitty window</span>
        <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$imageheight</span><span class="s2">&#34;</span> -gt <span class="s2">&#34;</span><span class="nv">$kittyheight</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="c1"># if it&#39;s taller than the terminal window</span>
        <span class="k">then</span>
            <span class="c1"># divide terminal window height by image height</span>
            <span class="nv">reducebyy</span><span class="o">=</span><span class="k">$((</span> <span class="o">(</span> kittyheight <span class="o">*</span> <span class="m">100</span> <span class="o">)</span> <span class="o">/</span> imageheight <span class="k">))</span>
        <span class="k">fi</span>
        <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$imagewidth</span><span class="s2">&#34;</span> -gt <span class="s2">&#34;</span><span class="nv">$kittywidth</span><span class="s2">&#34;</span> <span class="o">]</span>  <span class="c1"># if it&#39;s wider than the terminal window</span>
        <span class="k">then</span>
            <span class="c1"># divide terminal window width by image width</span>
            <span class="nv">reducebyx</span><span class="o">=</span><span class="k">$((</span> <span class="o">(</span> kittywidth <span class="o">*</span> <span class="m">100</span> <span class="o">)</span> <span class="o">/</span> imagewidth <span class="k">))</span>
        <span class="k">fi</span>
        <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$reducebyy</span><span class="s2">&#34;</span> -lt <span class="s2">&#34;</span><span class="nv">$reducebyx</span><span class="s2">&#34;</span> <span class="o">]</span>  <span class="c1"># set both reducebyx, reducebyy to smallest of 2 values</span>
        <span class="k">then</span>
            <span class="nv">reducebyx</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$reducebyy</span><span class="s2">&#34;</span>
        <span class="k">else</span>
            <span class="nv">reducebyy</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$reducebyx</span><span class="s2">&#34;</span>
        <span class="k">fi</span>
        <span class="c1"># calculate target image height and width to fit fully in terminal window</span>
        <span class="nv">imageheight</span><span class="o">=</span><span class="k">$((</span> <span class="o">(</span> reducebyy <span class="o">*</span> imageheight <span class="o">)</span> <span class="o">/</span> <span class="m">100</span> <span class="k">))</span>
        <span class="nv">imagewidth</span><span class="o">=</span><span class="k">$((</span> <span class="o">(</span> reducebyx <span class="o">*</span> imagewidth <span class="o">)</span>  <span class="o">/</span> <span class="m">100</span> <span class="k">))</span>
        kitten icat --use-window-size <span class="nv">$COLUMNS</span>,<span class="nv">$LINES</span>,<span class="nv">$imagewidth</span>,<span class="nv">$imageheight</span> <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span>
    <span class="o">}</span>
    <span class="c1"># if we&#39;re in WezTerm, alias `icat&#39; to wezterm&#39;s imgcat</span>
<span class="k">elif</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$TERM_PROGRAM</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;WezTerm&#34;</span> <span class="o">]</span>
<span class="k">then</span>
    <span class="nb">alias</span> <span class="nv">icat</span><span class="o">=</span><span class="s2">&#34;wezterm imgcat&#34;</span>
<span class="k">fi</span>

<span class="c1">#################### END image display things ######################</span>
</code></pre></div><h2 id="note-about-built-in-capacity">Note about built-in capacity</h2>
<p>I confirmed with the developer, <a href="https://kovidgoyal.net/">Kovid Goyal</a>, that currently Kitty
does not have an &ldquo;autofit&rdquo; option (other than it&rsquo;s default autofit to
fit width), see <a href="https://github.com/kovidgoyal/kitty/issues/9201">GitHub issue #9201</a>. But Kovid seems to have gone ahead
and added a built-it <code>--fit</code> option for Kitty, so a built-in way of
managing image display with respect to window size should be
available natively in an upcoming Kitty release.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/terminals" term="terminals" label="terminals" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Trials and Visions of Internet Relay Chat]]></title>
            <link href="https://babbagefiles.xyz/no-one-can-be-told-what-irc-is-you-have-to-logon-for-yourself/"/>
            <id>https://babbagefiles.xyz/no-one-can-be-told-what-irc-is-you-have-to-logon-for-yourself/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2025-03-28T13:15:00-05:00</published>
            <updated>2025-04-02T17:35:21-05:00</updated>
            
            
            <content type="html"><![CDATA[<p>This was meant to be a shortish bit on a couple of points on a decent
<a href="https://en.wikipedia.org/wiki/IRC">IRC (Internet Relay Chat)</a> set-up, including:</p>
<ul>
<li>some sort of persistence (a &ldquo;<a href="https://en.wikipedia.org/wiki/BNC_(software)#IRC">bouncer</a>&rdquo; or the always-connected server)</li>
<li>use with some sort of IRC client in Emacs</li>
<li>some sort of reasonable mobile phone client</li>
</ul>
<p>It ended up longer than I intended.</p>
<h2 id="tldr">tldr</h2>
<ul>
<li>Using some of the more <a href="https://ircv3.net">IRCv3</a> tools, particularly the cluster of
things by <a href="https://emersion.fr">Simon Ser</a> and co (soju, goguma, gamja - see below) makes
for a much better IRC experience, and easily supports synchronised
access across multiple devices (e.g., desktop, laptops, mobile).
<ul>
<li>soju provides a very capable bouncer</li>
<li>goguma provides an excellent mobile client</li>
<li>gamja is a very user-friendly web-client front-end</li>
<li>and these three things can complement each other in a complete IRC
setup system</li>
</ul>
</li>
<li>we can make such a soju-based set-up work well with Emacs IRC clients</li>
<li>soju and gamja can be self-hosted; but there are also two
paid/hosted instances currently available:
<ul>
<li><a href="https://chat.sr.ht">SourceHut&rsquo;s chat.sr.ht</a> (on a fork of soju with a different feature-set)</li>
<li><a href="https://irctoday.com">IRC Today&rsquo;s service</a> (I think closer to mainline soju, but not certain)</li>
</ul>
</li>
<li>we can leverage some of the IRCv3 functions nicely in Emacs</li>
</ul>
<p>The most useful bits here may be notes on how to set up clients to
work with <a href="https://sourcehut.org/blog/2021-11-29-announcing-the-chat.sr.ht-public-beta">SourceHut&rsquo;s chat.sr.ht</a>, particularly Goguma and Emacs/ERC,
and some of the cool features of IRCv3 things [modernising IRC], like
the soju bouncer (which is used by chat.sr.ht), for which see the
section below &ldquo;<a href="https://babbagefiles.xyz/no-one-can-be-told-what-irc-is-you-have-to-logon-for-yourself/#smooth-and-modern-irc-implementation-and-practice--mobile-emacs">Smooth &amp; Modern IRC Implementation &amp; Practice
(mobile, Emacs)</a>&quot;, which you might jump to if you don&rsquo;t want to read a
bunch of preamble.</p>
<h2 id="history-theory-background">History, theory, background</h2>
<h3 id="pre-history-wee-chats-in-musl-pies">Pre-history: wee chats in musl pies</h3>
<p>Long ago now I spent a while setting up a RaspberryPi 3b (running <a href="https://docs.voidlinux.org/installation/musl.html">musl
flavoured Void Linux</a>) as a <a href="https://weechat.org/">Weechat</a> Server/Bouncer in order to make
using IRC less painful. This involved a lot of steps, including
setting up a <a href="https://www.noip.com/">NoIP</a> script and SSL certs with <a href="https://letsencrypt.org/">Let&rsquo;s Encrypt</a>, and setting
up scripts to auto-fetch new certs. But once up, it worked well
(unless my home internet went out), and Weechat had a nice mobile app,
so I could connect both on desktop/laptop with an <a href="https://github.com/the-kenny/weechat.el">Emacs IRC client</a> and
also had a pocket connection via my mobile
phone <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. This gave me a persistent connection and
log history and all that.</p>
<p>Then I started using <a href="https://matrix.org">Matrix</a> (=a modern, but heavy, &ldquo;IRC replacement&rdquo;,
itself theoretically a Slack, Discord, &amp;c. competitor) more, (in part) since some projects have
chosen it as a more modern/capable alternative to IRC, and, soon
after, it was the case that most of the IRC rooms I was in were
bridged to Matrix anyway. And then it seemed perhaps more convenient
just to access everything in one place, since some things were only
Matrix and not on IRC, it made sense for that to be Matrix.</p>
<p><a href="https://libera.chat/news/matrix-bridge-disabled-retrospective">And then the Matrix bridges mostly shut down.</a></p>
<h3 id="path-of-the-prodircal-son">Path of the prodIRCal son</h3>
<p>I toyed off and on with getting re-set up on IRC, but there has been a
lot of other things going on in life, and I didn&rsquo;t feel like trying to
set up my rpi mini-server IRC bouncer again.</p>
<p>I tried some other hosted IRC bouncer services. But some of these
wouldn&rsquo;t let me connect with on a VPN.</p>
<p>I found one good free (as in freedom and free as in no paisa) <a href="https://en.wikipedia.org/wiki/ZNC">ZNC</a>
bouncer service, which is FreeIRC.org <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>The ZNC bouncer worked fine on Emacs (with <a href="https://www.gnu.org/software/emacs/erc.html">ERC</a>; I&rsquo;ve gone back and
forth between ERC and <a href="https://www.nongnu.org/circe/">Circe</a>, but this time I was trying ERC
again).<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> And I eventually figured out how to connect to multiple
networks through the ZNC bouncer.</p>
<p>But the best thing I could find on mobile was the <a href="https://github.com/MCMrARM/revolution-irc">Revolution IRC
client</a>, and, while it&rsquo;s a nice enough front-end, it struggled with
maintaining a connection to the ZNC bouncer I was using. (I asked
about this in an IRC room, and the general consensus was that mobile&rsquo;s
just not a good place to try to do IRC, but I remembered that via the
<a href="https://github.com/ubergeek42/weechat-android">Weechat android app</a> or through <a href="https://glowing-bear.org/">Glowing Bear</a> I&rsquo;d had a good mobile IRC
experience years ago.)</p>
<h3 id="ircv3-and-other-korean-tubers">IRCv3, and other Korean tubers</h3>
<p>I also came across another IRC mobile app, <a href="https://codeberg.org/emersion/goguma">Goguma</a>, but couldn&rsquo;t get it
work. But, frustrated with the situation, I wanted to see if, assuming
I <em>could</em> somehow get it work, Goguma might be a better mobile
solution.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<p>I ultimately ended up stumbling across an <a href="https://lobste.rs/s/wy2jgl/goguma_irc_client_for_mobile_devices">interesting lobste.rs
discussion of Goguma</a> which pointed to it being part of a set of
<a href="https://ircv3.net">IRCv3</a>-aware software, including an IRC bouncer <a href="https://soju.im">soju</a>, which a number of
the commenters on the lobste.rs thread comparing the experience
favourably against using ZNC.</p>
<p>A cluster of IRCv3 things:<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<ul>
<li><a href="https://codeberg.org/emersion/goguma">Goguma</a> (Korean 고구마 <em>goguma</em> &ldquo;sweet potato&rdquo;): IRC mobile client written in Flutter by Simon Ser</li>
<li><a href="https://codeberg.org/emersion/gamja">Gamja</a> (Korean 감자 <em>gamja</em> &ldquo;potato&rdquo;): simple IRC web client written in Javascript by Simon Ser</li>
<li><a href="https://soju.im">Soju</a> (Korean 소주 <em>soju</em> &ldquo;a distilled alcoholic beverage&rdquo;): IRC bouncer written in Go by Simon Ser</li>
<li><a href="https://git.sr.ht/~delthas/senpai/">Senpai</a> (Japanese 先輩 <em>sèńpáí</em> &ldquo;senior in social standing or level of education/skill; an elder&rdquo;): a modern terminal IRC written in Go, started by &lsquo;<a href="https://sr.ht/~taiite/">taiite</a>', who handed development over to &lsquo;<a href="https://delthas.fr">delthas</a>'.</li>
<li><a href="https://github.com/ergochat/ergo">Ergo</a> (a play on ergonomic [and &ldquo;<a href="https://github.com/jlatt/ergonomadic">ergonomadic</a>&quot;] and Go(lang), so I suppose ultimately from Ancient Greek ἔργον <em>érgon</em> &ldquo;work&rdquo;, but not sure that&rsquo;s relevant): a modern IRC server written in Go [<a href="https://github.com/jlatt">Jeremy Latt</a>, <a href="https://danieloaks.net">Daniel Oaks</a>, <a href="https://cs.stanford.edu/people/slingamn">Shivaram Lingamneni</a>] implementing bleeding-edge IRCv3 features/support</li>
</ul>
<p>The first three things are the most relevant for us (well, me). Ergo
sounds great, but I&rsquo;m not <em>running</em> an IRC server myself at the moment;
and I&rsquo;ve got a whole wealth of choices of Emacs IRC clients if I&rsquo;m on
desktop/laptop, so Senpai doesn&rsquo;t seem relevant either for my use
case.</p>
<h3 id="trains-wayland-and-things-that-go">Trains, Wayland, and Things that Go</h3>
<p>The first three of the IRCv3-looking things are all by <a href="https://emersion.fr">Simon Ser</a>, who
was lately at <a href="https://drewdevault.com">Drew DeVault</a>&lsquo;s <a href="https://sourcehut.org">SourceHut</a>, but seems to have left
sometime in 2024<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> and now works at <a href="https://osrd.fr/">Open
Source Railway Designer</a> (something to do with making tools for railway
infrastructure simulation, which sounds quite cool: open source
trains). Ser obviously works a good bit on IRC-related software, and
also a lot of things in Go, and has been involved with <a href="https://wayland.freedesktop.org/">Wayland</a>-related
projected (taking over maintainership of <a href="https://swaywm.org">Sway</a> (a Wayland window
manager) and <a href="https://gitlab.freedesktop.org/wlroots/wlroots/">wlroots</a> (Wayland general purpose compositor underlying
Sway and other things) from DeVault <a href="https://drewdevault.com/2020/10/23/Im-handing-wlroots-and-sway-to-Simon.html">some years back</a>).</p>
<p>Here&rsquo;s Thomas Flament &amp; Simon Ser talking about IRCv3 and soju,
senpai, gamja, goguma at FOSDEM &lsquo;25 in Brussels:</p>
<p><strong><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-6407-chatting-on-irc-in-2025-grandpa-what-s-up-/">Chatting on IRC in 2025: grandpa, what&rsquo;s up?</a> [video link with captions]</strong></p>
<p>In any case, one of the paid-only features that SourceHut offers is
via <a href="https://man.sr.ht/chat.sr.ht/">chat.sr.ht</a>, an IRC bouncer, which is running on a <a href="https://git.sr.ht/~bitfehler/soju">fork of soju</a>,
with a web-client based on gamja.</p>
<p>As far as I can tell, Sourcehut&rsquo;s servers are now (all? mainly?) in
Amsterdam.<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup></p>
<p>chat.sr.ht isn&rsquo;t the only hosted IRC bouncer service which runs some
version of the soju bouncer, there is also <a href="https://irctoday.com">IRC Today</a> [<a href="https://lobste.rs/s/zc0xh7/irc_today_hosted_irc_bouncer">discussed also
on lobste.rs</a>], which is hosted in Paris on <a href="https://www.scaleway.com">Scaleway</a>
servers.<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup></p>
<p>So, there are at least two apparently EU-based hosted &ldquo;IRCv3-forward&rdquo;
bouncers.</p>
<h2 id="smooth-and-modern-irc-implementation-and-practice--mobile-emacs">Smooth &amp; Modern IRC Implementation &amp; Practice (mobile, Emacs)</h2>
<h3 id="how-to-manage-to-drink-rice-liquor-in-a-hut-preliminaries">How to manage to drink rice liquor in a hut: preliminaries</h3>
<p>In any case, I signed up for the SourceHut offering at chat.sr.ht,
which uses their fork of soju.</p>
<p>Here&rsquo;s what I did to get it set up for mobile and in Emacs (it seemed
worth documenting, in what was meant to be a short post&hellip;.):</p>
<p>The main things one needs to know are in the <a href="https://man.sr.ht/chat.sr.ht/">chat.sr.hut &lsquo;manpages&rsquo;</a>,
especially the <a href="https://man.sr.ht/chat.sr.ht/quickstart.md">&lsquo;quickstart guide&rsquo;</a>. But here are a few more notes for
specific set-up on Goguma and Emacs.</p>
<p>The first thing one needs to do for any &ldquo;third-party&rdquo; client (this
includes Goguma or any Emacs IRC client) is <a href="https://meta.sr.ht/oauth2/personal-token?grants=meta.sr.ht/PROFILE:RO">generate an OAuth 2.0
personal access token in your SourceHut account</a>. This will be
effectively your &ldquo;password&rdquo; (with your SourceHut login name as your
username).</p>
<p><strong>Nb</strong>: on mobile, you have to be careful to actually manage to copy the
entire token with &ldquo;Select all&rdquo; or the like - I was stuck for a long
time not being able to make Goguma work because I&rsquo;d not managed to
copy the entire token, and this wasn&rsquo;t at all obvious in a mobile
browser. On desktop, it&rsquo;s not an issue.</p>
<h3 id="goguma--mobile--one-potato-two-potato-sweet-potato">Goguma (mobile): One potato, two potato, sweet potato</h3>
<p>For Goguma, you&rsquo;re just going to put in:</p>
<ul>
<li>Server: <code>chat.sr.ht</code></li>
<li>Nickname: &lt;your SourceHut username&gt;</li>
<li>Password: &lt;an OAuth2.0 token you generated as above&gt;</li>
</ul>
<p>And you&rsquo;re in. And here&rsquo;s what it looks like:</p>



<figure>
    <a href="/ox-hugo/goguma-sr.ht.png">
        <img src="https://babbagefiles.xyz/ox-hugo/goguma-sr.ht.png" alt="Figure 1: Goguma with normal &ldquo;bubble&rdquo; display mode in #sr.ht@libera.chat"/> </a><figcaption>
                
                <p>
                    <span class="figure-number">Figure 1: </span>Goguma with normal &ldquo;bubble&rdquo; display mode in #sr.ht@libera.chat
                    
                        
                        </p>
                
            </figcaption></figure>

<p>And there&rsquo;s a setting in Goguma (&ldquo;Compact message list&rdquo;) to make it
look less like a mobile messaging app and just have plain text if you
prefer that:</p>



<figure>
    <a href="/ox-hugo/goguma-sr.ht_compact.png">
        <img src="https://babbagefiles.xyz/ox-hugo/goguma-sr.ht_compact.png" alt="Figure 2: Goguma in &ldquo;compact message list&rdquo; display mode in #sr.ht@libera.chat"/> </a><figcaption>
                
                <p>
                    <span class="figure-number">Figure 2: </span>Goguma in &ldquo;compact message list&rdquo; display mode in #sr.ht@libera.chat
                    
                        
                        </p>
                
            </figcaption></figure>

<p>And you can have Goguma show you notifications if someone mentions
your nick. Here is an example showing on a Pebble watch:</p>



<figure>
    <a href="/ox-hugo/goguma-pebble-notification.jpg">
        <img src="https://babbagefiles.xyz/ox-hugo/goguma-pebble-notification.jpg" alt="Figure 3: Goguma nick mention notification displaying on Pebble watch"/> </a><figcaption>
                
                <p>
                    <span class="figure-number">Figure 3: </span>Goguma nick mention notification displaying on Pebble watch
                    
                        
                        </p>
                
            </figcaption></figure>

<p>(IRC on one&rsquo;s wrist; it truly is 2025.)</p>
<h3 id="emacs-machines-and-authority-tokens-what-to-put-in-your-authinfo-dot-gpg-and-init-dot-el">Emacs machines and authority tokens: what to put in your authinfo.gpg and init.el</h3>
<p>In your <code>~/.authinfo.gpg</code> file, add a line like this (with &lt;&gt;&lsquo;ed items
to be replaced fully; don&rsquo;t actually include any <code>&lt;&gt;</code>'s here or above or
elsewhere; where <code>&quot;my-sourcehut-id&quot;</code> is your actual sourcehut login id
in quotes):</p>
<p><code>machine chat.sr.ht login &quot;my-sourcehut-id&quot; password &quot;&lt;one of your OAuth 2.0 tokens generated at SourceHut meta&gt;&quot;</code></p>
<p>And then in your <code>~.emacs.d/init.el</code>, assuming you&rsquo;re using ERC and have
that configured otherwise to your liking, make a user-function for
each actual IRC server you want to connect with via chat.sr.ht/soju,
like this (assuming your user-id is <code>&quot;my-sourcehut-id&quot;</code>: replace
appropriately— and you might have different <code>:nick</code>'s (&ldquo;nicknames&rdquo; ≈
user-handles/user-names/identities) on different servers, of course,
so adjust those as well; but your <code>:username</code> is going to be
consistently your SourceHut username followed by
&ldquo;/&lt;the-particular-irc-server-address&gt;&quot;):</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp">
<span class="c1">;; just so we don&#39;t have to type it each time, a wrapping `let&#39;,</span>
<span class="c1">;; and so after this our `server&#39;, `port&#39;, &amp; `passwd&#39; will be thus defined</span>
<span class="c1">;; (you could do `:nick&#39; like this too if it&#39;s the same everywhere)</span>
<span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">server</span> <span class="s">&#34;chat.sr.ht&#34;</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">port</span> <span class="mi">6697</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">passwd</span>
       <span class="p">(</span><span class="nv">cadr</span>
        <span class="p">(</span><span class="nv">auth-source-user-and-password</span> <span class="s">&#34;chat.sr.ht&#34;</span> <span class="s">&#34;my-sourcehut-id&#34;</span><span class="p">))))</span>
  <span class="c1">;; `auth-source-user-and-password&#39; returns a list of `login&#39; and `password&#39;</span>
  <span class="c1">;; from your .authinfo.gpg; but we just need the second, so `cadr&#39; (= return</span>
  <span class="c1">;; the `car&#39; of the `cdr&#39;), because since the list is going to be something</span>
  <span class="c1">;; like `(mylogin mypassword)&#39;, will give us just the atomic `mypassword&#39;,</span>
  <span class="c1">;; which is what we want [because the `cdr&#39; of `(mylogin mypassword)&#39; will be</span>
  <span class="c1">;; `(mypassword)&#39; and the `car&#39; of `(mypassword)&#39; will be just `mypassword&#39;].)</span>

  <span class="p">(</span><span class="nb">defun</span> <span class="nv">erc-libera</span> <span class="p">()</span>
    <span class="s">&#34;Connect to Libera.Chat IRC server.&#34;</span>
    <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">erc-tls</span>
     <span class="nb">:nick</span> <span class="s">&#34;emacsuser007&#34;</span>
     <span class="nb">:server</span> <span class="nv">server</span>
     <span class="nb">:port</span> <span class="nv">port</span>
     <span class="nb">:user</span> <span class="s">&#34;my-sourcehut-id/irc.libera.chat&#34;</span>
     <span class="nb">:password</span> <span class="nv">passwd</span><span class="p">))</span>

  <span class="p">(</span><span class="nb">defun</span> <span class="nv">erc-oftc</span> <span class="p">()</span>
    <span class="s">&#34;Connect to OFTC IRC server.&#34;</span>
    <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">erc-tls</span>
     <span class="nb">:nick</span> <span class="s">&#34;emacsuser007&#34;</span>
     <span class="nb">:server</span> <span class="nv">server</span>
     <span class="nb">:port</span> <span class="nv">port</span>
     <span class="nb">:user</span> <span class="s">&#34;my-sourcehut-id/irc.oftc.net&#34;</span>
     <span class="nb">:password</span> <span class="nv">passwd</span><span class="p">))</span>

  <span class="p">(</span><span class="nb">defun</span> <span class="nv">erc-ircnow</span> <span class="p">()</span>
    <span class="s">&#34;Connect to IRCNow IRC server.&#34;</span>
    <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">erc-tls</span>
     <span class="nb">:nick</span> <span class="s">&#34;emacsuser007&#34;</span>
     <span class="nb">:server</span> <span class="nv">server</span>
     <span class="nb">:port</span> <span class="nv">port</span>
     <span class="nb">:user</span> <span class="s">&#34;my-sourcehut-id/irc6.ircnow.org&#34;</span>
     <span class="nb">:password</span> <span class="nv">passwd</span><span class="p">))</span>

  <span class="p">(</span><span class="nb">defun</span> <span class="nv">erc-ergo</span> <span class="p">()</span>
    <span class="s">&#34;Connect to IRCv3-forward Ergo IRC server.&#34;</span>
    <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">erc-tls</span>
     <span class="nb">:nick</span> <span class="s">&#34;emacsuser007&#34;</span>
     <span class="nb">:server</span> <span class="nv">server</span>
     <span class="nb">:port</span> <span class="nv">port</span>
     <span class="nb">:user</span> <span class="s">&#34;my-sourcehut-id/irc.ergo.chat&#34;</span>
     <span class="nb">:password</span> <span class="nv">passwd</span><span class="p">)))</span>
</code></pre></div><p>Here we&rsquo;re making connections for <a href="https://libera.chat">Libera</a>, <a href="https://www.oftc.net/">OFTC</a>, <a href="https://ircnow.org">IRCNow</a>, and <a href="https://ergo.chat/about-network">Ergo</a>, the
IRCv3-forward server implemented in Go we mentioned above.<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup></p>
<p>But you can add lots of different servers to your bouncer, and both in
Goguma and Emacs/ERC (just make a new <code>erc-&lt;server&gt;</code> function as above),
you&rsquo;ll just be able to get to all of the different rooms in one place
and don&rsquo;t necessary need to worry about which server a particular
thing is on after getting it set up.</p>
<p>And then you can make a &ldquo;connect to all the things function&rdquo; like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">erc-connect-all</span> <span class="p">()</span>
  <span class="s">&#34;Connect to all IRC servers above.&#34;</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="c1">;; we&#39;re just calling all of the functions we defined above here:</span>
  <span class="p">(</span><span class="nv">erc-libera</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">erc-oftc</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">erc-ircnow</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">erc-ergo</span><span class="p">))</span>
</code></pre></div><p>And what it looks like (well, it looks like however you&rsquo;ve configured
ERC or whatever Emacs IRC client you&rsquo;re using, but, in case you want
to see a possible way it could look):</p>



<figure>
    <a href="/ox-hugo/erc-sr.ht.png">
        <img src="https://babbagefiles.xyz/ox-hugo/erc-sr.ht.png" alt="Figure 4: in ERC on chat.sr.ht visiting #sr.ht@libera.chat"/> </a><figcaption>
                
                <p>
                    <span class="figure-number">Figure 4: </span>in ERC on chat.sr.ht visiting #sr.ht@libera.chat
                    
                        
                        </p>
                
            </figcaption></figure>

<p>If you get knocked offline momentarily, ERC will try to reconnect to
the bouncer. If you are knocked off or close Emacs for a while, you
might want to catch up on what you missed recently. And there&rsquo;s a nice
IRCv3 feature implemented in soju (and in chat.sr.ht) for this, as
discussed in the next section.</p>
<h3 id="leveraging-ircv3-features-in-emacs">Leveraging IRCv3 features in Emacs</h3>
<p>We can also do another convenient thing for our Emacs IRC
configuration now. IRCv3 defines a &ldquo;chathistory&rdquo; extension, as an easy
way of retrieving earlier IRC room content. This doesn&rsquo;t work most
places, but it works on any bouncers based on soju (see notes below),
the syntax being <code>/quote CHATHISTORY LATEST &lt;channelname&gt; * &lt;number-of-lines-to-fetch&gt;</code>.</p>
<p>We can make this more user-friendly in Emacs with something like
this, an interactive command that fetches history for whatever IRC
room it&rsquo;s called in (a 100 lines by default; prefix <code>C-u</code> to enter a
different number):</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">erc-chatsrht-give-me-more-irc-history</span> <span class="p">()</span>
  <span class="s">&#34;Get more history for current IRC buffer (IRCv3 only).
</span><span class="s">
</span><span class="s">Defaults to 100 lines of history; when C-u prefixed, asks user for
</span><span class="s">number of lines to fetch.
</span><span class="s">
</span><span class="s">If using an IRCv3 capable server/bouncer (like chat.sr.ht), fetch the
</span><span class="s">chat history via the IRCv3 chathistory extension. (Currently, only
</span><span class="s">soju-based servers implement this feature; see:
</span><span class="s">https://ircv3.net/software/clients)
</span><span class="s">
</span><span class="s">For more on chathistory, see:
</span><span class="s"> - https://man.sr.ht/chat.sr.ht/bouncer-usage.md#chat-history-logs
</span><span class="s"> - https://ircv3.net/specs/extensions/chathistory
</span><span class="s"> - https://soju.im/doc/soju.1.html&#34;</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nv">not</span> <span class="p">(</span><span class="nf">member</span>
            <span class="p">(</span><span class="nb">with-current-buffer</span> <span class="p">(</span><span class="nf">current-buffer</span><span class="p">)</span>
              <span class="nv">major-mode</span><span class="p">)</span>
            <span class="o">&#39;</span><span class="p">(</span><span class="nv">erc-mode</span>
              <span class="nv">circe-mode</span>
              <span class="nv">rcirc-mode</span><span class="p">)))</span>
      <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;not an IRC buffer; ignoring&#34;</span><span class="p">)</span>
    <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">lines</span> <span class="mi">100</span><span class="p">)</span>
          <span class="p">(</span><span class="nv">channel</span> <span class="p">(</span><span class="nf">buffer-name</span><span class="p">)))</span>
      <span class="p">(</span><span class="nb">when</span> <span class="nv">current-prefix-arg</span>
        <span class="p">(</span><span class="nb">progn</span>
          <span class="p">(</span><span class="nb">setq</span> <span class="nv">lines</span>
                <span class="p">(</span><span class="nv">read-number</span> <span class="p">(</span><span class="nf">format</span> <span class="s">&#34;How many lines to fetch: &#34;</span><span class="p">)</span> <span class="nv">lines</span><span class="p">))))</span>
      <span class="p">(</span><span class="nv">erc-send-input</span>
       <span class="p">(</span><span class="nf">concat</span> <span class="s">&#34;/quote CHATHISTORY LATEST &#34;</span> <span class="nv">channel</span> <span class="s">&#34; * &#34;</span> <span class="p">(</span><span class="nf">number-to-string</span> <span class="nv">lines</span><span class="p">))</span>
       <span class="no">t</span><span class="p">))))</span>
</code></pre></div><p>Then you can just call <code>erc-chatsrht-give-me-more-irc-history</code> in an IRC
buffer (you might find a convenient keybinding for it) to get prior
IRC chat in that buffer.</p>
<p>This sort of thing makes Emacs a very pleasant environment to manage
IRC in.</p>
<p>Now, for actually adding servers to your chat.sr.ht bouncer in the
first place, you might try out the gamja web client (see <a href="https://babbagefiles.xyz/no-one-can-be-told-what-irc-is-you-have-to-logon-for-yourself/#web-client-the-sweetness-of-non-sweet-potatoes-on-the-web">below</a>).</p>
<h3 id="inline-images-and-files-in-goguma-and-emacs">Inline images and files in Goguma and Emacs</h3>
<p>Oh. Gamja (as least as implemented by chat.sr.ht) doesn&rsquo;t seem to do it, but
Goguma fetches image links and displays a preview:</p>



<figure>
    <a href="/ox-hugo/irc-df-image-goguma.jpg">
        <img src="https://babbagefiles.xyz/ox-hugo/irc-df-image-goguma.jpg" alt="Figure 5: in the goguma mobile client, an image preview in #dwarffortress@libera.chat"/> </a><figcaption>
                
                <p>
                    <span class="figure-number">Figure 5: </span>in the goguma mobile client, an image preview in #dwarffortress@libera.chat
                    
                        
                        </p>
                
            </figcaption></figure>

<p>[Note: chat.sr.ht&rsquo;s soju fork doesn&rsquo;t currently offer direct
file-upload and is perhaps unlikely to, but this seems to be
implemented in upstream soju and maybe on IRC Today.]</p>
<p>Additional note: Goguma doesn&rsquo;t do this by default; you need to go
into the main &ldquo;Settings&rdquo; menu and enable &ldquo;Display link previews&rdquo;. (I
also have &ldquo;Send &amp; display typing indicators&rdquo; turned on here.)</p>
<p>And you can make ERC do similarly, with respect to fetching posted
image links, with the <a href="https://melpa.org/#/erc-image">erc-image package from MELPA</a>. I have a
<code>use-package</code> definition like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">use-package</span> <span class="nv">erc-image</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:after</span> <span class="nv">erc</span>
  <span class="nb">:config</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">erc-image-inline-rescale</span> <span class="mi">300</span><span class="p">)</span> <span class="c1">; maybe set bigger</span>
  <span class="p">(</span><span class="nv">add-to-list</span> <span class="ss">&#39;erc-modules</span> <span class="ss">&#39;image</span><span class="p">))</span>
</code></pre></div><p>And then you&rsquo;ll see something like this if you post an image link in ERC:</p>



<figure>
    <a href="/ox-hugo/irc-df-image-erc.png">
        <img src="https://babbagefiles.xyz/ox-hugo/irc-df-image-erc.png" alt="Figure 6: in ERC, an image preview in #dwarffortress@libera.chat"/> </a><figcaption>
                
                <p>
                    <span class="figure-number">Figure 6: </span>in ERC, an image preview in #dwarffortress@libera.chat
                    
                        
                        </p>
                
            </figcaption></figure>

<p>I&rsquo;d like to figure out how one might better manage it, but — in terms
of handling posting local files — there&rsquo;s a (not-on-MELPA) package
<a href="https://github.com/ecraven/imgbb.el/">imgbb.el</a> which will upload images in Emacs (when passed a file-path or
interactively from a file-chooser) to <a href="https://imgbb.com/">ImgBB</a>, and then put the link in
the clipboard.<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup></p>
<p>[Note: For better or worse, <code>erc-image</code> will pull images from http as
well as https addresses; while Goguma seems only to do the latter.]</p>
<h3 id="web-client-the-sweetness-of-non-sweet-potatoes-on-the-web">Web-client: The sweetness of non-sweet potatoes on the web</h3>
<p>The web interface for chat.sr.ht (at <a href="https://chat.sr.ht">https://chat.sr.ht</a>) is, again,
based on gamja (&ldquo;potato&rdquo;) (discussed above) and gamja is a nice
interface indeed.</p>



<figure>
    <a href="/ox-hugo/gamja-sr.ht.png">
        <img src="https://babbagefiles.xyz/ox-hugo/gamja-sr.ht.png" alt="Figure 7: in the gamja web-client at chat.sr.ht visiting #sr.ht@libera.chat"/> </a><figcaption>
                
                <p>
                    <span class="figure-number">Figure 7: </span>in the gamja web-client at chat.sr.ht visiting #sr.ht@libera.chat
                    
                        
                        </p>
                
            </figcaption></figure>

<p>And, for making your initial connections to different IRC servers, it
may be easier just to use this web interface, especially if you&rsquo;ve
already got accounts set up on any of the servers, as for most of them
you&rsquo;ll be able to enter your username and password and so on in the
&ldquo;Add server&rdquo; interface and not have to do all of the <code>/msg NickServ ....</code> stuff one usually does. (If you don&rsquo;t have an account already,
you&rsquo;ll have to register in someway, maybe by messaging Nick).</p>
<p>Looks like this:</p>



<figure>
    <a href="/ox-hugo/gamja-add-network.png">
        <img src="https://babbagefiles.xyz/ox-hugo/gamja-add-network.png" alt="Figure 8: in the gamja web-client at chat.sr.ht in the server-add menu"/> </a><figcaption>
                
                <p>
                    <span class="figure-number">Figure 8: </span>in the gamja web-client at chat.sr.ht in the server-add menu
                    
                        
                        </p>
                
            </figcaption></figure>

<p>In fact, even for signing in/up on a brand new server, at least on some
servers, gamja lets you click on &ldquo;login&rdquo; or &ldquo;register&rdquo; and pulls up a
pop-up box to fill in password, email, &amp;c. So it&rsquo;s quite convenient
(and you may not have to message Nick manually).</p>
<h2 id="wrap-up-and-evaluation">Wrap-up &amp; evaluation</h2>
<p>There are interesting IRC things still going on, both in terms of
active IRC communities (though this varies; #emacs is quite active,
#dwarffortress isn&rsquo;t; Discord has eaten into some spaces), and
innovations in IRC technologies and tools.</p>
<p>I&rsquo;m not sure what the best thing is terms of choice of a soju/gajma
ecosystem. chat.sr.ht seems to be the cheapest hosted option; IRC
Today is more expensive but may currently be more
featureful. SourceHut has had trouble with being DDOS&rsquo;ed/going
off-line (see, e.g., <a href="https://sourcehut.org/blog/2024-01-19-outage-post-mortem/">SourceHut network outage
post-mortem</a>).</p>
<p>Self-hosted set-ups are another option, but are likely to be more
fiddly/effort to run/maintain than a &ldquo;professional&rdquo; service. But here
are a few links to descriptions of people&rsquo;s self-hosted soju setups:</p>
<ul>
<li>self-hosting on <a href="#d41d8c">Alpine Linux</a>: <a href="https://wiredspace.de/blog/self-hosting-soju/">Self-Hosting Soju</a></li>
<li>self-hosting on <a href="#d41d8c">OpenBSD</a>: <a href="https://whynothugo.nl/journal/2024/01/12/setting-up-an-irc-bouncer-soju-on-openbsd/">Setting up an IRC bouncer (soju) on OpenBSD · Hugo&rsquo;s weblog</a></li>
<li>more self-hosting on <a href="#d41d8c">Alpine Linux</a>: <a href="https://isaacganoung.com/posts/2024/03/setting-up-self-hosted-soju/">Self-Hosted IRC with Soju &amp; Senpai | Isaac Ganoung&rsquo;s Website</a></li>
<li>self-hosting on <a href="#d41d8c">IRCNow</a>: <a href="https://wiki.ircnow.org/pmwiki.php?n=Soju.Guide">IRCNow | Soju / Guide</a></li>
</ul>
<p>Currently, chat.sr.ht is working well for me, even if it may be behind
on some mainline soju features.</p>
<h2 id="appendix-etymologies">Appendix: etymologies</h2>
<p>You probably don&rsquo;t need to know any of this. It&rsquo;s not going to help
you log into Goguma or make your Emacs config work.</p>
<p>But the naming of a lot of the IRC things above is strange and I&rsquo;m a
linguist by trade and it&rsquo;s hard for me to avoid paying a lot of
attention to words and meanings of words and connections of words.</p>
<p>(While on the topic of mostly irrelevant things: I have some
other-language interference (Hindi here) with the Korean names of some
of the software discussed here. Specifically, <em>gamja</em> makes me think
either गंजा <em>ganjā</em> &ldquo;bald&rdquo; or गाँजा <em>gā̃jā</em> &ldquo;ganja, cannabis&rdquo;; and <em>soju</em> keeps
making me think of an elided version of सो जाऊं <em>so jāū̃</em> &ldquo;shall I go to
sleep?&rdquo; [&ldquo;नहीं! आप आई॰आर॰सी॰ बाउंसर हैं, आपको सो नहीं जाना चाहिए! No!
You&rsquo;re an IRC bouncer: you should not go to sleep!&quot;])</p>
<p>At any rate, here are, unasked for, etymological notes on the four IRCv3
things with Korean or Japanese names, largely sourced from Wiktionary,
as indicated, with some connecting text:</p>
<h3 id="goguma">Goguma</h3>
<p>[mainly sourced from:
<a href="https://en.wiktionary.org/wiki/%EA%B3%A0%EA%B5%AC%EB%A7%88">https://en.wiktionary.org/wiki/%EA%B3%A0%EA%B5%AC%EB%A7%88</a>]</p>
<p>Named apparently after Korean 고구마 <em>goguma</em> &ldquo;sweet potato&rdquo; (cp. the
web client by the same team, gamja &ldquo;potato&rdquo;). First attested in the
<em>Mulmyeonggo</em> (물명고 / 物名考), 1824, as Early Modern Korean <em>고금아</em>
(Yale: <em>kokuma</em>), borrowed from Japanese <em>孝行芋</em> (<em>kōkō imo</em>), a term used
in the Tsushima dialect. Some earlier attestations are known, but they
are in the context of quoting the dialectal Japanese word, not in a
Korean context.</p>
<p>There&rsquo;s also apparently a recent &ldquo;Internet slang&rdquo; sense: &ldquo;a plot
development which frustrates the reader (e.g. the protagonist fails to
achieve their goal)&rdquo; [from c. 2012].</p>
<p>The Japanese word <em>孝行芋</em> (<em>kōkō imo</em>) from which Korean 고구마 <em>goguma</em> is
borrowed is apparently 孝行 (<em>kōkō</em>) [? &ldquo;dutiful&rdquo;??] + 芋 (<em>imo</em>) from Old
Japanese, attested in the Nihon Shoki of 720, referring to &ldquo;edible
tubers&rdquo;. May be a shift from older form うも (<em>umo</em>), ultimately from
Proto-Japonic *umo. Cognate with Okinawan 芋 (<em>&lsquo;nmu</em>). [see:
<a href="https://en.wiktionary.org/wiki/%E5%AD%9D%E8%A1%8C%E8%8A%8B#Japanese">https://en.wiktionary.org/wiki/%E5%AD%9D%E8%A1%8C%E8%8A%8B#Japanese</a>
<a href="https://en.wiktionary.org/wiki/%E8%8A%8B#Japanese">https://en.wiktionary.org/wiki/%E8%8A%8B#Japanese</a> ]</p>
<p>The Japanese word 芋 (<em>imo</em>) is connected to Chinese 芋 [Old <em>*ɢʷas</em>,
Modern Mandarin <em>yù</em> ] &ldquo;taro&rdquo;.</p>
<p>Seemingly a phono-semantic compound (形聲 / 形声, OC *ɢʷa, *ɢʷas):
semantic 艸 (“grass; plant”) + phonetic 于 (OC *ɢʷa) – &ldquo;taro&rdquo;.</p>
<p>Compare Proto-Hmong-Mien <em>*wouH</em> (“taro”), Burmese <em>ဝ</em> (<em>wa.</em>, “elephant
foot yam”), Tibetan གྲོ་མ (<em>gro ma</em>, “ <em>Argentina anserina</em> (syn. <em>Potentilla
anserina</em>), a plant with small edible tubers”).</p>
<p>There are various theories on how all these words are related:</p>
<ul>
<li>
<p>Schuessler (2007) considers it to be an areal word, comparing it to
the Hmong-Mien and Burmese words. Schuessler (2015) does not
consider the Tibetan word to be cognate.</p>
</li>
<li>
<p>Blench (2012) suggests that the Chinese word is borrowed from
Proto-Hmong-Mien and that the Burmese word may be a late loan from
Old Chinese.</p>
</li>
<li>
<p>STEDT reconstructs Proto-Sino-Tibetan <em>*g/s-rwa</em> (“taro; yam; tuber”),
whence the Tibetan word. This etymon is regarded as allofamically
related this word and 薯 (<em>OC *djas</em>).</p>
</li>
<li>
<p>Gong Hwang-cherng (2002) and Baxter and Sagart (2017) also suggest
that this word is related to the Tibetan word.</p>
<p>[on the Chinese, see: <a href="https://en.wiktionary.org/wiki/%E8%8A%8B">https://en.wiktionary.org/wiki/%E8%8A%8B</a>]</p>
</li>
</ul>
<h3 id="gamja">Gamja</h3>
<p>[sourced from: <a href="https://en.wiktionary.org/wiki/%EA%B0%90%EC%9E%90">https://en.wiktionary.org/wiki/%EA%B0%90%EC%9E%90</a>]</p>
<p>Seemingly named from Korean 감자 <em>gamja</em> &ldquo;potato&rdquo;, which is a
nativisation of the Sino-Korean term 감저 (甘藷, <em>gamjeo</em>, “lesser yam
(<em>Dioscorea esculenta</em>)”). First attested 1766 in Korea, then referring
to the sweet potato (<em>Ipomoea batatas</em>). The word came to refer to both
&ldquo;potato&rdquo; and &ldquo;sweet potato&rdquo; in the nineteenth century, and later lost
its original meaning. (So <em>gamja</em> meant &ldquo;sweet potato&rdquo; first; but
partially supplanted by <em>goguma</em>.)</p>
<p>With 甘藷, <em>gamjeo</em> apparently borrowed from Early Modern Korean 감져
(<em>kamcye</em>), itself from 甘藷
[<a href="https://en.wiktionary.org/wiki/%E7%94%98%E8%97%B7">https://en.wiktionary.org/wiki/%E7%94%98%E8%97%B7</a>].</p>
<p>[There&rsquo;s also homophonous verbal form: 감자 (gamja) (<em>plain hortative of</em> 감다)</p>
<ol>
<li>
<p>let&rsquo;s close (our eyes)</p>
</li>
<li>
<p>let&rsquo;s wash</p>
<p>which is presumably unrelated.]</p>
</li>
</ol>
<h3 id="soju">Soju</h3>
<p>[source: <a href="https://en.wikipedia.org/wiki/Soju">https://en.wikipedia.org/wiki/Soju</a>]</p>
<p>Seemingly named for Korean <em>Soju</em> (소주; 燒酒), which means &ldquo;burned
liquor&rdquo;: with the first syllable, <em>so</em> (소; 燒; &ldquo;burn&rdquo;), referring to
the heat of distillation and the second syllable, <em>ju</em> (주; 酒), meaning
&ldquo;alcoholic drink&rdquo;. Etymological dictionaries record that China&rsquo;s
<em>shaozhou</em> (<em>shāojiǔ</em>, 烧酒), Japan&rsquo;s <em>shochu</em> (<em>shōchū</em>, 焼酎), and Korea&rsquo;s
<em>soju</em> (<em>soju</em>, 燒酒) have the same etymology. [A Wikipedian has here
added a note about unreliable sources(?).]</p>
<p>Another name for soju is <em>noju</em> (노주; 露酒; &ldquo;dew liquor&rdquo;), with its
first syllable, <em>no/ro</em> (노/로; 露; &ldquo;dew&rdquo;), likening the droplets of the
collected alcohol during the distilling process to dewdrops.</p>
<p>The origin of soju dates back to 13th-century Goryeo. The <a href="https://en.wikipedia.org/wiki/Yuan_dynasty">Yuan Mongols</a>
[the imperial dynasty of China founded by Kublai Khan, grandson of
Genghis Khan] acquired the technique of distilling <em>arak</em> from the
Persians during their invasions of the Levant, Anatolia, and Persia,
and in turn introduced it to the Korean Peninsula during the Mongol
invasions of Korea (1231–1259). Distilleries were set up around the
city of Gaegyeong, the then-capital (current Kaesong). In the areas
surrounding Kaesong, soju is still called <em>arak-ju</em> (아락주). <em>Andong</em>
soju, the direct root of modern South Korean soju varieties, started
as the home-brewed liquor developed in the city of Andong, where the
Yuan Mongols&rsquo; logistics base was located during this era.</p>
<h3 id="senpai">Senpai</h3>
<p>[source: <a href="https://en.wiktionary.org/wiki/%E5%85%88%E8%BC%A9#Japanese">https://en.wiktionary.org/wiki/%E5%85%88%E8%BC%A9#Japanese</a>]</p>
<p>Obviously named for Japanese 先輩 <em>senpai</em> &ldquo;senior/superior in social
standing or education or skill; an elder&rdquo; (the homepage has a tagline
&ldquo;Your everyday IRC student&rdquo; and also &ldquo;Welcome home, desune~&rdquo; and also a smiling anime-ish cat-ish logo).</p>
<p>The Japanese word was borrowed from the Middle Chinese 先輩 (<em>sen
pwojH</em>). A doublet of ソンベ (<em>sonbe</em>) [With Japanese <em>sonbe</em> being itself
borrowed from Korean 선배(先輩) (<em>seonbae</em>), which means &ldquo;(chiefly in
Korean media) <em>sunbae</em> (upperclassman or senior, in the context of
Korea)&quot;].</p>
<p>Both ultimately from Chinese 先輩 [ <em>xiānbèi</em> ] &ldquo;older generation;
senior; elder; ancestor; predecessor&rdquo;.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>See: <a href="https://weechat.org/about/interfaces/">https://weechat.org/about/interfaces/</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>See <a href="https://wiki.ircnow.org/index.php?n=FreeIRC.About">https://wiki.ircnow.org/index.php?n=FreeIRC.About</a>;
freeirc.org is part of the <a href="https://ircnow.org">IRCNow network</a>:— a group/community with a
lot of useful free and paid services of various sorts, including shell
accounts and VPSes and such, and a lot of freely accessible
Unix/OpenBSD <a href="https://wiki.ircnow.org/index.php?n=Adminforces.Training">server admin tutorials</a>.</p>
<p>I also thought about the <a href="http://sdf.org">SDF Public Access Unix System[sdf.org]​</a>, which
has shell accounts and an IRC bouncer, but didn&rsquo;t end up trying their
bouncer. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Alongside of <a href="https://en.wikipedia.org/wiki/ERC_(software)">ERC</a> and <a href="https://github.com/emacs-circe/circe">Circe</a>, another popular Emacs
IRC client is <a href="https://en.wikipedia.org/wiki/Rcirc">rcirc</a>. But the Emacswiki <a href="https://www.emacswiki.org/emacs?action=browse;oldid=IRC;id=InternetRelayChat">lists at least nine different
Emacs IRC clients</a>. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>Goguma exists in an official version for Android
on F-Droid [on the <a href="https://f-droid.org/packages/fr.emersion.goguma/">official F-Droid respository</a>; and a &lsquo;nightly&rsquo;
version on <a href="https://fdroid.emersion.fr/#goguma-nightly">Goguma&rsquo;s own F-Droid repository</a>]; with unofficial/community
versions on <a href="https://play.google.com/store/apps/details?id=fr.emersion.goguma.play">Google Play for Android</a> and on the <a href="https://apps.apple.com/us/app/goguma-irc/id6470394620">App Store for iOS</a>. At
the moment I&rsquo;d recommend the official F-Droid version; I had some
issues with missing messages on the &lsquo;nightly&rsquo; version. <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>I&rsquo;m not entirely sure why Goguma is named after the
Korean word for &ldquo;sweet potato&rdquo;. Though <a href="https://en.wiktionary.org/wiki/%EA%B3%A0%EA%B5%AC%EB%A7%88">Wiktionary does report</a> there&rsquo;s
also apparently a recent &ldquo;Internet slang&rdquo; sense of the word: &ldquo;a plot
development which frustrates the reader (e.g. the protagonist fails to
achieve their goal)&rdquo; [from c. 2012]; that may be a red herring.</p>
<p>It&rsquo;s obviously connected to one of Ser&rsquo;s other IRC applications,
gamja, and maybe it&rsquo;s just wanting to name things after Korean words
for root vegetables for some reason. I would have said it was a play
on Go, but Goguma isn&rsquo;t written in Go.</p>
<p>Though soju (which is written in Go), while also a Korean word,
doesn&rsquo;t fit quite the root vegetable pattern. (Nor does <em>senpai</em>, but
that&rsquo;s by different authors [though Korean 先輩 (in hangeul 선배)
<em>seonbae</em> &ldquo;upperclassman, senior&rdquo; exists too, and is cognate with
<em>senpai</em>, both ultimately borrowed from Middle Chinese 先輩(<em>sen pwojH</em>)
&ldquo;elder, senior, ancestor&rdquo;]. Well, see more of this <a href="https://babbagefiles.xyz/no-one-can-be-told-what-irc-is-you-have-to-logon-for-yourself/#appendix-etymologies">in the appendix</a>, if
it&rsquo;s your sort of thing. <a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p>I think this is what is referred to in the <a href="https://sourcehut.org/blog/2024-06-04-status-and-plans/">June 2024
SourceHut blog post</a>, &ldquo;&hellip;You may have heard that we also had to part
ways with one of our staff members recently. This reduces our
headcount to two. For the time being we will not be hiring a
replacement, but our near-future plans are achievable with our current
headcount. Though we usually aim for transparency to the maximum
extent possible, we will not be sharing further details about this
departure, as a matter of reasonable privacy.&rdquo; <a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7" role="doc-endnote">
<p>see <a href="https://sourcehut.org/blog/2024-06-04-status-and-plans">2024-06-04 blog report &ldquo;The state of SourceHut and our
plans for the future&rdquo;</a>, where DeVault says &ldquo;Also, as a happy
side-effect of our surprise move to Amsterdam, SourceHut’s datacenter
installation is now entirely powered by renewable energy sources. We
have also finally rolled out IPv6 support for most SourceHut services
as part of our migration!&rdquo; <a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8" role="doc-endnote">
<p>In the &ldquo;Who is behind IRC Today&rdquo; <a href="https://web.archive.org/web/20250114235137/https://irctoday.com/faq">FAQ</a>, they say &ldquo;We have
co-developed the open source piece of software used in our service,
soju.&rdquo; I suspect this &ldquo;we&rdquo; includes at least, <a href="https://github.com/delthas">delthas</a> (the current
developer of senpai mentioned above, who also has a number of commits
to the soju repo, and who is in France), and Thomas Flament.</p>
<p>(For whatever reason, there seem to be at least two open source
developers who are both (a) French, and with interests in (b) IRCv3
and (c) Golang and (d) Korean or Japanese foodstuffs/culture.) <a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9" role="doc-endnote">
<p>If you&rsquo;re on IRC at all, you probably want to be on
Libera (which is where the main body of <a href="https://en.wikipedia.org/wiki/Freenode">Freenode</a> went after Freenode
was bought by the founder of <a href="https://en.wikipedia.org/wiki/Private_Internet_Access">Private Internet Access VPN</a>, <a href="https://en.wikipedia.org/wiki/Andrew_Lee_(entrepreneur)">Andrew Lee</a>,
<a href="https://en.wikipedia.org/wiki/Pretender">pretender</a> to the defunct throne of <a href="https://en.wikipedia.org/wiki/Joseon">Joseon</a> and the <a href="https://en.wikipedia.org/wiki/Korean_Empire">Korean Empire</a>; Korea
seems tied up in strange ways to IRC), and lots of free software
projects which aren&rsquo;t on Libera are on OFTC [=Open and Free Technology
Community] (e.g., <a href="https://irclogs.alpinelinux.org/">Alpine Linux</a>). <a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10" role="doc-endnote">
<p>With Elpaca I use the following:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">use-package</span> <span class="nv">imgbb</span>
  <span class="nb">:ensure</span> <span class="p">(</span><span class="nb">:host</span> <span class="nv">github</span> <span class="nb">:repo</span> <span class="s">&#34;ecraven/imgbb.el&#34;</span><span class="p">))</span>
</code></pre></div><p>With Emacs 30+'s vc-package you should be able to do something like:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">use-package</span> <span class="nv">imgbb</span>
  <span class="nb">:vc</span> <span class="p">(</span><span class="nv">imgbb</span> <span class="nb">:url</span> <span class="s">&#34;https://github.com/ecraven/imgbb.el&#34;</span>
                    <span class="nb">:branch</span> <span class="s">&#34;master&#34;</span><span class="p">)</span>
  <span class="nb">:ensure</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div><p>We might try to make something a bit better/more integrated, but it
works well enough for now. <a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/irc" term="irc" label="irc" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[C-c-c-conjecturing, and dealing with recursion in Emacs (more excursus)]]></title>
            <link href="https://babbagefiles.xyz/c-conjecturing-and-practical-considerations-of-recursion-in-emacs/"/>
            <id>https://babbagefiles.xyz/c-conjecturing-and-practical-considerations-of-recursion-in-emacs/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2025-02-24T04:10:00-06:00</published>
            <updated>2025-02-24T12:13:13-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>I&rsquo;m not putting this in the <a href="https://babbagefiles.xyz/categories/lambdacalculus/">lambda-calculus</a> series, though it touches
on issues from the <a href="https://babbagefiles.xyz/lambda-calculus-and-lisp-02-recursion/">last post</a> in the series, but specifically issues of
recursion. I was curious to go back and recall how <em><a href="https://web.archive.org/web/20150426092105/http://www.ccs.neu.edu/home/matthias/BTLS/">The Little Schemer</a></em>
dealt with problems of recursion (and the Y Combinator (which we still
haven&rsquo;t got properly to yet, but we will, I promise)).</p>
<p>In Chapter 9 of <em>The Little Schemer</em> (&ldquo;and Again, and Again, and
Again,&hellip;&quot;), it starts off by querying the reader if they want caviar
and how to find it in a list, and then essentially gets (from caviar
and grits) into issues around the halting problem.</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/bibby-again-again-again-little-schemer-ch9.png" alt="Figure 1: Duane Bibby illustration heading Chapter 9 of &ldquo;The Little Schemer&rdquo;"/> <figcaption>
                
                <p>
                    <span class="figure-number">Figure 1: </span><a href="https://wiki.c2.com/?DuaneBibby">Duane Bibby</a> illustration heading Chapter 9 of &ldquo;The Little Schemer&rdquo;
                    
                        
                        </p>
                
            </figcaption></figure>

<p>But a number of pages into this chapter the book queries of a
particular function: &ldquo;Is this function total?&rdquo;  and presents the
following:</p>
<div class="highlight"><pre class="chroma"><code class="language-scheme" data-lang="scheme"><span class="p">(</span><span class="k">define </span><span class="nv">C</span>
  <span class="p">(</span><span class="k">lambda </span><span class="p">(</span><span class="nf">n</span><span class="p">)</span>
    <span class="p">(</span><span class="nf">cond</span>
     <span class="p">((</span><span class="nf">one?</span> <span class="nv">n</span><span class="p">)</span> <span class="mi">1</span><span class="p">)</span>
     <span class="p">(</span><span class="nf">else</span>
      <span class="p">(</span><span class="nf">cond</span>
       <span class="p">((</span><span class="nb">even? </span><span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nf">C</span> <span class="p">(</span><span class="err">÷</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span>
       <span class="p">(</span><span class="k">else </span><span class="p">(</span><span class="nf">C</span> <span class="p">(</span><span class="nf">add1</span> <span class="p">(</span><span class="err">×</span> <span class="mi">3</span> <span class="nv">n</span><span class="p">)))))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 1:</span>
  a function puzzle from "The Little Schemer" (p.155)
</div>
<p>I didn&rsquo;t know what <em>C</em> was (a number of readers probably have recognised
it already, but please don&rsquo;t spoil it for the others just yet).</p>
<p>But whether we get the point of the function or not,we can implement
it in Emacs Lisp, translating from the Scheme into Emacs Lisp (defining an
equivalent of <code>even?</code> as an Emacs Lisp <code>evenp</code> first) as:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">evenp</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Returns </span><span class="ss">`t&#39;</span><span class="s"> if </span><span class="ss">`n&#39;</span><span class="s"> is even.&#34;</span>
  <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nf">=</span> <span class="p">(</span><span class="nf">%</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)</span> <span class="mi">0</span><span class="p">)</span>
      <span class="no">t</span>
    <span class="no">nil</span><span class="p">))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">C</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Function C from Little Schemer p.155.&#34;</span>
  <span class="p">(</span><span class="nb">cond</span>
   <span class="p">((</span><span class="nf">=</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span>
    <span class="mi">1</span><span class="p">)</span>
   <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">C</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span>
   <span class="p">(</span><span class="no">t</span>
    <span class="p">(</span><span class="nv">C</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">3</span> <span class="nv">n</span><span class="p">))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 2:</span>
  translation of the The Little Schemer 'C' function into Elisp
</div>
<p>And we can try running this on various numbers:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">C</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">; 1</span>
<span class="p">(</span><span class="nv">C</span> <span class="mi">9</span><span class="p">)</span> <span class="c1">; 1</span>
<span class="p">(</span><span class="nv">C</span> <span class="mi">108</span><span class="p">)</span> <span class="c1">; 1</span>
<span class="p">(</span><span class="nv">C</span> <span class="mi">837799</span><span class="p">)</span> <span class="c1">; 1</span>
<span class="p">(</span><span class="nv">C</span> <span class="mi">8400511</span><span class="p">)</span> <span class="c1">; 1</span>
<span class="p">(</span><span class="nv">C</span> <span class="mi">63728126</span><span class="p">)</span> <span class="c1">; 1</span>
</code></pre></div><p>That is, for all of these numbers it just outputs <code>1</code>. Well, we can try
a couple of other values:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">C</span> <span class="mi">0</span><span class="p">)</span> <span class="c1">; cl-prin1: (error &#34;Lisp nesting exceeds ‘max-lisp-eval-depth’&#34;)</span>
<span class="p">(</span><span class="nv">C</span> <span class="mi">63728127</span><span class="p">)</span> <span class="c1">; cl-prin1: (error &#34;Lisp nesting exceeds ‘max-lisp-eval-depth’&#34;)</span>
</code></pre></div><p>So <code>C</code> fails for <code>0</code> and numbers above <code>63728126</code>, with a &ldquo;excessive lisp
nesting&rdquo;-type issue.</p>
<p>But since the only non-error result we&rsquo;re getting is <code>1</code>, we could write
a much simpler function.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">ersatz-C</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Returns 1 if </span><span class="ss">`n&#39;</span><span class="s"> is 1;
</span><span class="s">enter an infinite nesting loop
</span><span class="s">if </span><span class="ss">`n&#39;</span><span class="s"> is 0 or above 63728126;
</span><span class="s">for any other integer value,
</span><span class="s">pause briefly for sake of plausibly,
</span><span class="s">and then return 1.&#34;</span>
  <span class="p">(</span><span class="nb">cond</span>
   <span class="p">((</span><span class="nf">=</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span>
    <span class="mi">1</span><span class="p">)</span>
   <span class="p">((</span><span class="nf">=</span> <span class="nv">n</span> <span class="mi">0</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">ersatz-C</span> <span class="nv">n</span><span class="p">))</span>
   <span class="p">((</span><span class="nf">&gt;</span> <span class="nv">n</span> <span class="mi">63728126</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">ersatz-C</span> <span class="nv">n</span><span class="p">))</span>
   <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nb">progn</span>
        <span class="p">(</span><span class="nf">sleep-for</span> <span class="mf">0.005</span><span class="p">)</span>
        <span class="mi">1</span><span class="p">))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 3:</span>
  A simpler function to return "1"
</div>
<p>But obviously function <code>C</code> is doing something more interesting. So, let&rsquo;s make it
actually show us what it&rsquo;s doing:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">C-verbose</span> <span class="p">(</span><span class="nv">n</span> <span class="kp">&amp;optional</span> <span class="nv">count</span><span class="p">)</span>
  <span class="s">&#34;For a number </span><span class="ss">`n&#39;</span><span class="s">, return </span><span class="ss">`1&#39;</span><span class="s"> if it&#39;s </span><span class="ss">`1&#39;</span><span class="s">; otherwise,
</span><span class="s">if it&#39;s even, run the same function on half of </span><span class="ss">`n&#39;</span><span class="s">;
</span><span class="s">else, run the same function on one more than three times
</span><span class="s"></span><span class="ss">`n&#39;</span><span class="s">.&#34;</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">count</span> <span class="p">(</span><span class="nb">or</span> <span class="nv">count</span> <span class="mi">0</span><span class="p">)))</span>
    <span class="p">(</span><span class="nf">print</span> <span class="p">(</span><span class="nf">format</span> <span class="s">&#34;Step %s: %s&#34;</span> <span class="nv">count</span> <span class="nv">n</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">setq</span> <span class="nv">count</span> <span class="p">(</span><span class="nf">1+</span> <span class="nv">count</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">cond</span>
     <span class="p">((</span><span class="nf">=</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span>
      <span class="mi">1</span><span class="p">)</span>
     <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">C-verbose</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)</span> <span class="nv">count</span><span class="p">))</span>
     <span class="p">(</span><span class="no">t</span>
      <span class="p">(</span><span class="nv">C-verbose</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">3</span> <span class="nv">n</span><span class="p">))</span> <span class="nv">count</span><span class="p">)))))</span>

<span class="p">(</span><span class="nv">C-verbose</span> <span class="mi">7</span><span class="p">)</span> <span class="c1">; =</span>
<span class="c1">;; &#34;Step 0: 7&#34;</span>
<span class="c1">;; &#34;Step 1: 22&#34;</span>
<span class="c1">;; &#34;Step 2: 11&#34;</span>
<span class="c1">;; &#34;Step 3: 34&#34;</span>
<span class="c1">;; &#34;Step 4: 17&#34;</span>
<span class="c1">;; &#34;Step 5: 52&#34;</span>
<span class="c1">;; &#34;Step 6: 26&#34;</span>
<span class="c1">;; &#34;Step 7: 13&#34;</span>
<span class="c1">;; &#34;Step 8: 40&#34;</span>
<span class="c1">;; &#34;Step 9: 20&#34;</span>
<span class="c1">;; &#34;Step 10: 10&#34;</span>
<span class="c1">;; &#34;Step 11: 5&#34;</span>
<span class="c1">;; &#34;Step 12: 16&#34;</span>
<span class="c1">;; &#34;Step 13: 8&#34;</span>
<span class="c1">;; &#34;Step 14: 4&#34;</span>
<span class="c1">;; &#34;Step 15: 2&#34;</span>
<span class="c1">;; &#34;Step 16: 1&#34;</span>

<span class="p">(</span><span class="nv">C-verbose</span> <span class="mi">9</span><span class="p">)</span> <span class="c1">; =</span>
<span class="c1">;; &#34;Step 0: 9&#34;</span>
<span class="c1">;; &#34;Step 1: 28&#34;</span>
<span class="c1">;; &#34;Step 2: 14&#34;</span>
<span class="c1">;; &#34;Step 3: 7&#34;</span>
<span class="c1">;; &#34;Step 4: 22&#34;</span>
<span class="c1">;; &#34;Step 5: 11&#34;</span>
<span class="c1">;; &#34;Step 6: 34&#34;</span>
<span class="c1">;; &#34;Step 7: 17&#34;</span>
<span class="c1">;; &#34;Step 8: 52&#34;</span>
<span class="c1">;; &#34;Step 9: 26&#34;</span>
<span class="c1">;; &#34;Step 10: 13&#34;</span>
<span class="c1">;; &#34;Step 11: 40&#34;</span>
<span class="c1">;; &#34;Step 12: 20&#34;</span>
<span class="c1">;; &#34;Step 13: 10&#34;</span>
<span class="c1">;; &#34;Step 14: 5&#34;</span>
<span class="c1">;; &#34;Step 15: 16&#34;</span>
<span class="c1">;; &#34;Step 16: 8&#34;</span>
<span class="c1">;; &#34;Step 17: 4&#34;</span>
<span class="c1">;; &#34;Step 18: 2&#34;</span>
<span class="c1">;; &#34;Step 19: 1&#34;</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 4:</span>
  Write `C' to make it show us what it's doing; with some examples of use
</div>
<p>Perhaps not surprisingly, we see that setting <code>n=9</code> makes the function
take more steps to reach 1 than setting <code>n=7</code>. But it&rsquo;s not actually a
direct linear relation, because if we set <code>n=10</code>, it&rsquo;s a much shorter
path:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">C-verbose</span> <span class="mi">10</span><span class="p">)</span> <span class="c1">; =</span>
<span class="c1">;; &#34;Step 0: 10&#34;</span>
<span class="c1">;; &#34;Step 1: 5&#34;</span>
<span class="c1">;; &#34;Step 2: 16&#34;</span>
<span class="c1">;; &#34;Step 3: 8&#34;</span>
<span class="c1">;; &#34;Step 4: 4&#34;</span>
<span class="c1">;; &#34;Step 5: 2&#34;</span>
<span class="c1">;; &#34;Step 6: 1&#34;</span>
</code></pre></div><p>So we might still wonder about exactly what&rsquo;s going on with function
<code>C</code>, how it works, and what&rsquo;s the point of it anyway.</p>
<h2 id="conjectures-about-simple-arithmetic-operations-and-integer-pathways">Conjectures about simple arithmetic operations and integer pathways</h2>
<p><em>The Little Schemer</em> does give a clue about what <code>C</code> is, referring to the
question of whether it&rsquo;s a total function or not:</p>
<blockquote>
<p>It doesn&rsquo;t yield a value for 0, but otherwise nobody knows. Thank you,
Lothar Collatz (1910–1990).</p>
</blockquote>
<p>A bit of research and we can find quite a bit about <code>C</code>: it turns out to
be the <strong>Collatz conjecture</strong>: one of the most famous unsolved problems in
mathematics.</p>
<p>The <a href="https://en.wikipedia.org/wiki/Collatz_conjecture">Wikipedia page on the Collatz conjecture</a> is useful here in laying
out basics. As well, <a href="http://www.ericr.nl">Eric Roosendaal</a>&lsquo;s <a href="http://www.ericr.nl/wondrous/index.html">&ldquo;On the 3x + 1 problem&rdquo;</a>. And
Quantamagazine&rsquo;s <a href="https://www.quantamagazine.org/why-mathematicians-still-cant-solve-the-collatz-conjecture-20200922/">&ldquo;The Simple Math Problem We Still Can’t Solve&rdquo;</a>
presents a fairly accessible overview, including recent developments.</p>
<p>There&rsquo;s some very interesting visualisations of the Collatz Functions
paths as a Collatz tree, which is a rather aesthetically-appealling
visual object. Here&rsquo;s Algoritmarte&rsquo;s 3D live render, based on a
discussion from the <a href="https://www.youtube.com/@numberphile">Numberphile channel</a> (for Numberphile&rsquo;s original video, done by hand, see the fn):<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/4Z1MqCvMskI?si=tWIPRsVnYOwt2AuB" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<center/><a href="https://www.algoritmarte.com/the-collatz-tree/"</>Algoritmarte's Collatz Tree visualisation</a></center><br/>
<p>But Collatz conjecture is an unsolved problem that looks like it
should certainly be solvable. It seems quite trivial in some sense:
inductive logic would seem to suggest that the same basic patterns
would repeat, even if there are more of them, or they are longer,
given that every number that&rsquo;s been tested has converged to 1 (or,
more precisely, fallen into the &ldquo;4-2-1&rdquo; loop) and so this tempts
people in trying to solve it. But Paul Erdős said of it &ldquo;Mathematics
may not be ready for such problems&rdquo;.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>



<figure>
    
        
            <img src="https://imgs.xkcd.com/comics/collatz_conjecture.png" alt="Figure 2: https://xkcd.com/710/ [see: https://www.explainxkcd.com/wiki/index.php/710:_Collatz_Conjecture ]"/> <figcaption>
                
                <p>
                    <span class="figure-number">Figure 2: </span><a href="https://xkcd.com/710/">https://xkcd.com/710/</a> [see: <a href="https://www.explainxkcd.com/wiki/index.php/710:_Collatz_Conjecture">https://www.explainxkcd.com/wiki/index.php/710:_Collatz_Conjecture</a> ]
                    
                        
                        </p>
                
            </figcaption></figure>

<p>Well, we&rsquo;ll not try to solve it, but rather use it for thinking about
issues of recursion and long calculations in Emacs Lisp, and doing
some practical engineering-type testing of different methods of
dealing with such functions.</p>
<p>One thing we might note, as a sort of aside, is that <em>The Little
Schemer</em>&lsquo;s <code>C</code> isn&rsquo;t properly the Collatz function. The conjecture is
really about whether the process will ever reach the repeating &ldquo;4-2-1&rdquo;
loop, because the process of dividing even numbers by 2 and multiple
odd numbers and adding 1 to them would actually mean that once we&rsquo;ve
reached 1, we need to multiple it by 3 and add 1, which means we&rsquo;re
back at 4, which is even, so we&rsquo;ll divide by 2 and get 2, which is
even, so we divide by 2 and get 1, which is odd, so we&hellip;. And so on.</p>
<p>We can write this &ldquo;real Collatz&rdquo; function:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">real-collatz</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;For an integer </span><span class="ss">`n&#39;</span><span class="s">, if it&#39;s evan, divide it by 2;
</span><span class="s">if it&#39;s odd, multiple it by 3 and add 1.&#34;</span>
  <span class="p">(</span><span class="nb">cond</span>
   <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span>
   <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 5:</span>
  the real basic Collatz operations
</div>
<p>If we now try to feed it 9, we&rsquo;ll hit the lisp-nesting error, but if we turn on
<code>toggle-debug-on-error</code>, we can inspect what&rsquo;s going on, and it&rsquo;s
exactly the &ldquo;4-2-1&rdquo; loop we predicted:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">real-collatz</span> <span class="mi">9</span><span class="p">)</span> <span class="c1">; cl-prin1: (error &#34;Lisp nesting exceeds ‘max-lisp-eval-depth’&#34;)</span>

<span class="c1">;; DEBUG:</span>
<span class="o">....</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">40</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">26</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">52</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">17</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">34</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">11</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">22</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">28</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nv">evenp</span> <span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span> <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">real-collatz</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">n</span> <span class="mi">3</span><span class="p">)))))</span>
  <span class="nv">real-collatz</span><span class="p">(</span><span class="mi">9</span><span class="p">)</span>
  <span class="nf">eval</span><span class="p">((</span><span class="nv">real-collatz</span> <span class="mi">9</span><span class="p">)</span> <span class="no">nil</span><span class="p">)</span>
  <span class="nv">elisp--eval-last-sexp</span><span class="p">(</span><span class="no">nil</span><span class="p">)</span>
<span class="o">.....</span>
</code></pre></div><p>We&rsquo;ll make use of it later on though, somewhat (essentially with the
<code>n=1</code> condition displaced.)</p>
<h2 id="dealing-with-long-winding-paths-through-hailstorms">Dealing with long winding paths, through hailstorms</h2>
<p>Ok, so two things. One, let&rsquo;s not just print things (longer paths are
going to make his quite messy), but return a list of the steps our
function goes through and a count of them. We do still want to be able
to inspect the path patterns. (Wikipedia notes on this &ldquo;[t]he sequence
of numbers involved [in the paths] is sometimes referred to as the
hailstone sequence, hailstone numbers or hailstone numerals (because
the values are usually subject to multiple descents and ascents like
hailstones in a cloud)&quot;.)</p>
<p>And, two, let&rsquo;s use the <code>cl-labels</code> trick from last time to get
tail-call optimisation in Emacs.</p>
<p>(And a minor thing: not bother with a separate <code>evenp</code> function; just
use the calculation directly.)</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">collatz-cllabels-tco</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Calculates the &#39;Collatz path&#39; for </span><span class="ss">`n&#39;</span><span class="s">,
</span><span class="s">return a list of all of the steps cons&#39;ed
</span><span class="s">with count of the steps in the path.
</span><span class="s">
</span><span class="s">[Uses </span><span class="ss">`cl-labels&#39;</span><span class="s"> to get tail-call optimisation.]&#34;</span>
  <span class="p">(</span><span class="nb">cl-labels</span> <span class="p">((</span><span class="nv">collatz*</span> <span class="p">(</span><span class="nv">n</span> <span class="nv">r</span><span class="p">)</span>
                  <span class="p">(</span><span class="nb">setq</span> <span class="nv">r</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">n</span> <span class="nv">r</span><span class="p">))</span>
                  <span class="p">(</span><span class="nb">cond</span>
                     <span class="p">((</span><span class="nf">=</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span>
                      <span class="nv">r</span><span class="p">)</span>
                     <span class="p">((</span><span class="nf">=</span> <span class="p">(</span><span class="nf">%</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)</span> <span class="mi">0</span><span class="p">)</span>
                        <span class="p">(</span><span class="nv">collatz*</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)</span> <span class="nv">r</span><span class="p">))</span>
                     <span class="p">(</span><span class="no">t</span>
                        <span class="p">(</span><span class="nv">collatz*</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">3</span> <span class="nv">n</span><span class="p">))</span> <span class="nv">r</span><span class="p">)))))</span>
    <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">clst</span> <span class="p">(</span><span class="nv">collatz*</span> <span class="nv">n</span> <span class="no">nil</span><span class="p">))</span>
          <span class="p">(</span><span class="nv">clngth</span> <span class="p">(</span><span class="nf">1-</span> <span class="p">(</span><span class="nf">length</span> <span class="nv">clst</span><span class="p">))))</span>
      <span class="p">(</span><span class="nf">cons</span> <span class="p">(</span><span class="nf">nreverse</span> <span class="nv">clst</span><span class="p">)</span> <span class="nv">clngth</span><span class="p">))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 6:</span>
  A tail-call optimising implementation of a recursive Collatz function
</div>
<p>Here you will note that we&rsquo;ve got our <code>collatz*</code> function (defined in
<code>cl-labels</code>) being self-recursive only properly in tail positions, so it
can be tail-call optimised. We start by calling <code>collatz*</code> with
arguments <code>n</code> (whatever value we passed for <code>n</code>) and <code>nil</code>, with <code>collatz*</code>
taking two arguments, <code>n</code> and <code>r</code> and setting <code>r</code> to the <code>cons</code> of <code>n</code> with <code>r</code>
(we&rsquo;ll pass this value on, collecting the steps), and then
implementing the rest of the Collatz function in the same way as
previously, except passing along the <code>r</code> value as our collector. At the
end, we&rsquo;ll count the steps and <code>cons</code> the list of the steps themselves,
using <code>nreverse</code> to flip the list over (because it&rsquo;ll have the last
results on the &ldquo;outer&rdquo; left and the first results on the &ldquo;inner&rdquo;
right), with the step count.</p>
<p>Some examples:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">collatz-cllabels-tco</span> <span class="mi">9</span><span class="p">)</span> <span class="c1">; ((9 28 14 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1) . 20)</span>
                <span class="c1">; path from 9 to 1; 20 steps</span>

<span class="p">(</span><span class="nv">collatz-cllabels-tco</span> <span class="mi">21</span><span class="p">)</span> <span class="c1">; ((21 64 32 16 8 4 2 1) . 8)</span>
                 <span class="c1">; path from 21 to 1; 8 steps</span>
</code></pre></div><p>Excellent, but what about &ldquo;63728127&rdquo;?:— the number that tripped us up
before:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>

<span class="p">(</span><span class="nv">collatz-cllabels-tco</span> <span class="mi">63728127</span><span class="p">)</span> <span class="c1">; it works!</span>

<span class="c1">;; ...but the result is quite long, you can try it yourself;</span>
<span class="c1">;; use setq to be able to see all of it, e.g.,</span>
<span class="c1">;; (setq c-result (collatz-cllabels-tco 63728127)).</span>

<span class="c1">;; for, let&#39;s just see how many steps it is:</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">collatz-return-steps</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Output just the number of steps that
</span><span class="s"></span><span class="ss">`n&#39;</span><span class="s"> takes to reach 1.&#34;</span>
  <span class="p">(</span><span class="nf">format</span> <span class="s">&#34;%s takes %s steps to reach 1&#34;</span>
          <span class="nv">n</span>
          <span class="p">(</span><span class="nf">cdr</span>
           <span class="p">(</span><span class="nv">collatz-cllabels-tco</span> <span class="nv">n</span><span class="p">))))</span>


<span class="p">(</span><span class="nv">collatz-return-steps</span> <span class="mi">63728127</span><span class="p">)</span> <span class="c1">; =</span>
<span class="s">&#34;63728127 takes 949 steps to reach 1&#34;</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 7:</span>
  simple counting of Collatz steps on the path
</div>
<p>&ldquo;Let&rsquo;s try it with a bigger number!&quot;, you say. Well, we can try it on
<code>most-positive-fixnum</code> (which is I think probably set to
2305843009213693951 by default) — but remember the size of the number
and the number of steps is not a direct linear relation, so you might
be disappointed:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">collatz-return-steps</span> <span class="nv">most-positive-fixnum</span><span class="p">)</span> <span class="c1">; =</span>
<span class="s">&#34;2305843009213693951 takes 860 steps to reach 1&#34;</span>
</code></pre></div><p>This is less than for 63728127.</p>
<p>Ok, so let&rsquo;s make something really big then. <code>2^1000 + 1</code>.</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">collatz-return-steps</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">1000</span><span class="p">)))</span> <span class="c1">; =</span>
<span class="s">&#34;10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069377 takes 7248 steps to reach 1&#34;</span>
</code></pre></div><p>It takes 7,248 steps. What about <code>2^10000 + 1</code>?</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">collatz-return-steps</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))</span> <span class="c1">; =</span>
<span class="s">&#34;19950631168807583848837421626835850838234968318861924548520089498529438830221946631919961684036194597899331129423209124271556491349413781117593785932096323957855730046793794526765246551266059895520550086918193311542508608460618104685509074866089624888090489894838009253941633257850621568309473902556912388065225096643874441046759871626985453222868538161694315775629640762836880760732228535091641476183956381458969463899410840960536267821064621427333394036525565649530603142680234969400335934316651459297773279665775606172582031407994198179607378245683762280037302885487251900834464581454650557929601414833921615734588139257095379769119277800826957735674444123062018757836325502728323789270710373802866393031428133241401624195671690574061419654342324638801248856147305207431992259611796250130992860241708340807605932320161268492288496255841312844061536738951487114256315111089745514203313820202931640957596464756010405845841566072044962867016515061920631004186422275908670900574606417856951911456055068251250406007519842261898059237118054444788072906395242548339221982707404473162376760846613033778706039803413197133493654622700563169937455508241780972810983291314403571877524768509857276937926433221599399876886660808368837838027643282775172273657572744784112294389733810861607423253291974813120197604178281965697475898164531258434135959862784130128185406283476649088690521047580882615823961985770122407044330583075869039319604603404973156583208672105913300903752823415539745394397715257455290510212310947321610753474825740775273986348298498340756937955646638621874569499279016572103701364433135817214311791398222983845847334440270964182851005072927748364550578634501100852987812389473928699540834346158807043959118985815145779177143619698728131459483783202081474982171858011389071228250905826817436220577475921417653715687725614904582904992461028630081535583308130101987675856234343538955409175623400844887526162643568648833519463720377293240094456246923254350400678027273837755376406726898636241037491410966718557050759098100246789880178271925953381282421954028302759408448955014676668389697996886241636313376393903373455801407636741877711055384225739499110186468219696581651485130494222369947714763069155468217682876200362777257723781365331611196811280792669481887201298643660768551639860534602297871557517947385246369446923087894265948217008051120322365496288169035739121368338393591756418733850510970271613915439590991598154654417336311656936031122249937969999226781732358023111862644575299135758175008199839236284615249881088960232244362173771618086357015468484058622329792853875623486556440536962622018963571028812361567512543338303270029097668650568557157505516727518899194129711337690149916181315171544007728650573189557450920330185304847113818315407324053319038462084036421763703911550639789000742853672196280903477974533320468368795868580237952218629120080742819551317948157624448298518461509704888027274721574688131594750409732115080498190455803416826949787141316063210686391511681774304792596709377 takes 72378 steps to reach 1&#34;</span>
</code></pre></div><p>72,378 steps.</p>
<p>Can we go one bigger?</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">collatz-return-steps</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">100000</span><span class="p">)))</span>
<span class="c1">;; Debugger entered--Lisp error: (overflow-error)</span>
</code></pre></div><p>No. But it&rsquo;s because Emacs has <code>maximum-integer-width</code> set to 65536 (so
a number can only be 65,536 digits long). We can set it to something
bigger, and it&rsquo;ll work.</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">collatz-return-steps</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">100000</span><span class="p">))))</span> <span class="c1">; =</span>

<span class="c1">;; I&#39;ll spare you the number `n&#39; itself as it&#39;s realy quite long,</span>
<span class="c1">;; but that number takes 717,858 steps to reach 1.</span>
</code></pre></div><p>You&rsquo;ll note, if you&rsquo;ve been evaluating along in your own Emacs that
these results have been nearly instantaneous so far. This one takes a
bit longer.</p>
<p>(I subsequently tried doing <code>collatz-cllabels-tco</code> on <code>2^1000000 + 1</code>, but with only
16Gb in my machine, Linux&rsquo;s <a href="https://docs.kernel.org/admin-guide/mm/concepts.html#id9">OOM-killer</a> killed Emacs before it reached
the end. I should probably try setting up some sort of swap better.)</p>
<h2 id="streaming-though-the-hail">Streaming though the hail</h2>
<p>We can also try the <a href="https://elpa.gnu.org/packages/stream.html"><code>stream</code></a> package <a href="https://babbagefiles.xyz/lambda-calculus-and-lisp-02-recursion/#stream-of-conses-ness">we looked at before</a>, which
creates &ldquo;lazy&rdquo; conses, of potentially infinite sequences, which can be
evaluated one piece at a time, as needed.</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">collatz-stream</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;For a number </span><span class="ss">`n&#39;</span><span class="s">, return </span><span class="ss">`1&#39;</span><span class="s"> if it&#39;s </span><span class="ss">`1&#39;</span><span class="s">; otherwise,
</span><span class="s">if it&#39;s even, run the same function on half of </span><span class="ss">`n&#39;</span><span class="s">;
</span><span class="s">else, run the same function on one more than three times
</span><span class="s"></span><span class="ss">`n&#39;</span><span class="s">.
</span><span class="s">(Also, collect each step into a list and, when </span><span class="ss">`n&#39;</span><span class="s"> is </span><span class="ss">`1&#39;</span><span class="s">,
</span><span class="s">print the list.)
</span><span class="s">[Uses the </span><span class="ss">`stream&#39;</span><span class="s"> package for lazy-evaluated conses.]&#34;</span>
  <span class="p">(</span><span class="nb">cl-labels</span> <span class="p">((</span><span class="nv">collatz*</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
                <span class="p">(</span><span class="nv">stream-cons</span> <span class="nv">n</span>
                             <span class="c1">;; note that this is the `real&#39; collatz func</span>
                             <span class="c1">;; no checking for 1 here; we do it below</span>
                              <span class="p">(</span><span class="nb">cond</span>
                               <span class="p">((</span><span class="nf">=</span> <span class="p">(</span><span class="nf">%</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)</span> <span class="mi">0</span><span class="p">)</span>
                                <span class="p">(</span><span class="nv">collatz*</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span>
                               <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nv">collatz*</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">3</span> <span class="nv">n</span><span class="p">))))))))</span>
    <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">clst</span> <span class="p">(</span><span class="nv">collatz*</span> <span class="nv">n</span><span class="p">))</span>
          <span class="p">(</span><span class="nv">last</span> <span class="mi">0</span><span class="p">)</span>
          <span class="p">(</span><span class="nv">out</span> <span class="no">nil</span><span class="p">))</span>
      <span class="p">(</span><span class="nb">while</span> <span class="p">(</span><span class="nv">not</span> <span class="p">(</span><span class="nf">=</span> <span class="nv">last</span> <span class="mi">1</span><span class="p">))</span> <span class="c1">;; stop when we reach 1</span>
        <span class="p">(</span><span class="nb">setq</span> <span class="nv">last</span> <span class="p">(</span><span class="nv">stream-pop</span> <span class="nv">clst</span><span class="p">))</span>
        <span class="p">(</span><span class="nb">setq</span> <span class="nv">out</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">last</span> <span class="nv">out</span><span class="p">)))</span>
      <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">steps</span> <span class="p">(</span><span class="nf">1-</span> <span class="p">(</span><span class="nf">length</span> <span class="nv">out</span><span class="p">)))</span>
            <span class="p">(</span><span class="nv">rlist</span> <span class="p">(</span><span class="nf">nreverse</span> <span class="nv">out</span><span class="p">)))</span>
        <span class="p">(</span><span class="nf">cons</span> <span class="nv">rlist</span> <span class="nv">steps</span><span class="p">)))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 8:</span>
  Using lazy cons sequences in "stream" for Collatz calculations
</div>
<p>This, however, turns out to in fact be much slower than our <code>cl-labels</code>
tco recursion.</p>
<p>In fact, we can quantify this a bit further using <a href="https://github.com/alphapapa/">Adam Porter</a>&lsquo;s
<a href="https://github.com/alphapapa/emacs-package-dev-handbook?tab=readme-ov-file#benchmarking"><code>bench-multi</code> macro</a>:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">bench-multi</span>
  <span class="nb">:times</span> <span class="mi">5</span>
  <span class="nb">:forms</span> <span class="p">((</span><span class="s">&#34;collatz-stream&#34;</span> <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
            <span class="p">(</span><span class="nv">collatz-stream</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cllabels-tco&#34;</span> <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
            <span class="p">(</span><span class="nv">collatz-cllabels-tco</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 9:</span>
  bench-marking cl-labels vs stream collatz functions
</div>
<p>Here the forms are ranked in order by fastest first; the second column
says how much slower the form is compared against the fastest; then
there are total runtime (for all iterations; we did here 5 for each);
and total <a href="https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)">garbage collections</a> performed by Emacs during those tests;
and the total amount of time the garbage collection tasks themselves took.</p>
<table>
<thead>
<tr>
<th>Form</th>
<th>x fastest</th>
<th>Total runtime</th>
<th># of GCs</th>
<th>Total GC runtime</th>
</tr>
</thead>
<tbody>
<tr>
<td>collatz-cllabels-tco</td>
<td>fastest</td>
<td>1.310546</td>
<td>2</td>
<td>0.484381</td>
</tr>
<tr>
<td>collatz-stream</td>
<td>12.72</td>
<td>16.665472</td>
<td>45</td>
<td>11.808777</td>
</tr>
</tbody>
</table>
<p>I&rsquo;m not quite sure why <code>collatz-stream</code> is this much slower than
<code>collatz-cllabels-tco</code> is the case. My guess is that because of the delayed/lazy
evaluation, we aren&rsquo;t able to get real reduction of <a href="https://en.wikipedia.org/wiki/Call_stack#STACK-FRAME">stack frames</a>
because we&rsquo;re only evaluating one piece/instance of the Collatz
function on each <code>stream-pop</code>, and there&rsquo;s no advantage to using <code>cl-labels</code>
here really.</p>
<p>The <code>stream</code> method certainly triggers more garbage collection, which is
part of it, though I&rsquo;m not entirely sure why it should involve
triggering more garbage collection. (I suppose we could play with
garbage collection settings in emacs and see if we can deal garbage
collection and make the <code>stream</code> method better.)</p>
<h2 id="caught-in-loops-still">Caught in loops still?</h2>
<p>But what about a plain looping method for the Collatz function. We
generally saw with the Fibonacci series that <a href="/lambda-calculus-and-lisp-02-recursion/#back-to-loops">loops seemed more
efficient</a>.</p>
<p>We can try with both plain <code>while</code> loops and also <code>cl-loops</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">collatz-while-loop</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Collatz function using emacs </span><span class="ss">`while&#39;</span><span class="s"> loop.&#34;</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">m</span> <span class="nv">n</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">clst</span> <span class="p">(</span><span class="nf">list</span> <span class="nv">n</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">while</span> <span class="p">(</span><span class="nv">not</span> <span class="p">(</span><span class="nf">=</span> <span class="nv">m</span> <span class="mi">1</span><span class="p">))</span>
      <span class="p">(</span><span class="nb">setq</span> <span class="nv">m</span>
            <span class="p">(</span><span class="nb">cond</span>
             <span class="p">((</span><span class="nf">=</span> <span class="p">(</span><span class="nf">%</span> <span class="nv">m</span> <span class="mi">2</span><span class="p">)</span> <span class="mi">0</span><span class="p">)</span>
              <span class="p">(</span><span class="nf">/</span> <span class="nv">m</span> <span class="mi">2</span><span class="p">))</span>
             <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">3</span> <span class="nv">m</span><span class="p">)))))</span>
      <span class="p">(</span><span class="nb">setq</span> <span class="nv">clst</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">m</span> <span class="nv">clst</span><span class="p">)))</span>
          <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">steps</span> <span class="p">(</span><span class="nf">1-</span> <span class="p">(</span><span class="nf">length</span> <span class="nv">clst</span><span class="p">)))</span>
            <span class="p">(</span><span class="nv">rlist</span> <span class="p">(</span><span class="nf">nreverse</span> <span class="nv">clst</span><span class="p">)))</span>
            <span class="p">(</span><span class="nf">cons</span> <span class="nv">rlist</span> <span class="nv">steps</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">collatz-cl-loop</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Collatz function using </span><span class="ss">`cl-loop&#39;</span><span class="s"> with
</span><span class="s"></span><span class="ss">`while&#39;</span><span class="s"> clause condition.&#34;</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">m</span> <span class="nv">n</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">clst</span> <span class="p">(</span><span class="nf">list</span> <span class="nv">n</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">cl-loop</span> <span class="nb">while</span> <span class="p">(</span><span class="nv">not</span> <span class="p">(</span><span class="nf">=</span> <span class="nv">m</span> <span class="mi">1</span><span class="p">))</span>
             <span class="nb">do</span> <span class="p">(</span><span class="nb">progn</span>
                  <span class="p">(</span><span class="nb">setq</span> <span class="nv">m</span>
                        <span class="p">(</span><span class="nb">cond</span>
                         <span class="p">((</span><span class="nf">=</span> <span class="p">(</span><span class="nf">%</span> <span class="nv">m</span> <span class="mi">2</span><span class="p">)</span> <span class="mi">0</span><span class="p">)</span>
                          <span class="p">(</span><span class="nf">/</span> <span class="nv">m</span> <span class="mi">2</span><span class="p">))</span>
                         <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">3</span> <span class="nv">m</span><span class="p">)))))</span>
                  <span class="p">(</span><span class="nb">setq</span> <span class="nv">clst</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">m</span> <span class="nv">clst</span><span class="p">)))</span>
             <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">steps</span> <span class="p">(</span><span class="nf">1-</span> <span class="p">(</span><span class="nf">length</span> <span class="nv">clst</span><span class="p">)))</span>
                    <span class="p">(</span><span class="nv">rlist</span> <span class="p">(</span><span class="nf">nreverse</span> <span class="nv">clst</span><span class="p">)))</span>
               <span class="p">(</span><span class="nf">cons</span> <span class="nv">rlist</span> <span class="nv">steps</span><span class="p">))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 10:</span>
  looping methods for the Collatz function
</div>
<p>And now comparing all of the methods:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">bench-multi</span>
  <span class="nb">:times</span> <span class="mi">5</span>
  <span class="nb">:forms</span> <span class="p">((</span><span class="s">&#34;collatz-stream&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-stream</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cllabels-tco&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cllabels-tco</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-while-loop&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-while-loop</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cl-loop&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cl-loop</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 11:</span>
  bench-marking our Collatz function implementations
</div>
<p>We find the following results:</p>
<table>
<thead>
<tr>
<th>Form</th>
<th>x fastest</th>
<th>Total runtime</th>
<th># of GCs</th>
<th>Total GC runtime</th>
</tr>
</thead>
<tbody>
<tr>
<td>collatz-while-loop</td>
<td>fastest</td>
<td>0.876315</td>
<td>1</td>
<td>0.240661</td>
</tr>
<tr>
<td>collatz-cl-loop</td>
<td>1.02</td>
<td>0.896261</td>
<td>1</td>
<td>0.235273</td>
</tr>
<tr>
<td>collatz-cllabels-tco</td>
<td>1.43</td>
<td>1.255294</td>
<td>2</td>
<td>0.449736</td>
</tr>
<tr>
<td>collatz-stream</td>
<td>18.77</td>
<td>16.446478</td>
<td>48</td>
<td>11.677753</td>
</tr>
</tbody>
</table>
<p>Both types of loops turn out faster than even <code>collatz-cllabels-tco</code>, with the
plain emacs <code>while loop</code> method seeming slightly faster than the <code>cl-loop</code>
method, but probably within margin of error (or maybe just a bit of
extra machine in <code>cl-loop</code> which loses a small amount of efficiency.)</p>
<p>For our TCO tail-recurive <code>cll</code> maybe it&rsquo;s the additional garbage collection
that slows down <code>collatz-cllabels-tco</code>.</p>
<p>What if we just look at the two loop methods, and run more trials?</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">bench-multi</span>
  <span class="nb">:times</span> <span class="mi">100</span>
  <span class="nb">:forms</span> <span class="p">((</span><span class="s">&#34;collatz-while-loop&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-while-loop</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cl-loop&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cl-loop</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 12:</span>
  bench-marking loop methods of implementing the Collatz function
</div>
<p>Our results are:</p>
<table>
<thead>
<tr>
<th>Form</th>
<th>x fastest</th>
<th>Total runtime</th>
<th># of GCs</th>
<th>Total GC runtime</th>
</tr>
</thead>
<tbody>
<tr>
<td>collatz-while-loop</td>
<td>fastest</td>
<td>17.584334</td>
<td>20</td>
<td>4.851656</td>
</tr>
<tr>
<td>collatz-cl-loop</td>
<td>1.03</td>
<td>18.027418</td>
<td>20</td>
<td>5.070158</td>
</tr>
</tbody>
</table>
<p>Still the plain <code>while</code> is slightly faster than <code>cl-loop</code>.</p>
<h3 id="an-attempt-at-promoting-our-recursive-tco-function">An attempt at promoting our recursive tco function</h3>
<p>I&rsquo;d really like to somehow improve our recursive TCO collatz function.</p>
<p>What if we pull the collector <code>r</code> out in a higher-scoping <code>let</code> and so
don&rsquo;t have to pass it into <code>collatz*</code> each time; thus:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">collatz-cllabels-tco-improved</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Calculates the &#39;Collatz path&#39; for </span><span class="ss">`n&#39;</span><span class="s">,
</span><span class="s">return a list of all of the steps cons&#39;ed
</span><span class="s">with count of the steps in the path.
</span><span class="s">
</span><span class="s">[Uses </span><span class="ss">`cl-labels&#39;</span><span class="s"> to get tail-call optimisation.]&#34;</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">r</span> <span class="no">nil</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">cl-labels</span> <span class="p">((</span><span class="nv">collatz*</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
                    <span class="p">(</span><span class="nb">setq</span> <span class="nv">r</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">n</span> <span class="nv">r</span><span class="p">))</span>
                    <span class="p">(</span><span class="nb">cond</span>
                       <span class="p">((</span><span class="nf">=</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span>
                        <span class="nv">r</span><span class="p">)</span>
                       <span class="p">((</span><span class="nf">=</span> <span class="p">(</span><span class="nf">%</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)</span> <span class="mi">0</span><span class="p">)</span>
                          <span class="p">(</span><span class="nv">collatz*</span> <span class="p">(</span><span class="nf">/</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span>
                       <span class="p">(</span><span class="no">t</span>
                          <span class="p">(</span><span class="nv">collatz*</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">3</span> <span class="nv">n</span><span class="p">)))))))</span>
      <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">clst</span> <span class="p">(</span><span class="nv">collatz*</span> <span class="nv">n</span><span class="p">))</span>
            <span class="p">(</span><span class="nv">clngth</span> <span class="p">(</span><span class="nf">length</span> <span class="nv">clst</span><span class="p">)))</span>
        <span class="p">(</span><span class="nf">cons</span> <span class="p">(</span><span class="nf">nreverse</span> <span class="nv">clst</span><span class="p">)</span> <span class="nv">clngth</span><span class="p">)))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 13:</span>
  trying to eke out more performance for TCO cl-labels recursive Collatz function
</div>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">bench-multi</span>
  <span class="nb">:times</span> <span class="mi">10</span>
  <span class="nb">:forms</span> <span class="p">((</span><span class="s">&#34;collatz-cllabels-tco&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cllabels-tco</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cllabels-tco-improved&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cllabels-tco-improved</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 14:</span>
  bench-marking the two cl-labels recursive Collatz functions
</div>
<p>Here are the results:</p>
<table>
<thead>
<tr>
<th>Form</th>
<th>x fastest</th>
<th>Total runtime</th>
<th># of GCs</th>
<th>Total GC runtime</th>
</tr>
</thead>
<tbody>
<tr>
<td>collatz-cllabels-tco-improved</td>
<td>fastest</td>
<td>2.198572</td>
<td>3</td>
<td>0.719563</td>
</tr>
<tr>
<td>collatz-cllabels-tco</td>
<td>1.17</td>
<td>2.579470</td>
<td>4</td>
<td>0.923288</td>
</tr>
</tbody>
</table>
<p>We do seem to eke out a bit more performance.</p>
<p>But compared to the loops, it&rsquo;s still not as performant:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">bench-multi</span>
  <span class="nb">:times</span> <span class="mi">10</span>
  <span class="nb">:forms</span> <span class="p">((</span><span class="s">&#34;collatz-cllabels-tco&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cllabels-tco</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cllabels-tco-improved&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cllabels-tco-improved</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-while-loop&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-while-loop</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cl-loop&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cl-loop</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 15:</span>
  benchmarking two cl-labels and two loop Collatz implementations
</div>
<p>We seem to have done a bit better. Now <code>collatz-cllabels-tco-improved</code> is only
1.26x slower than the fastest, <code>collatz-while-loop</code>. We save an extra gc
over the older <code>collatz-cllabels-tco</code>, but still have to run an additional one
compared to the two loop functions.</p>
<table>
<thead>
<tr>
<th>Form</th>
<th>x fastest</th>
<th>Total runtime</th>
<th># of GCs</th>
<th>Total GC runtime</th>
</tr>
</thead>
<tbody>
<tr>
<td>collatz-while-loop</td>
<td>fastest</td>
<td>1.754987</td>
<td>2</td>
<td>0.486154</td>
</tr>
<tr>
<td>collatz-cl-loop</td>
<td>1.01</td>
<td>1.769489</td>
<td>2</td>
<td>0.486728</td>
</tr>
<tr>
<td>collatz-cllabels-tco-improved</td>
<td>1.26</td>
<td>2.208662</td>
<td>3</td>
<td>0.732637</td>
</tr>
<tr>
<td>collatz-cllabels-tco</td>
<td>1.44</td>
<td>2.528099</td>
<td>4</td>
<td>0.899793</td>
</tr>
</tbody>
</table>
<p>Putting all of the methods we&rsquo;ve come up with together and running more trials:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">bench-multi</span>
  <span class="nb">:times</span> <span class="mi">100</span>
  <span class="nb">:forms</span> <span class="p">((</span><span class="s">&#34;collatz-stream&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-stream</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cllabels-tco&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cllabels-tco</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cllabels-tco-improved&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cllabels-tco-improved</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-while-loop&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-while-loop</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))</span>
          <span class="p">(</span><span class="s">&#34;collatz-cl-loop&#34;</span>
           <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">integer-width</span> <span class="mi">99999999</span><span class="p">))</span>
             <span class="p">(</span><span class="nv">collatz-cl-loop</span> <span class="p">(</span><span class="nf">1+</span> <span class="p">(</span><span class="nf">expt</span> <span class="mi">2</span> <span class="mi">10000</span><span class="p">)))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 16:</span>
  full benchmark of all of our Collatz implementations
</div>
<p>Here, interestingly, we end up with <code>collatz-cl-loop</code> slightly edging
out <code>collatz-while-loop</code>. And our favourite (well, my favourite)
<code>collatz-cllabels-tco-improved</code> function doesn&rsquo;t do too poorly: it&rsquo;s only x1.28 slower
than the fastest (<code>collatz-cl-loop</code>); and even the old <code>collatz-cllabels-tco</code> with
additional argument passing is only x1.53 slower</p>
<table>
<thead>
<tr>
<th>Form</th>
<th>x fastest</th>
<th>Total runtime</th>
<th># of GCs</th>
<th>Total GC runtime</th>
</tr>
</thead>
<tbody>
<tr>
<td>collatz-cl-loop</td>
<td>fastest</td>
<td>17.733980</td>
<td>19</td>
<td>4.817641</td>
</tr>
<tr>
<td>collatz-while-loop</td>
<td>1.01</td>
<td>17.850171</td>
<td>20</td>
<td>4.977408</td>
</tr>
<tr>
<td>collatz-cllabels-tco-improved</td>
<td>1.28</td>
<td>22.736898</td>
<td>33</td>
<td>7.961263</td>
</tr>
<tr>
<td>collatz-cllabels-tco</td>
<td>1.53</td>
<td>27.146827</td>
<td>47</td>
<td>10.735285</td>
</tr>
<tr>
<td>collatz-stream</td>
<td>18.97</td>
<td>336.443089</td>
<td>961</td>
<td>240.167718</td>
</tr>
</tbody>
</table>
<p>The <code>collatz-stream</code> function remains significantly slower, x19 so.</p>
<p>There is an obvious correlation between runtime and garbage
collection, (19 vs 20 vs 33 vs 47 vs 961), so again possibly tweaking
garbage collection could change things. Although, if we get rid of the
garbage collection runtime differences we end up with:</p>
<table>
<thead>
<tr>
<th>Form</th>
<th>Total runtime w/o GC</th>
<th>x fastest</th>
</tr>
</thead>
<tbody>
<tr>
<td>collatz-while-loop</td>
<td>12.872762999999999</td>
<td>fastest</td>
</tr>
<tr>
<td>collatz-cl-loop</td>
<td>12.916338999999999</td>
<td>1.003</td>
</tr>
<tr>
<td>collatz-cllabels-tco-improved</td>
<td>14.775635000000001</td>
<td>1.148</td>
</tr>
<tr>
<td>collatz-cllabels-tco</td>
<td>16.411541999999997</td>
<td>1.275</td>
</tr>
<tr>
<td>collatz-stream</td>
<td>96.27537099999998</td>
<td>7.479</td>
</tr>
</tbody>
</table>
<p>Now <code>collatz-while-loop</code> is again indeed actually the fastest, but still
just barely more so than <code>collatz-cl-loop</code>; both recursive TCO&rsquo;ing
<code>cl-labels</code> aren&rsquo;t actually too much slower than the loops, though the
improved version is indeed better; and <code>collatz-stream</code> is still quite slow
in comparison. So garbage collection is part of the difference, but
not the only one, especially for the <code>streams</code> method.</p>
<h2 id="escaping--at-least-some--loops-out-of-recursion-and-into-dot-dot-dot-different-recursion--next-time">Escaping (at least some) loops: out of recursion and into&hellip; different recursion (next time)</h2>
<p>This was really an excursus (on an excursus). And, next time — really
— we&rsquo;ll deal with the Y Combinator. I know this one really wasn&rsquo;t a lambda
calculus post at all, but an excursion on our previous excursion into
recursion, here mostly practical considerations for Emacs. But
recursion is crucial for the upcoming discussion of the Y Combinator,
and paradoxes (and their uses). And that&rsquo;ll be interesting for lambda
calculus, and its connections with Lisp.</p>
<p>And, after we&rsquo;re through with Y Combinators and Fibonacci sequences
and barbers who shave everyone who does&rsquo;t shave themselves, and how
these things all tie together, we can get to something I&rsquo;ve been
working on for a while:— given that, connections aside, Lisp isn&rsquo;t
lambda calculus: can we come up with a good implementation of lambda
calculus in Lisp?</p>
<p>(And specifically in Emacs Lisp: which is maybe one of the Lisps least
naturally suited for this: a Scheme or Clojure would be a better
choice really. But it&rsquo;s more fun to try to get it to work in Elisp,
with all of the additional challenges it presents.)</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>(I&rsquo;m perhaps very slightly giving the game away; or, at least
providing a clue in the naming of it if you can think of what C-words
&ldquo;ersatz&rdquo; rhymes with.) <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>See <a href="https://www.algoritmarte.com/the-collatz-tree/">The Collatz Tree | Algoritmarte</a>. And the video it&rsquo;s
based on
<a href="https://www.youtube.com/watch?v=LqKpkdRRLZw">https://www.youtube.com/watch?v=LqKpkdRRLZw</a></p>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/246l3U2zngk?si=Y5owwBoDC3ivBouI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></li>
<li id="fn:3" role="doc-endnote">
<p>The <a href="https://www.quantamagazine.org/why-mathematicians-still-cant-solve-the-collatz-conjecture-20200922/">Quantamagazine article</a> notes:</p>
<blockquote>
<p>Do not try to solve this math problem.</p>
<p>You will be tempted. This problem is simply stated, easily understood,
and all too inviting. Just pick a number, any number: If the number is
even, cut it in half; if it’s odd, triple it and add 1. Take that new
number and repeat the process, again and again. If you keep this up,
you’ll eventually get stuck in a loop. At least, that’s what we think
will happen.</p>
<p>Take 10 for example: 10 is even, so we cut it in half to get 5. Since
5 is odd, we triple it and add 1. Now we have 16, which is even, so we
halve it to get 8, then halve that to get 4, then halve it again to
get 2, and once more to get 1. Since 1 is odd, we triple it and
add 1. Now we’re back at 4, and we know where this goes: 4 goes to 2
which goes to 1 which goes to 4, and so on. We’re stuck in a loop.</p>
<p>Or try 11: It’s odd, so we triple it and add 1. Now we have 34, which
is even, so we halve it to get 17, triple that and add 1 to get 52,
halve that to get 26 and again to get 13, triple that and add 1 to get
40, halve that to get 20, then 10, then 5, triple that and add 1 to
get 16, and halve that to get 8, then 4, 2 and 1. And we’re stuck in
the loop again.</p>
<p>The infamous Collatz conjecture says that if you start with any
positive integer, you’ll always end up in this loop. And you’ll
probably ignore my warning about trying to solve it: It just seems too
simple and too orderly to resist understanding. In fact, it would be
hard to find a mathematician who hasn’t played around with this
problem.</p>
<p>I couldn’t ignore it when I first learned of it in school. My friends
and I spent days trading thrilling insights that never seemed to get
us any closer to an answer. But the Collatz conjecture is infamous for
a reason: Even though every number that’s ever been tried ends up in
that loop, we’re still not sure it’s always true. Despite all the
attention, it’s still just a conjecture.</p>
<p>&hellip;.</p>
<p>It’s easy to verify that the Collatz conjecture is true for any
particular number: Just compute the orbit until you arrive at 1. But
to see why it’s hard to prove for every number, let’s explore a
slightly simpler function, <code>ℊ</code>.</p>
<p><code>g(n)=</code> <code>{n/2n+1, if n is even</code></p>
<p><code>{n+1, if n is odd</code></p>
<p>We might conjecture that orbits under <code>ℊ</code> always get to 1. I’ll call this
the “Nollatz” conjecture, but we could also call it the <code>n + 1</code>
conjecture. We could explore this by testing more orbits, but knowing
something is true for a bunch of numbers — even 268 of them — isn’t a
proof that it’s true for every number. Fortunately, the Nollatz
conjecture can actually be proved. Here’s how.</p>
<p>First, we know that half of a positive integer is always less than the
integer itself. So if <code>n</code> is even and positive, then <code>ℊ(n) = n/2 &lt; n</code>. In
other words, when an orbit reaches an even number, the next number
will always be smaller.
&hellip;.
This tells us that when an orbit under <code>ℊ</code> reaches an odd number greater
than 1, we’ll always be at a smaller number two steps later. And now
we can outline a proof of the Nollatz conjecture: Anywhere in our
orbit, whether at an even or an odd number, we’ll trend downward. The
only exception is when we hit 1 at the bottom of our descent. But once
we hit 1 we’re trapped the loop, just as we conjectured.
&hellip;
[For <code>f</code>, the Collatz conjencture,] as with <code>ℊ</code>, applying <code>f</code> to an even
number makes it smaller. And as with <code>ℊ</code>, applying <code>f</code> to an odd number
produces an even number, which means we know what happens next: <code>f</code> will
cut the new number in half.</p>
<p>But here’s where our argument falls apart. Unlike our example above,
this number is bigger than <code>n</code>: <code>3n+12 = 3n2 + 12</code>, and <code>3n2 = 1.5n</code>, which
is always bigger than <code>n</code>. The key to our proof of the Nollatz
conjecture was that an odd number must end up smaller two steps later,
but this isn’t true in the Collatz case. Our argument won’t work.</p>
</blockquote>
 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/recursion" term="recursion" label="recursion" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Lambda Calculus and Lisp, part 2 (recursion excursion)]]></title>
            <link href="https://babbagefiles.xyz/lambda-calculus-and-lisp-02-recursion/"/>
            <id>https://babbagefiles.xyz/lambda-calculus-and-lisp-02-recursion/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2025-02-20T01:26:00-06:00</published>
            <updated>2025-02-24T12:16:46-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>From the <a href="https://babbagefiles.xyz/lambda-calculus-and-lisp-01/">previous entry in this series</a>, one of the things of note in
discussing the nature of the connections between LISP and (the) lambda
calculus was John McCarthy&rsquo;s concern about recursion and higher-order
functions.</p>
<p>A couple of excerpts from previous quotes from McCarthy on the subject
to set the stage:</p>
<blockquote>
<p>…And so, the way in which to [be able to handle function passing/higher
order functions] was to borrow from Church&rsquo;s Lambda Calculus, to
borrow the lambda definition. Now, having borrowed this notation, one
the myths concerning LISP that people think up or invent for
themselves becomes apparent, and that is that LISP is somehow a
realization of the lambda calculus, or that was the intention. The
truth is that I didn&rsquo;t understand the lambda calculus, really. In
particular, I didn&rsquo;t understand that you really could do conditional
expressions in <strong>recursion</strong> in some sense in the pure lambda calculus.…</p>
<p><strong>[McCarthy 1978a:190<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>]</strong></p>
</blockquote>
<!--quoteend-->
<blockquote>
<p>…Writing <code>eval</code> required inventing a notation representing LISP
functions as LISP data, and such a notation was devised for the
purposes of the paper with no thought that it would be used to express
LISP programs in practice. Logical completeness required that the
notation used to express functions used as functional arguments be
extended to provide for recursive functions, and the <code>LABEL</code> notation
was invented by Nathaniel Rochester for that purpose. D.M.R. Park
pointed out that <code>LABEL</code> was logically unnecessary since the result
could be achieved using only <code>LAMBDA</code> — by a construction analogous to
Church&rsquo;s <em>Y</em>-operator, albeit in a more complicated way.…</p>
<p><strong>[McCarthy 1978a:179<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>]</strong></p>
</blockquote>
<p>Examining Church&rsquo;s Y Combinator will be something we return to
(probably in a number of posts), but I&rsquo;ll defer discussion of it for
the moment.</p>
<p>For now, let&rsquo;s consider recursion in lisps. We&rsquo;ll be talking a lot of
recursion in Emacs Lisp today in fact.</p>
<h2 id="self-reference-self-embedding">Self-reference, self-embedding</h2>
<p><strong>Recursion</strong> is a concept or process depends on a simpler or previous
version of itself. It&rsquo;s ubiquitous, including in the natural world:
Wikipedia <a href="https://en.wikipedia.org/wiki/Recursion#In_biology">notes</a> that &ldquo;Shapes that seem to have been created by
recursive processes sometimes appear in plants and animals, such as in
branching structures in which one large part branches out into two or
more similar smaller parts. One example is Romanesco broccoli.&rdquo;</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/Romanesco_broccoli_%28Brassica_oleracea%29.jpg" alt="Figure 1: Romanesco broccoli (Brassica oleracea), from Wikipedia"/> <figcaption>
                
                <p>
                    <span class="figure-number">Figure 1: </span>Romanesco broccoli (Brassica oleracea), from <a href="Romanesco broccoli (Brassica oleracea)">Wikipedia</a>
                    
                        
                        </p>
                
            </figcaption></figure>

<p>Recursion is a thing I deal with a lot in my day job, as it is a
feature of natural language, especially syntax and semantics. To
provide a quick illustration — though this is not at all how modern
generative syntax is done anymore — consider <a href="https://en.wikipedia.org/wiki/Phrase_structure_grammar">phrase structure grammar</a>
and <a href="https://en.wikipedia.org/wiki/Phrase_structure_rules">phrase structure rules</a> used by <a href="https://en.wikipedia.org/wiki/Noam_Chomsky">Noam Chomsky</a> and his colleagues in
the 1950s.</p>
<p>Sentences (and linguistics objects generally) have formal structure,
and it is part of the productive/creative nature of language that we
might envision this structure as involving abstract structure rules
that can be expanded in different ways (and then have vocabulary
filled in).</p>
<p>A phrase structure rule will generally have the form <code>A → [B C]</code>,
indicating that <code>A</code> may be rewritten or expanded into <code>[B C]</code>. (In the
following, I&rsquo;ll use round brackets <code>()</code>'s to denote optional
constituents.)</p>
<p>So, a subset of these rules for English might include something like
(note that some things have multiple rules that can apply to them):</p>
<div class="highlight"><pre class="chroma"><code class="language-prolog" data-lang="prolog"><span class="mf">1.</span> <span class="nv">S</span> <span class="s">→</span> <span class="nv">NP</span> <span class="nv">VP</span>
<span class="mf">2.</span> <span class="nv">NP</span> <span class="s">→</span> <span class="p">(</span><span class="nv">Det</span><span class="p">)</span> <span class="nv">N</span>
<span class="mf">3.</span> <span class="nv">N</span> <span class="s">→</span> <span class="p">(</span><span class="nv">AdjP</span><span class="p">)</span><span class="o">*</span> <span class="nv">N</span>
<span class="mf">4.</span> <span class="nv">VP</span> <span class="s">→</span> <span class="nv">V</span>
<span class="mf">5.</span> <span class="nv">VP</span> <span class="s">→</span> <span class="nv">V</span> <span class="nv">NP</span> <span class="p">(</span><span class="nv">NP</span><span class="p">)</span>
<span class="mf">6.</span> <span class="nv">VP</span> <span class="s">→</span> <span class="nv">V</span> <span class="nv">CP</span>
<span class="mf">7.</span> <span class="nv">CP</span> <span class="s">→</span> <span class="p">(</span><span class="nv">Comp</span><span class="p">)</span> <span class="nv">S</span>
<span class="p">...</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 1:</span>
  a snippet of phrase structure grammar rules for English [Nb.: again, not prolog, but maybe the best fontlocking choice here]
</div>
<p>(Where <code>S</code> is &ldquo;sentence&rdquo;; <code>Det</code> is a determiner (like &ldquo;the&rdquo;, &ldquo;a&rdquo;); <code>NP</code> is a
noun phrase; <code>Nₙ</code> is a noun head; <code>AdjP</code> is an adjective phrase; <code>VP</code> is
a verb phrase; <code>V</code> is a verb head; <code>CP</code> is a complementiser phrase; <code>Comp</code>
is a complementiser (like &ldquo;that&rdquo;).)</p>
<p>So we can rewrite <code>S</code> as <code>NP VP</code> (1) and then rewrite <code>NP VP</code> as <code>Det N VP</code>
(2, choosing an optional <code>Det</code>) and then <code>Det N VP</code> as <code>Det N V</code> (4) and then
insert lexical items of the appropriate category into the &lsquo;heads&rsquo; (the
non-<code>P</code> elements). So we might choose &ldquo;the&rdquo; for <code>Det</code> and &ldquo;cat&rdquo; for <code>N</code> and
&ldquo;purrs&rdquo; for <code>V</code>, and get the sentence &ldquo;the cat purrs&rdquo;.</p>
<p>But note that some of the rules allow for expansion into elements that
contain expansions back into themselves. So rule (1) allows an <code>S</code> to
exapnd into <code>NP VP</code> and rule (6) allows for a <code>VP</code> to expand into <code>V CP</code> and rule
(7) allows for a <code>CP</code> to expand into an <code>S</code>. At which point we can apply
rule (1) again to expand the new <code>S</code> into <code>NP VP</code>, and then repeat this
process as many times as we like:</p>
<div class="highlight"><pre class="chroma"><code class="language-prolog" data-lang="prolog"><span class="nv">S</span>
<span class="nv">NP</span> <span class="nv">VP</span>
<span class="nv">N</span> <span class="nv">VP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">CP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">S</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">NP</span> <span class="nv">VP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">VP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">CP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">S</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">NP</span> <span class="nv">VP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">VP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">CP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">S</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">NP</span> <span class="nv">VP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">VP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">CP</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">S</span>
<span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">N</span> <span class="nv">V</span> <span class="nv">Comp</span> <span class="nv">NP</span> <span class="nv">VP</span>
<span class="p">...</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 2:</span>
  a recursive English sentence expansion [Nb.: again, not prolog, but maybe the best fontlocking choice here]
</div>
<p>And it&rsquo;s easy to imagine examples of what such a sentence could be
like, e.g., &ldquo;John said that Sita said that Bill said that Mary said
that Ram said that Kim said that&hellip;&quot;. It won&rsquo;t be infinitely long, but
there&rsquo;s no particular theoretical bound on how long it could be
(memory processing and finite human lifespans will impose practical
limits, of course).</p>
<p>On the formal semantics side, things are similar: consider that
logical languages too (which are often used to formalise natural
language semantics) allow for recursion. <a href="https://en.wikipedia.org/wiki/Propositional_calculus">Propositional logic</a>
construction with Boolean operators too have no theoretical upper
limits: we can write <code>(t ↔ (p ∧ (q ∨ (r → (s ∧ ¬¬¬¬¬¬t)))))</code>,<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> and
there&rsquo;s nothing which prevents composing this bit of formalism with
yet another bit, and so on.</p>
<p>And in mathematics and computer science, recursion is often a thing
which suggests itself.</p>
<p>For instance, though there are other ways of calculating it, the
Fibonacci sequence<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, i.e., a sequence of numbers in which each
element is the sum of the two elements that precede it (e.g. <code>0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144…</code>).</p>
<p>A natural way of writing an equation to calculate these is something
like:</p>
<blockquote>
<ol>
<li>Fib(0) = 0 as base case 1.</li>
<li>Fib(1) = 1 as base case 2.</li>
<li>For all integers <em>n</em> &gt; 1, Fib(<em>n</em>) = Fib(<em>n</em> − 1) + Fib(<em>n</em> − 2).</li>
</ol>
</blockquote>
<p>Rule 3 makes reference to itself. I.e., in order (by this method) to
calculate <code>Fib(6)</code>, you have to calculate <code>Fib(5)</code>, for which you have to
calculate <code>Fib(4))</code>, for which you have to calculate <code>Fib(3)</code>, for which
you have to calculate <code>Fib(2)</code>, which you can then base on rules (1) &amp;
(2): you can add <code>0</code> and <code>1</code> (= <code>Fib(0)</code> and <code>Fib(1))</code> together to get <code>Fib(2)</code>,
and then you can calculate <code>Fib(3)</code> by adding <code>Fib(1)</code> and <code>Fib(2)</code> and so on.</p>
<h2 id="cursed-recursion">Cursed recursion</h2>
<p>In early Lisp(s), despite the concern around recursion, writing
recursive functions was/is not always pragmatically viable, because it
can lead to stack overflows (potential infinities are hard in
practice).</p>
<p><a href="https://en.wikipedia.org/wiki/Scheme_(programming_language)">Scheme</a>, a Lisp dialect originally created at MIT by <a href="https://en.wikipedia.org/wiki/Guy_L._Steele">Guy L. Steele, Jr.</a>
and <a href="https://en.wikipedia.org/wiki/Gerald_Jay_Sussman">Gerald Jay Sussman</a>, was the first Lisp to implement tail call
optimisation, which is a way of making significantly recursive
functions viable, making tail calls similar in memory requirement to
their equivalent loops.</p>
<p>Before talking (a little bit more) about tail recursion, let&rsquo;s look at
concrete examples of a tail recursive function and its loop
equivalent. We&rsquo;ve mentioned Fibonacci numbers already, so let&rsquo;s try to
write functions calculate these (in Emacs Lisp).</p>
<p>Following the mathematical abstraction for the Fibonacci sequence
above, we could write a function like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">fib1</span> <span class="p">(</span><span class="nv">n</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">)</span>
  <span class="s">&#34;Calculate the first </span><span class="ss">`n&#39;</span><span class="s"> Fibonacci numbers, recursively.&#34;</span>
  <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nf">&lt;</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span>
      <span class="nv">a</span>
    <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span>
          <span class="p">(</span><span class="nv">fib1</span> <span class="p">(</span><span class="nf">1-</span> <span class="nv">n</span><span class="p">)</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">)))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 3:</span>
  a first go at a recursive elisp function for fibonacci numbers
</div>
<p>I&rsquo;ve tried to make this as simple as possible, we could make it nicer
(say, with a wrapper function or some some of <code>flet</code>) so that we didn&rsquo;t
have to pass in the initial values. But, keeping it simple (for now):
<code>fib1</code> is a function taking three arguments: <code>n</code>, the quantity of
Fibonacci numbers to return; <code>a</code>, the first number to start with; and <code>b</code>,
the second number to start with.</p>
<p>Following the schema above, we&rsquo;re going to pass <code>0</code> for <code>a</code> and <code>1</code> for
<code>b</code>. Let&rsquo;s get the first ten numbers of the sequence, and pass <code>10</code> for <code>n</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">fib1</span> <span class="mi">10</span> <span class="mi">0</span> <span class="mi">1</span><span class="p">)</span>  <span class="c1">; (0 1 1 2 3 5 8 13 21 34 . 55)</span>
</code></pre></div><p>I know, the output is a bit ugly because we&rsquo;re just cons&rsquo;ing the
results and so it&rsquo;s not a proper list, but we&rsquo;re keeping things
simple.</p>
<p>Let&rsquo;s walk through how it works. The function first looks at <code>n</code>, if <code>n</code>
is less than <code>1</code>, it returns <code>a</code> (whatever it is). If <code>n</code> isn&rsquo;t less than <code>1</code>,
we return the <code>cons</code> of <code>a</code> with the result of calling <code>fib1</code> itself on &ldquo;<code>n</code>
minus 1&rdquo; <code>b</code> and &ldquo;<code>a</code> plus <code>b</code>&rdquo;.</p>
<p>So if we start with <code>n=3</code>, <code>a=0</code>, <code>b=1</code>, that is, evaluate <code>(fib1 3 0 1)</code>, the
function would say, well, 3 isn&rsquo;t less than 1, so I&rsquo;m going to create
a <code>cons</code> with <code>0</code> and the result of calling <code>(fib1 2 1 1)</code> (2 = &ldquo;<code>n</code> minus 1&rdquo;,
because <code>n</code> is currently 3; <code>1</code> because <code>b=1</code>; and <code>1</code> because <code>a + b</code> = <code>0 + 1</code> =
<code>1</code>).</p>
<p>So at this point we have a <code>cons</code> that looks like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="mi">0</span> <span class="o">.</span> <span class="p">(</span><span class="nv">fib1</span> <span class="mi">2</span> <span class="mi">1</span> <span class="mi">1</span><span class="p">))</span>  <span class="c1">;; [= (cons 0 (fib1 2 1 1))]</span>
</code></pre></div><p>When we evaluate <code>(fib1 2 1 1)</code>, <code>n</code> is still not less than <code>1</code>, so we&rsquo;re
going to <code>cons</code> the current value of <code>a</code> (which is <code>1</code>) with another call to
<code>fib1</code>: <code>(fib1 1 1 2)</code> (1 = &ldquo;<code>n</code> minus 1&rdquo;, because <code>n</code> is currently 2; <code>1</code>
because <code>b=1</code>; and <code>2</code> because <code>a + b</code> = <code>1 + 1</code> = <code>2</code>).</p>
<p>So now we have:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="mi">0</span> <span class="mi">1</span> <span class="o">.</span> <span class="p">(</span><span class="nv">fib1</span> <span class="mi">1</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">))</span>  <span class="c1">;; [= (cons 0 (cons 1 (fib1 1 1 2)))]</span>
</code></pre></div><p>Now we have to evaluate <code>(fib1 1 1 2)</code>. <code>n</code> is still not less than 1; so
we create another <code>cons</code> of <code>1</code> (as <code>a</code> is currently <code>1</code>) with yet another
call of <code>fib1</code>: <code>(fib1 0 2 3)</code> (0 = &ldquo;<code>n</code> minus 1&rdquo;, because <code>n</code> is currently 1; <code>2</code>
because <code>b=2</code>; and <code>3</code> because <code>a + b</code> = <code>1 + 2</code> = <code>3</code>). And so now:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="mi">0</span> <span class="mi">1</span> <span class="mi">1</span> <span class="o">.</span> <span class="p">(</span><span class="nv">fib1</span> <span class="mi">0</span> <span class="mi">2</span> <span class="mi">3</span><span class="p">))</span>  <span class="c1">;; [= (cons 0 (cons 1 (cons 1 (fib1 0 2 3))))]</span>
</code></pre></div><p>And, finally, evaluating <code>(fib1 0 2 3)</code>, now <code>n</code> is less than one, so we
take the first branch of the conditional and just return <code>a</code>, which is
<code>2</code>. So the result of starting with <code>(fib1 3 0 1)</code> is:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="mi">0</span> <span class="mi">1</span> <span class="mi">1</span> <span class="o">.</span> <span class="mi">2</span><span class="p">)</span>  <span class="c1">;; [= (cons 0 (cons 1 (cons 1 2)))]</span>
</code></pre></div><p>And you can try this with other values of <code>n</code>, e.g., try evaluating
<code>(fib1 100 0 1)</code> to get the first 100 members of the sequence.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<p>But, at least for me on Emacs 30.0.93, 529 is the limit. If we try
<code>(fib1 520 0 1)</code>, the debugger pops up with a 1622 line long error,
which begins:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="nv">Debugger</span> <span class="nv">entered--Lisp</span> <span class="nv">error:</span> <span class="p">(</span><span class="nv">excessive-lisp-nesting</span> <span class="mi">1622</span><span class="p">)</span>
  <span class="nv">cl-print--cons-tail</span><span class="p">(</span><span class="nv">excessive-lisp-nesting</span> <span class="p">(</span><span class="mi">1601</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">)</span>
  <span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">(</span><span class="nv">object</span> <span class="nv">stream</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x491d55561699a09&gt;</span><span class="p">)(</span><span class="err">#</span><span class="mi">0</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">)</span>
  <span class="nf">apply</span><span class="p">(</span><span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">(</span><span class="nv">object</span> <span class="nv">stream</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x491d55561699a09&gt;</span><span class="p">)</span> <span class="p">(</span><span class="err">#</span><span class="mi">0</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">))</span>
  <span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">(</span><span class="kp">&amp;rest</span> <span class="nv">args</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x1c31d892b8046a8b&gt;</span><span class="p">)()</span>
  <span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">(</span><span class="nv">cl--cnm</span> <span class="nv">object</span> <span class="nv">stream</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x7c361f66f109692&gt;</span><span class="p">)(</span><span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">(</span><span class="kp">&amp;rest</span> <span class="nv">args</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x1c31d892b8046a8b&gt;</span><span class="p">)</span> <span class="err">#</span><span class="mi">0</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">)</span>
  <span class="nf">apply</span><span class="p">(</span><span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">(</span><span class="nv">cl--cnm</span> <span class="nv">object</span> <span class="nv">stream</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x7c361f66f109692&gt;</span><span class="p">)</span> <span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">(</span><span class="kp">&amp;rest</span> <span class="nv">args</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x1c31d892b8046a8b&gt;</span><span class="p">)</span> <span class="p">(</span><span class="err">#</span><span class="mi">0</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">))</span>
  <span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">(</span><span class="nv">object</span> <span class="nv">stream</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x1f277a9a6dc403fa&gt;</span><span class="p">)(</span><span class="err">#</span><span class="mi">0</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">)</span>
  <span class="nf">apply</span><span class="p">(</span><span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">(</span><span class="nv">object</span> <span class="nv">stream</span><span class="p">)</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x1f277a9a6dc403fa&gt;</span><span class="p">)</span> <span class="err">#</span><span class="mi">0</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">)</span>
  <span class="nv">cl-print-object</span><span class="p">(</span><span class="err">#</span><span class="mi">0</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">)</span>
  <span class="nv">cl-prin1</span><span class="p">(</span><span class="err">#</span><span class="mi">0</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">)</span>
  <span class="nv">backtrace--print</span><span class="p">(</span><span class="err">#</span><span class="mi">0</span> <span class="err">#</span><span class="nv">&lt;buffer</span>  <span class="vg">*temp*</span><span class="nf">&gt;</span><span class="p">)</span>
  <span class="nv">cl-print-to-string-with-limit</span><span class="p">(</span><span class="nv">backtrace--print</span> <span class="err">#</span><span class="mi">0</span> <span class="mi">5000</span><span class="p">)</span>
  <span class="nv">backtrace--print-to-string</span><span class="p">(</span><span class="err">#</span><span class="mi">0</span> <span class="no">nil</span><span class="p">)</span>
  <span class="nv">backtrace-print-to-string</span><span class="p">(</span><span class="err">#</span><span class="mi">0</span><span class="p">)</span>
  <span class="nv">debugger--insert-header</span><span class="p">((</span><span class="ne">error</span> <span class="err">#</span><span class="mi">0</span> <span class="nb">:backtrace-base</span> <span class="nv">eval-expression--debug</span><span class="p">))</span>
  <span class="err">#</span><span class="nv">f</span><span class="p">(</span><span class="nv">compiled-function</span> <span class="p">()</span> <span class="err">#</span><span class="nv">&lt;bytecode</span> <span class="nv">0x299e53d67d1ac62&gt;</span><span class="p">)()</span>
  <span class="nv">backtrace-print</span><span class="p">()</span>
  <span class="nv">debugger-setup-buffer</span><span class="p">((</span><span class="ne">error</span> <span class="err">#</span><span class="mi">0</span> <span class="nb">:backtrace-base</span> <span class="nv">eval-expression--debug</span><span class="p">))</span>
  <span class="nv">debug</span><span class="p">(</span><span class="ne">error</span> <span class="err">#</span><span class="mi">0</span> <span class="nb">:backtrace-base</span> <span class="nv">eval-expression--debug</span><span class="p">)</span>
  <span class="nv">eval-expression--debug</span><span class="p">(</span><span class="err">#</span><span class="mi">0</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nf">&lt;</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="nv">a</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span> <span class="p">(</span><span class="nv">fib1</span> <span class="p">(</span><span class="nf">1-</span> <span class="nv">n</span><span class="p">)</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">))))</span>
  <span class="nv">fib1</span><span class="p">(</span><span class="mi">0</span> <span class="mi">259396630450514843945535792456880074043523940756078363514486570322782139633750401577338505233670220572153381665</span> <span class="mi">419712564636128966418863068957011388899128076671547993021605479585858227224221424221791102364954108601240491394</span><span class="p">)</span>
  <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span> <span class="p">(</span><span class="nv">fib1</span> <span class="p">(</span><span class="nf">1-</span> <span class="nv">n</span><span class="p">)</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">)))</span>
  <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nf">&lt;</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="nv">a</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span> <span class="p">(</span><span class="nv">fib1</span> <span class="p">(</span><span class="nf">1-</span> <span class="nv">n</span><span class="p">)</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">))))</span>
  <span class="nv">fib1</span><span class="p">(</span><span class="mi">1</span> <span class="mi">160315934185614122473327276500131314855604135915469629507118909263076087590471022644452597131283888029087109729</span> <span class="mi">259396630450514843945535792456880074043523940756078363514486570322782139633750401577338505233670220572153381665</span><span class="p">)</span>
  <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span> <span class="p">(</span><span class="nv">fib1</span> <span class="p">(</span><span class="nf">1-</span> <span class="nv">n</span><span class="p">)</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">)))</span>
  <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nf">&lt;</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="nv">a</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span> <span class="p">(</span><span class="nv">fib1</span> <span class="p">(</span><span class="nf">1-</span> <span class="nv">n</span><span class="p">)</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">))))</span>
  <span class="nv">fib1</span><span class="p">(</span><span class="mi">2</span> <span class="mi">99080696264900721472208515956748759187919804840608734007367661059706052043279378932885908102386332543066271936</span> <span class="mi">160315934185614122473327276500131314855604135915469629507118909263076087590471022644452597131283888029087109729</span><span class="p">)</span>
<span class="o">....</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 4:</span>
  beginning of excessive-lisp-nesting error for our fib1 function
</div>
<p>Because we&rsquo;ve built up a long, deeply-embedded list of conses and
Emacs has a limit of how deep it&rsquo;s willing/able to go (you can see
above that we&rsquo;ve almost made it to the end, just needing to calculate
<code>fib1(0)</code>, when Emacs decides it&rsquo;s had enough).</p>
<p>In Scheme and elsewhere, self-recursive calls at the ends (&ldquo;tails&rdquo;) of
functions can be optimised to avoid these sorts of stack overflows
(<code>excessive-lisp-nesting</code>).<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> Tail-call optimisation lets
procedure calls in tail positions be treated a specialised GOTO
statements, which can be efficiently processed:</p>
<blockquote>
<p>…only in cases where structures are explicitly declared to be
dynamically referenced should the compiler be forced to leave them on
the stack in an otherwise tail-recursive situation. In general,
procedure calls may be usefully thought of as GOTO statements which
also pass parameters, and can be uniformly encoded as JUMP
instructions. This is a simple, universal technique, to be contrasted
with […] more powerful recursion-removal techniques…</p>
<p><strong>[Steele 1977:155<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>]</strong></p>
</blockquote>
<p>But our <code>fib1</code> function doesn&rsquo;t do this, and we end up flooded the stack
with too many conses.</p>
<h2 id="back-to-loops">Back to loops</h2>
<p>Recursive functions are perhaps most idiomatic in Scheme (among lisps,
I mean). Some implementations of Common Lisp can do tail-call
optimisation, but loops are perhaps more common, and certainly in
Emacs Lisp (for reasons you can see above), loops are usually what are
used. And so we can write a new Fibonacci function with a loop. It
won&rsquo;t be nearly as pretty, but it&rsquo;ll work better.</p>
<p>Here&rsquo;s one possible implementation:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">fib2</span> <span class="p">(</span><span class="nv">n</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">)</span>
  <span class="s">&#34;Calculate the first </span><span class="ss">`n&#39;</span><span class="s"> Fibonacci numbers,
</span><span class="s">in a loop.&#34;</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">result</span> <span class="no">nil</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">dotimes</span> <span class="p">(</span><span class="nv">c</span> <span class="nv">n</span><span class="p">)</span>
      <span class="p">(</span><span class="nb">setq</span> <span class="nv">result</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span> <span class="nv">result</span><span class="p">))</span>
      <span class="p">(</span><span class="nb">setq</span> <span class="nv">tempa</span> <span class="nv">b</span><span class="p">)</span>
      <span class="p">(</span><span class="nb">setq</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">))</span>
      <span class="p">(</span><span class="nb">setq</span> <span class="nv">a</span> <span class="nv">tempa</span><span class="p">))</span>
    <span class="p">(</span><span class="nf">nreverse</span> <span class="nv">result</span><span class="p">)))</span>

<span class="p">(</span><span class="nv">fib2</span> <span class="mi">10</span> <span class="mi">0</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">; (0 1 1 2 3 5 8 13 21 34)</span>

<span class="p">(</span><span class="nb">setq</span> <span class="nv">bigfib-50000</span> <span class="p">(</span><span class="nv">fib2</span> <span class="mi">50000</span> <span class="mi">0</span> <span class="mi">1</span><span class="p">))</span> <span class="c1">; this will work - we can get 50,000 numbers</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 5:</span>
  a second go at a fibonacci function, with looping
</div>
<p>The result is prettier at least: a proper rather than an improper list
(because we started by <code>cons</code>'ing onto an empty list). Our <code>fib2</code> function
itself isn&rsquo;t as mathematically pleasing as our <code>fib1</code> function, we end
up with a lot of <code>setq</code>'s (the <code>nreverse</code> at the end reverses our list,
because the way we build up our list is by <code>cons</code>'ing the first results
first, so they end up at the end until we flip them with
<code>nreverse</code>). But it works well. If you try to <code>(fib2 100000 0 1)</code>, it&rsquo;ll
fail, but not because of stack overflow, just because we end up with
numbers that are too big for Emacs. But you can certain get the over
50,000 members of the Fibonacci sequence, which is much better than
<code>fib1</code>'s limit of 529.</p>
<p>And <code>dotimes</code> is just one loop procedure available. (See <a href="https://www.gnu.org/software/emacs/manual/html_node/cl/Loop-Examples.html"><code>cl-loop</code></a> for a more
powerful one.)</p>
<h2 id="optimal-tail-with-emacs">Optimal Tail with Emacs</h2>
<p>Ok, so, practically, we should probably generally prefer loops over
tail-recursive functions in Emacs. But, what if we just like the latter
more?<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> Are there any other possibilities?</p>
<p><a href="https://mastodon.social/@wilfredh">Wilfred Hughes</a> has an emacs package <a href="https://github.com/Wilfred/tco.el"><code>tco.el</code></a> which implements a
special macro for writing tail-recursive functions.<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> It works by
replacing each self-call with a <a href="https://en.wikipedia.org/wiki/Thunk">thunk</a>, and wrapping the function body
in a loop that repeatedly evaluates the thunk. Thus a
function <code>foo</code> defined with the <code>defun-tco</code> macro:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">defun-tco</span> <span class="nv">foo</span> <span class="p">(</span><span class="o">...</span><span class="p">)</span>
  <span class="p">(</span><span class="o">...</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">foo</span> <span class="p">(</span><span class="o">...</span><span class="p">)))</span>
</code></pre></div><p>would be re-written as:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">foo</span> <span class="p">(</span><span class="o">...</span><span class="p">)</span>
   <span class="p">(</span><span class="nb">flet</span> <span class="p">(</span><span class="nv">foo-thunk</span> <span class="p">(</span><span class="o">...</span><span class="p">)</span>
               <span class="p">(</span><span class="o">...</span><span class="p">)</span>
               <span class="p">(</span><span class="nb">lambda</span> <span class="p">()</span> <span class="p">(</span><span class="nv">foo-thunk</span> <span class="p">(</span><span class="o">...</span><span class="p">))))</span>
     <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">result</span> <span class="p">(</span><span class="nf">apply</span> <span class="nv">foo-thunk</span> <span class="p">(</span><span class="o">...</span><span class="p">))))</span>
       <span class="p">(</span><span class="nb">while</span> <span class="p">(</span><span class="nf">functionp</span> <span class="nv">result</span><span class="p">)</span>
         <span class="p">(</span><span class="nb">setq</span> <span class="nv">result</span> <span class="p">(</span><span class="nf">funcall</span> <span class="nv">result</span><span class="p">)))</span>
       <span class="nv">result</span><span class="p">)))</span>
</code></pre></div><p>And this delays evaluation in such a way as to avoid stack
overflows. Unfortunately, at least currently for me (Emacs 30.0.93
again), tco.el seems to <a href="https://github.com/Wilfred/tco.el/issues/10">have some issues</a>.</p>
<p>In Emacs 28.1, <code>cl-labels</code> (one of the ways of sort of doing <code>let</code>'s for
functions) <a href="https://github.com/emacs-mirror/emacs/commit/29c7f8c915c3889dfd5b25878aa0692f826cd38f">gained some limited tail-call optimisation</a> (as did
<code>named-let</code>, which uses <code>cl-labels</code>).</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">fib3</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Calculate the first </span><span class="ss">`n&#39;</span><span class="s"> Fibonacci numbers,
</span><span class="s">recursively, with limited tail-call optimisation
</span><span class="s">through </span><span class="ss">`cl-labels&#39;</span><span class="s">?!&#34;</span>
  <span class="p">(</span><span class="nb">cl-labels</span> <span class="p">((</span><span class="nv">fib*</span> <span class="p">(</span><span class="nv">n</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">)</span>
                <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nf">&lt;</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span>
                    <span class="nv">a</span>
                  <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span>
                        <span class="p">(</span><span class="nv">fib*</span> <span class="p">(</span><span class="nf">1-</span> <span class="nv">n</span><span class="p">)</span>
                              <span class="nv">b</span>
                              <span class="p">(</span><span class="nf">+</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">))))))</span>
    <span class="p">(</span><span class="nv">fib*</span> <span class="nv">n</span> <span class="mi">0</span> <span class="mi">1</span><span class="p">)))</span>

<span class="p">(</span><span class="nb">setq</span> <span class="nv">bigfib3</span> <span class="p">(</span><span class="nv">fib3</span> <span class="mi">397</span><span class="p">))</span> <span class="c1">; 396 highest that works</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 6:</span>
  a third go at a fibonacci function, with cl-labels
</div>
<p>At least the way I&rsquo;ve written it, it seems to suffer an overflow even
sooner (at 397 rather than 529 as for our <code>fib1</code>), because it has to
come back and do the <code>cons</code> after the tail-call — so <code>fib*</code> isn&rsquo;t actually
in the tail. (We can fix this in at least one way, which we&rsquo;ll do in <code>fib5</code>.)</p>
<p>We could try to write an accumulator as a hack, where we try to do
ours conses one at a time and pass along the results, but this fares no
better than our <code>fib1</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">fib4</span> <span class="p">(</span><span class="nv">n</span> <span class="nv">a</span> <span class="nv">b</span> <span class="nv">accum</span><span class="p">)</span>
  <span class="s">&#34;Calculate the first </span><span class="ss">`n&#39;</span><span class="s"> Fibonacci numbers, recursively,
</span><span class="s">but collect conses as we go and keep track of the length of
</span><span class="s">the </span><span class="ss">`accum&#39;</span><span class="s"> cp. against </span><span class="ss">`n&#39;</span><span class="s">.&#34;</span>
  <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">accum</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span> <span class="nv">accum</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">accum-lng</span> <span class="p">(</span><span class="nf">length</span> <span class="nv">accum</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nf">&lt;</span> <span class="nv">n</span> <span class="nv">accum-lng</span><span class="p">)</span>
        <span class="p">(</span><span class="nf">nreverse</span> <span class="nv">accum</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">fib4</span> <span class="nv">n</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">b</span> <span class="nv">a</span><span class="p">)</span> <span class="nv">accum</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">setq</span> <span class="nv">bigfib4-529</span> <span class="p">(</span><span class="nv">fib4</span> <span class="mi">529</span> <span class="mi">0</span> <span class="mi">1</span> <span class="no">nil</span><span class="p">))</span> <span class="c1">; last good</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">bigfib4-530</span> <span class="p">(</span><span class="nv">fib4</span> <span class="mi">530</span> <span class="mi">0</span> <span class="mi">1</span> <span class="no">nil</span><span class="p">))</span> <span class="c1">; overflows</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 7:</span>
  a fourth go at a fibonacci function, with an accumulator
</div>
<p>If we combine <code>cl-labels</code> and the accumulator trick, however, we do seem
to be able to escape stack overflows, because now we&rsquo;ve got <code>fib*</code>
properly in the tail:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">fib5</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Calculate the first </span><span class="ss">`n&#39;</span><span class="s"> Fibonacci numbers, recursively,
</span><span class="s">using both cl-labels and the accumulator trick.&#34;</span>
  <span class="p">(</span><span class="nb">cl-labels</span> <span class="p">((</span><span class="nv">fib*</span> <span class="p">(</span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">accum</span><span class="p">)</span>
                   <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">accum</span> <span class="p">(</span><span class="nf">cons</span> <span class="nv">a</span> <span class="nv">accum</span><span class="p">))</span>
                         <span class="p">(</span><span class="nv">accum-lng</span> <span class="p">(</span><span class="nf">length</span> <span class="nv">accum</span><span class="p">)))</span>
                     <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nf">&lt;</span> <span class="nv">n</span> <span class="nv">accum-lng</span><span class="p">)</span>
                         <span class="p">(</span><span class="nf">nreverse</span> <span class="nv">accum</span><span class="p">)</span>
                         <span class="p">(</span><span class="nv">fib*</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">b</span> <span class="nv">a</span><span class="p">)</span> <span class="nv">accum</span><span class="p">)))))</span>
            <span class="p">(</span><span class="nv">fib*</span> <span class="mi">0</span> <span class="mi">1</span> <span class="no">nil</span><span class="p">)))</span>

<span class="p">(</span><span class="nb">setq</span> <span class="nv">bigfib5-10000</span> <span class="p">(</span><span class="nv">fib5</span> <span class="mi">10000</span><span class="p">))</span> <span class="c1">; ok</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">bigfib5-50000</span> <span class="p">(</span><span class="nv">fib5</span> <span class="mi">50000</span><span class="p">))</span> <span class="c1">; very slow, but ok</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 8:</span>
  a fifth go at a fibonacci function, with cl-labels and an accumulator
</div>
<p>Now we&rsquo;re back in the realms of what our <code>fib2</code> non-recursive loop-style
function could do. Although <code>(setq bigfib5-50000 (fib5 50000))</code>
calculates very slowly (worse than our looping <code>fib2</code>), so that&rsquo;s not ideal.</p>
<h2 id="stream-of-conses-ness">Stream of Conses-ness</h2>
<p>But here&rsquo;s another possibility: <a href="https://nicolas.petton.fr">Nicholas Pettton</a>&lsquo;s <a href="https://elpa.gnu.org/packages/stream.html"><code>stream</code></a> package
for emacs, where &ldquo;streams&rdquo; are delayed evaluations of <code>cons</code> cells.</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; -*- lexical-binding: t; -*-</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">fib6</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
  <span class="s">&#34;Return a list of the first </span><span class="ss">`n&#39;</span><span class="s"> Fibonacci numbers,
</span><span class="s">implemented as stream of (delayed evaluation) conses.&#34;</span>
  <span class="p">(</span><span class="nb">cl-labels</span> <span class="p">((</span><span class="nv">fibonacci-populate</span> <span class="p">(</span><span class="nv">a</span> <span class="nv">b</span><span class="p">)</span>
                <span class="p">(</span><span class="nv">stream-cons</span> <span class="nv">a</span> <span class="p">(</span><span class="nv">fibonacci-populate</span> <span class="nv">b</span> <span class="p">(</span><span class="nf">+</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">)))))</span>
    <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">fibonacci-stream</span>
           <span class="p">(</span><span class="nv">fibonacci-populate</span> <span class="mi">0</span> <span class="mi">1</span><span class="p">))</span>
          <span class="p">(</span><span class="nv">fibs</span> <span class="no">nil</span><span class="p">))</span>
      <span class="p">(</span><span class="nb">dotimes</span> <span class="p">(</span><span class="nv">c</span> <span class="nv">n</span><span class="p">)</span>
        <span class="p">(</span><span class="nb">setq</span> <span class="nv">fibs</span> <span class="p">(</span><span class="nf">cons</span> <span class="p">(</span><span class="nv">stream-pop</span> <span class="nv">fibonacci-stream</span><span class="p">)</span> <span class="nv">fibs</span><span class="p">)))</span>
      <span class="p">(</span><span class="nf">nreverse</span> <span class="nv">fibs</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">setq</span> <span class="nv">fib6-10k</span> <span class="p">(</span><span class="nv">fib6</span> <span class="mi">10000</span><span class="p">))</span> <span class="c1">; ok</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">fib6-50k</span> <span class="p">(</span><span class="nv">fib6</span> <span class="mi">50000</span><span class="p">))</span> <span class="c1">; little slow, but works</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">fib6-100k</span> <span class="p">(</span><span class="nv">fib6</span> <span class="mi">100000</span><span class="p">))</span> <span class="c1">; little slow &amp; overflow error</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 9:</span>
  a sixth go at a fibonacci function, with delayed evaluation conses
</div>
<p>This works well. <code>(fib6 50000)</code> still turns out to run a bit slower than
our <code>(fib2 50000)</code>, so loops are still probably more efficient, but
streams are pretty interesting. They can can used to represent
infinite sequences. So here, above, <code>fibonacci-stream</code> (set by
<code>(fibonacci-populate 0 1)</code>) is actually an infinite stream of Fibonaccis
numbers, but lazily evaluated, so we just get the next one each time
we call <code>stream-pop</code> on our <code>fibonacci-stream</code> local variable. (What
happens is that <code>stream-pop</code> takes the <code>car</code> of <code>fibonacci-stream</code>,
evaluates and returns it, and then sets <code>fibonacci-stream</code> to be its <code>cdr</code>
(i.e., popping off and &ldquo;discarding&rdquo; the first element; which which
captured in our <code>fibs</code> collector.))</p>
<h3 id="cascades-of-fibonacci-numbers">Cascades of Fibonacci numbers</h3>
<p>Oh, incidentally and irrelevantly, if you inspect the contents of your
<code>fib6-50k</code>, it&rsquo;s very aesthetically pleasing, a cascade of numbers:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/Fibonacci_Flooding_Streams.png" alt="Figure 2: fibonacci numbers burst forth from their seeds and spill out into the buffer"/> <figcaption>
                
                <p>
                    <span class="figure-number">Figure 2: </span>fibonacci numbers burst forth from their seeds and spill out into the buffer
                    
                        
                        </p>
                
            </figcaption></figure>

<h2 id="excessive-lisp-nesting--overflow-error"><code>excessive-lisp-nesting</code>: <strong>(overflow-error)</strong></h2>
<p>I had hoped to get to the Y Combinator today (and think I might have
suggested a promise of that), for that&rsquo;s where things really get
interesting. And we need to get back to lambda calculus, of course.
But we may be near the limits of excessive lisp nesting ourselves
here.</p>
<p>However, the recursion discussion here has set the stage for the Y
Combinator, which we&rsquo;ve already talked a couple of times, especially
in connection to John McCarthy&rsquo;s claims about &ldquo;not really
understanding&rdquo; lambda calculus and the fact that these really centre
on his not seeing how one could get recursion without direct
Self-reference (and thus the need for <code>LABEL</code>) because of not knowing
about the Y Combinator.</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/phoe-ycombinator-codex-closeup-fibo.jpg" alt="Figure 3: a close-up of a section of Michał &ldquo;phoe&rdquo; Herda&rsquo;s hand-illuminated Y Combinator Codex showing part of a Fibonacci defun"/> <figcaption>
                
                <p>
                    <span class="figure-number">Figure 3: </span>a close-up of a section of <a href="https://phoe.github.io/">Michał &ldquo;phoe&rdquo; Herda&rsquo;s</a> hand-illuminated <a href="https://phoe.github.io/codex.html">Y Combinator Codex</a> showing part of a Fibonacci defun
                    
                        
                        </p>
                
            </figcaption></figure>

<p>And, the Y Combinator ties in with all sorts of other curious
things. Paradoxes, types, calligraphy.</p>
<p>[Thus, I ended up with an excursus on this excursus as the next
post. I&rsquo;ll put a link here to the proper third part when it&rsquo;s up.]</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>McCarthy, John. 1978a. History of Lisp. In <em>History of programming languages</em>, ed. Richard L. Wexelblat, 173–185. New York:
Association for Computing Machinery. <a href="https://dl.acm.org/doi/10.1145/800025.1198360">https://dl.acm.org/doi/10.1145/800025.1198360</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>&ldquo;t if and only if p and q or if r then s and
not not not not not not t&rdquo; <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>A number of Indian philosophers, at least as far back as
Virahāṅka (ca. AD 600–800), gave formulations for what is usually
called the Fibonacci sequence. See Singh, P. (1985). The so-called
fibonacci numbers in ancient and medieval India. <em>Historia Mathematica</em>,
12(3), 229–244. <a href="https://doi.org/10.1016/0315-0860(85)90021-7">https://doi.org/10.1016/0315-0860(85)90021-7</a> [<a href="https://sci-hub.se/https://www.sciencedirect.com/science/article/pii/0315086085900217?via%3Dihub">pdf</a>] <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>You might actually want to do something like <code>(setq my-fib1-100 (fib1 100 0 1))</code> to put the result into a variable, because
the echo area at the bottom of Emacs isn&rsquo;t big enough for all of the
numbers. (Oh, to <code>eval</code> things in an Emacs buffer, put your cursor/point
at the end of the expression and press <code>C-x C-e</code>. But don&rsquo;t do that for
this one. If you want Emacs to just stick the results in directly into
the buffer rather than echoing them, press <code>C-u C-x C-e</code>. But don&rsquo;t do
that here either, because Emacs will still end up printing an ellipsis
because it thinks it&rsquo;s too long.) And then press <code>C-h v</code> and type
<code>my-fib1-100</code> and enter to see the result. <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>See further <a href="https://en.wikipedia.org/wiki/Tail_call">here</a>, for instance, for more about tail calls and
tail call optimisation. <a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p>Steele, Guy L., Jr. 1977. Debunking the “expensive procedure
call” myth or, procedure call implementations considered harmful or,
LAMBDA: The Ultimate GOTO. <em>ACM &lsquo;77: Proceedings of the 1977 annual
conference</em>, 153–162. [<a href="https://dl.acm.org/doi/10.1145/800179.810196">https://dl.acm.org/doi/10.1145/800179.810196</a>] <a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7" role="doc-endnote">
<p>If you&rsquo;ve read any of Paul Graham&rsquo;s Common Lisp books
(e.g., <a href="https://en.wikipedia.org/wiki/On_Lisp"><em>On Lisp</em></a>) or any of the <a href="https://web.archive.org/web/20150426092105/http://www.ccs.neu.edu/home/matthias/BTLS/">Little Schemer</a> books (the latter with
<a href="https://wiki.c2.com/?DuaneBibby">Duane Bibby</a>&lsquo;s lovely artwork in them), you may be disposed towards
using recursion rather than loops.</p>
<p>


<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/little-scheming-phant.png" alt="Figure 4: close-up of Duane Bibby&rsquo;s cover illustration for &ldquo;The Little Schemer&rdquo;"/> <figcaption>
                
                <p>
                    <span class="figure-number">Figure 4: </span>close-up of Duane Bibby&rsquo;s cover illustration for &ldquo;The Little Schemer&rdquo;
                    
                        
                        </p>
                
            </figcaption></figure>
 <a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8" role="doc-endnote">
<p>One of my emacs packages, <a href="https://melpa.org/#/equake">Equake</a>, actually used to use
tco.el (until <a href="https://github.com/emacsomancer/equake/commit/0ab08019e8aee5f2e27db6ee90f6a64856f39ff9">commit #0ab0801</a> <span class="timestamp-wrapper"><span class="timestamp">[2020-08-24 Mon]</span></span>). <a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lambdacalculus" term="lambdacalculus" label="lambdacalculus" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/recursion" term="recursion" label="recursion" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Lambda Calculus and Lisp, part 1]]></title>
            <link href="https://babbagefiles.xyz/lambda-calculus-and-lisp-01/"/>
            <id>https://babbagefiles.xyz/lambda-calculus-and-lisp-01/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2025-02-18T03:05:00-06:00</published>
            <updated>2025-10-17T10:46:04-05:00</updated>
            
            
            <content type="html"><![CDATA[<p>The first of a series of envisioned blog posts on lambda calculus, and
Lisp. It&rsquo;s unclear exactly where to start: there is a whole heap of
interesting issues, both theoretical and in terms of concrete
implementations, which tangle and interconnect.</p>
<p>A particular application of lambda calculus is a very salient part of
my &ldquo;day job&rdquo; as a formal semanticist of natural language. And my
interests in Emacs and lisp(s) feel like they tie in here as
well—though that&rsquo;s a question in itself which is probably as good of a
starting point into this (planned) series of posts as any.</p>
<p>There is much to explore: origins of <a href="https://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)">John McCarthy</a>&lsquo;s Lisp and <a href="https://en.wikipedia.org/wiki/Alonzo_Church">Alonzo
Church</a>&lsquo;s lambda calculus; encodings of the simple made complex by
restriction to a limited set of tools; recursion, fixed points, and
paradoxes; infinities, philosophy, and engineering. But much of this
requires stage setting.</p>
<p>And finding an exact entry point is yet tricky. But perhaps we start
with <strong>λ</strong>: the divining rod, the wizard&rsquo;s crooked staff, as it is the key
component of much magic of a sort.</p>
<h2 id="lisp-lambda-the-ultimate">Lisp: LAMBDA the Ultimate?</h2>
<p>We&rsquo;ll start on the programming side, before turning to more
philosophical or mathematical abstractions, with <code>lambda</code>.</p>
<p>It is now not only Lisps which contain <code>lambda</code> as a keyword, many/most
programming languages have <code>lambda</code> as a keyword, usually for the
introduction of an anonymous function. That is, an unnamed function,
sometimes for one-off use.</p>
<p>But in McCarthy&rsquo;s original formulation of LISP in 1958, <code>LAMBDA</code>
was used as the basis for the implementation of functions generally:</p>
<blockquote>
<p>Let <code>f</code> be an expression that stands for a function of two integer variables.
It should make sense to write <code>f</code> (3, 4) and the value of this expression should be
determined. The expression <code>y^2 + x</code> does not meet this requirement; <code>y^2 + x(3, 4)</code>
is not a conventional notation, and if we attempted to define it we would be
uncertain whether its value would turn out to be 13 or 19. Church calls an
expression like <code>y2 + x</code>, a form. A form can be converted into a function if we
can determine the correspondence between the variables occurring in the form
and the ordered list of arguments of the desired function. This is accomplished
by Church’s λ-notation. [p.6]</p>
<p>&hellip;.</p>
<p><code>{λ[[x_1;…; x_n]; 𝓔]}∗</code> is <code>(LAMBDA, (x∗_1,…, x∗_n), 𝓔∗)</code>. [p.16]</p>
<p><strong>[McCarthy 1960:6, 16<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>]</strong></p>
</blockquote>
<p>As in lambda calculus, <code>LAMBDA</code> binds variables, and replaces any
occurrences of them in the scope of the operator with whatever it
receives as arguments.</p>
<p>So the expression:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">LAMBDA</span> <span class="p">(</span><span class="nv">x</span> <span class="nv">y</span> <span class="nv">z</span><span class="p">)</span> <span class="p">(</span><span class="nf">+</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">y</span> <span class="nv">x</span><span class="p">)</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">z</span> <span class="nv">x</span><span class="p">)))</span>
</code></pre></div><p>if given the arguments <code>5</code>, <code>2</code>, <code>3</code>, would replace <code>x</code>'s with <code>5</code>; <code>y</code>'s with <code>2</code>,
and <code>z</code>'s with <code>3</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nf">+</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">2</span> <span class="mi">5</span><span class="p">)</span> <span class="p">(</span><span class="nf">*</span> <span class="mi">3</span> <span class="mi">5</span><span class="p">))</span>  <span class="c1">;; = (+ 10 15) = 25</span>
</code></pre></div><h3 id="the-illusion-of-a-blue-suffused-platonic-universe-of-car-s">The illusion of a blue-suffused platonic universe of <code>car</code>'s</h3>
<p>The origin of <code>lambda</code> keywords in LISP, and the origin of LISP&rsquo;s <code>LAMBDA</code>
in lambda calculus has suggested the idea that LISP was something like
an implementation of lambda calculus as a programming language, and
the certain mysticism<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> attaching to both suggests perhaps a tighter
surface association than there is direct evidence for.</p>



<figure>
    
        
            <img src="https://imgs.xkcd.com/comics/lisp.jpg" alt="Figure 1: xkcd 224 [see https://www.explainxkcd.com/wiki/index.php/224:_Lisp]"/> <figcaption>
                
                <p>
                    <span class="figure-number">Figure 1: </span>xkcd 224 [see <a href="https://www.explainxkcd.com/wiki/index.php/224:_Lisp">https://www.explainxkcd.com/wiki/index.php/224:_Lisp</a>]
                    
                        
                        </p>
                
            </figcaption></figure>

<p>This is the topic of a 2019 blog post based on his talk for the Heart of Clojure
conference in Daniel Szmulewicz expands on the theme &ldquo;Lisp is <strong>not</strong> a
realization of the Lambda Calculus&rdquo;.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> One of the points
Szmulewicz draws attention to is McCarthy&rsquo;s own words:</p>
<blockquote>
<p>…one of the myths concerning LISP that people think up or invent for
themselves becomes apparent, and that is that LISP is somehow a
realization of the lambda calculus, or that was the intention. The
truth is that I didn&rsquo;t understand the lambda calculus, really.</p>
<p><strong>[McCarthy 1978b:190<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>]</strong></p>
</blockquote>
<p>In the paper version of the talk, McCarthy makes a similar point, but
it&rsquo;s worth looking at it in the larger context:</p>
<blockquote>
<p>…how do you talk about the sum of the derivatives, and in programming
it, there were clearly two kinds of programs that could be written.</p>
<p>One is where you have a sum of a fixed number of terms, like just two,
where you regard a sum as a binary operation. And then you could write
down the formula easy enough. But the other was where you have a sum
of an indefinite number of terms, and you&rsquo;d really like to make that
work too. To make that work, what you want to be able to talk about is
doing the same operation on all the elements of a lit. You want to be
able to get a new list whose elements are obtained from the old list
by just taking each element and doing a certain operation to it.</p>
<p>In order to describe that, one has to have a notation for
functions. So one could write this function called <code>mapcar</code>. This says,
&ldquo;Apply the function <code>f</code> to all the elements of the list.&rdquo; If the list is
null then you&rsquo;re going to get a <code>NIL</code> here. Otherwise you are going to
apply the function to the first element of the list and put that onto
a front of a list which is obtained by doing the same operation again
to the rest of the list. So that&rsquo;s <code>mapcar</code>. It wasn&rsquo;t called <code>mapcar</code>
then. It was called <code>maplist</code>, but <code>maplist</code> is something different, which
I will describe in just a moment.</p>
<p>That was fine for that recursive definition of applying a function to
everything on the list. No new ideas were required. But then, how do
you write these functions?</p>
<p>And so, the way in which to do that was to borrow from Church&rsquo;s Lambda
Calculus, to borrow the lambda definition. Now, having borrowed this
notation, one the myths concerning LISP that people think up or invent
for themselves becomes apparent, and that is that LISP is somehow a
realization of the lambda calculus, or that was the intention. The
truth is that I didn&rsquo;t understand the lambda calculus, really. In
particular, I didn&rsquo;t understand that you really could do conditional
expressions in recursion in some sense in the pure lambda
calculus. So, it wasn&rsquo;t an attempt to make the lambda calculus
practical, although if someone had started out with that intention, he
might have ended up with something like LISP.</p>
<p><strong>[McCarthy 1978a:189–190<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>]</strong></p>
</blockquote>
<h3 id="the-discovery-of-lisp">The Discovery of LISP</h3>
<p>Two bits from the end of this I want to highlight. The first, well,
it&rsquo;ll come up in future posts, and probably later in this post itself,
and it has to do with recursion:</p>
<p><strong>&ldquo;I didn&rsquo;t understand that you really could do conditional expressions in recursion in some sense in the pure lambda
calculus&rdquo;</strong></p>
<p>And the second is that McCarthy hedges his &ldquo;LISP as a realisation of
the lambda calculus is myth&rdquo; stance slightly:</p>
<p><strong>&ldquo;So, it wasn&rsquo;t an attempt to make the lambda calculus practical, although if someone had started out with that intention, he
might have ended up with something like LISP.&quot;</strong></p>
<p>This does fit rather well with (and perhaps suggested) the framing
that <a href="https://en.wikipedia.org/wiki/Paul_Graham_(programmer)">Paul Graham</a> does of McCarthy as the &ldquo;discoverer&rdquo; of Lisp — like
Euclid of geometry — rather than its inventor in his &ldquo;The Roots of
Lisp&rdquo; paper:</p>
<blockquote>
<p>In 1960, John McCarthy published a remarkable paper in which he <strong>did
for programming something like what Euclid did for geometry</strong>. He
showed how, given a handful of simple operators and a notation for
functions, you can build a whole programming language. He called this
language Lisp, for “List Processing,” because one of his key ideas was
to use a simple data structure called a list for both code and data.</p>
<p>It’s worth understanding what McCarthy <strong>discovered</strong>, not just as a
landmark in the history of computers, but as a model for what
programming is tending to become in our own time.
….
In this article I’m going to try to explain in the simplest possible
terms what McCarthy <strong>discovered</strong>. The point is not just to learn about
an interesting theoretical result someone figured out forty years ago,
but to show where languages are heading. The unusual thing about
Lisp—in fact, the defining quality of Lisp—is that it can be written
in itself.</p>
<p><strong>[Graham 2002:1<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> (emphasis mine)]</strong></p>
</blockquote>
<p>Graham&rsquo;s paper itself, as well as some of Graham&rsquo;s other
publications/postings (e.g., talking about Lisp as a sort of &ldquo;secret
weapon&rdquo; in comparison to &ldquo;blub languages&rdquo;)<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> is perhaps another
contributor to the mystique of Lisp. (With the flip side of &ldquo;Lisp as
the Cleveriest Hacker&rsquo;s Secret Weapon&rdquo; enchanted coin being &ldquo;the Curse
of Lisp&rdquo;<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>.)</p>
<p>There are other components of the history of LISP, which are
suggestive of discovery rather than invention. McCarthy&rsquo;s initial aim
for LISP was more akin that of the <a href="https://en.wikipedia.org/wiki/Turing_machine">Turing Machine</a>: as a formal abstraction
describing a mathematical model whose components were simple and few
but yet was capable of performing any (and all) arbitrary
computational operation:</p>
<blockquote>
<p>One mathematical consideration that influenced LISP was to express
programs as applicative expressions built up from variables and
constants using functions. I considered it important to make these
expressions obey the usual mathematical laws allowing replacement of
expressions by expressions giving the same value. The motive was to
allow proofs of properties of programs using ordinary mathematical
methods. This is only possible to the extent that side effects can be
avoided. Unfortunately, side effects are often a great convenience
when computational efficiency is important, and &ldquo;functions&rdquo; with side
effects are present in LISP. However, the so-called pure LISP is free
of side effects, and Cartwright (1976) and Cartwright and McCarthy
(1978) show how to represent pure LISP programs by sentences and
schemata in first-order logic and prove their properties. This is an
additional vindication of the striving for mathematical neatness,
because it is now easier to prove that pure LISP programs meet their
specifications than it is for any other programming language in
extensive use. (Fans of other programming languages are challenged to
write a program to concatenate lists and prove that the operation is
associative.)</p>
<p>Another way to show that LISP was neater than Turing machines was to
write a universal LISP function and show that is briefer and more
comprehensible than the description of a universal Turing
machine. This was the LISP function <code>eval[e,a]</code>, which computers the
value of a LISP expression <code>e</code>, the second argument <code>a</code> being a list of
assignments of values to variables. (<code>a</code> is needed to make the recursion
work.) Writing <code>eval</code> required inventing a notation representing LISP
functions as LISP data, and such a notation was devised for the
purposes of the paper with no thought that it would be used to express
LISP programs in practice. Logical completeness required that the
notation used to express functions used as functional arguments be
extended to provide for recursive functions, and the <code>LABEL</code> notation
was invented by Nathaniel Rochester for that purpose. D.M.R. Park
pointed out that <code>LABEL</code> was logically unnecessary since the result
could be achieved using only <code>LAMBDA</code> — by a construction analogous to
Church&rsquo;s <em>Y</em>-operator, albeit in a more complicated way.</p>
<p>S.R. Russell noticed that <code>eval</code> could serve as an interpreter for LISP,
promptly hand coded it, and we now had a programming language with an
interpreter.</p>
<p><strong>[McCarthy 1978:179<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>]</strong></p>
</blockquote>
<p>(There&rsquo;s a lot going on in this passage, and its context.</p>
<p>Notions of reasons to avoid side-effects (an ideal of &ldquo;functional
programming&rdquo;, emphasised in <a href="https://en.wikipedia.org/wiki/Haskell">Haskell</a> and <a href="https://en.wikipedia.org/wiki/Clojure">Clojure</a> (another lisp)): thus
the &ldquo;functional&rdquo; mode of lambda calculus rather than the &ldquo;everything
is state&rdquo; mode of Turing machines and their infinitely long memory tapes.</p>
<p>Recursion again (which we&rsquo;ll get to, repeatedly).</p>
<p>And Alonzo Church, the originator (discoverer?)<a href="#are-we-in-platonic-heaven-yet">^{see &ldquo;X&rdquo; below}</a> of lambda calculus,
who we&rsquo;ll talk more about soon, and who was also Alan Turing&rsquo;s PhD
supervisor at Princeton.</p>
<p>But, first: let&rsquo;s turn back to the topic of the concrete instantiation
of Lisp on physical hardware by <a href="https://en.wikipedia.org/wiki/Steve_Russell_(computer_scientist)">Stephen Russell</a>.)</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/lisp-byte_magazine-1979.jpg" alt="Figure 2: cover of Byte Magazine August 1979 [full mag here]"/> <figcaption>
                
                <p>
                    <span class="figure-number">Figure 2: </span>cover of Byte Magazine August 1979 [full mag <a href="https://dn790008.ca.archive.org/0/items/byte-magazine-1979-08-rescan/1979_08_BYTE_04-08_LISP.pdf">here</a>]
                    
                        
                        </p>
                
            </figcaption></figure>

<p>Elsewhere, McCarthy makes clear that he hadn&rsquo;t thought at that point
of LISP being instantiatable but as a theoretical exploration of
computing at an abstract level. But, instead, the theoretical
description translated fairly easily and directly to a runnable
program, a LISP interpreter:</p>
<blockquote>
<p>This <code>EVAL</code> was written and published in the paper and Steve Russell
said, &ldquo;look, why don&rsquo;t I program this <code>EVAL</code>&rdquo; … and I said to him, &ldquo;ho,
ho, you&rsquo;re confusing theory with practice, this <code>EVAL</code> is intended for
reading, not for computing&rdquo;. But he went ahead and did it. That is, he
compiled the <code>EVAL</code> in my paper into IBM 704 machine code, fixing bugs,
and then advertised this as a Lisp interpreter, which it certainly
was. So at that point Lisp had essentially the form that it has today…</p>
<p><strong>[McCarthy 1974a:307<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup>]</strong></p>
</blockquote>
<h3 id="no-compute-dot-only-read-dot-hell-eval-that-dot">&ldquo;No compute. Only read.&rdquo; &ldquo;Hell, EVAL that.&rdquo;</h3>
<p>This is the <code>EVAL</code> code &ldquo;in the paper&rdquo; referred to above:</p>
<div class="highlight"><pre class="chroma"><code class="language-prolog" data-lang="prolog"><span class="s">eval</span><span class="p">[</span><span class="s">e</span><span class="p">;</span> <span class="s">a</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
   <span class="s">atom</span> <span class="p">[</span><span class="s">e</span><span class="p">]</span> <span class="s">→</span> <span class="s">assoc</span> <span class="p">[</span><span class="s">e</span><span class="p">;</span> <span class="s">a</span><span class="p">];</span>

   <span class="s">atom</span> <span class="p">[</span><span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">]]</span> <span class="s">→</span> <span class="p">[</span>
        <span class="s">eq</span> <span class="p">[</span><span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="nv">QUOTE</span><span class="p">]</span> <span class="s">→</span> <span class="s">cadr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span>
        <span class="s">eq</span> <span class="p">[</span><span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="nv">ATOM</span><span class="p">]</span> <span class="s">→</span> <span class="s">atom</span> <span class="p">[</span><span class="s">eval</span> <span class="p">[</span><span class="s">cadr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">]];</span>
        <span class="s">eq</span> <span class="p">[</span><span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="nv">EQ</span><span class="p">]</span> <span class="s">→</span> <span class="p">[</span><span class="s">eval</span> <span class="p">[</span><span class="s">cadr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">]</span> <span class="o">=</span> <span class="s">eval</span> <span class="p">[</span><span class="s">caddr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">]];</span>
        <span class="s">eq</span> <span class="p">[</span><span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="nv">COND</span><span class="p">]</span> <span class="s">→</span> <span class="s">evcon</span> <span class="p">[</span><span class="s">cdr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">];</span>
        <span class="s">eq</span> <span class="p">[</span><span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="nv">CAR</span><span class="p">]</span> <span class="s">→</span> <span class="s">car</span> <span class="p">[</span><span class="s">eval</span> <span class="p">[</span><span class="s">cadr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">]];</span>
        <span class="s">eq</span> <span class="p">[</span><span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="nv">CDR</span><span class="p">]</span> <span class="s">→</span> <span class="s">cdr</span> <span class="p">[</span><span class="s">eval</span> <span class="p">[</span><span class="s">cadr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">]];</span>
        <span class="s">eq</span> <span class="p">[</span><span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="nv">CONS</span><span class="p">]</span> <span class="s">→</span> <span class="s">cons</span> <span class="p">[</span><span class="s">eval</span> <span class="p">[</span><span class="s">cadr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">];</span> <span class="s">eval</span> <span class="p">[</span><span class="s">caddr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">]];</span>
        <span class="nv">T</span> <span class="s">→</span> <span class="s">eval</span> <span class="p">[</span><span class="s">cons</span> <span class="p">[</span><span class="s">assoc</span> <span class="p">[</span><span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">];</span> <span class="s">evlis</span> <span class="p">[</span><span class="s">cdr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">]];</span> <span class="s">a</span><span class="p">]];</span>

   <span class="s">eq</span> <span class="p">[</span><span class="s">caar</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="nv">LABEL</span><span class="p">]</span> <span class="s">→</span> <span class="s">eval</span> <span class="p">[</span><span class="s">cons</span> <span class="p">[</span><span class="s">caddar</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">cdr</span> <span class="p">[</span><span class="s">e</span><span class="p">]];</span>
                                <span class="s">cons</span> <span class="p">[</span><span class="s">list</span> <span class="p">[</span><span class="s">cadar</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">car</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">]]];</span>

   <span class="s">eq</span> <span class="p">[</span><span class="s">caar</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="nv">LAMBDA</span><span class="p">]</span> <span class="s">→</span> <span class="s">eval</span> <span class="p">[</span><span class="s">caddar</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span>
                                 <span class="s">append</span> <span class="p">[</span><span class="s">pair</span> <span class="p">[</span><span class="s">cadar</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span>
                                               <span class="s">evlis</span> <span class="p">[</span><span class="s">cdr</span> <span class="p">[</span><span class="s">e</span><span class="p">];</span> <span class="s">a</span><span class="p">];</span> <span class="s">a</span><span class="p">]]]</span>

<span class="s">evcon</span><span class="p">[</span><span class="s">c</span><span class="p">;</span> <span class="s">a</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="s">eval</span><span class="p">[</span><span class="s">caar</span><span class="p">[</span><span class="s">c</span><span class="p">];</span> <span class="s">a</span><span class="p">]</span> <span class="s">→</span> <span class="s">eval</span><span class="p">[</span><span class="s">cadar</span><span class="p">[</span><span class="s">c</span><span class="p">];</span> <span class="s">a</span><span class="p">];</span> <span class="nv">T</span> <span class="s">→</span> <span class="s">evcon</span><span class="p">[</span><span class="s">cdr</span><span class="p">[</span><span class="s">c</span><span class="p">];</span> <span class="s">a</span><span class="p">]]</span>

<span class="s">evlis</span><span class="p">[</span><span class="s">m</span><span class="p">;</span> <span class="s">a</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="s">null</span><span class="p">[</span><span class="s">m</span><span class="p">]</span> <span class="s">→</span> <span class="nv">NIL</span><span class="p">;</span> <span class="nv">T</span> <span class="s">→</span> <span class="s">cons</span><span class="p">[</span><span class="s">eval</span><span class="p">[</span><span class="s">car</span><span class="p">[</span><span class="s">m</span><span class="p">];</span> <span class="s">a</span><span class="p">];</span> <span class="s">evlis</span><span class="p">[</span><span class="s">cdr</span><span class="p">[</span><span class="s">m</span><span class="p">];</span> <span class="s">a</span><span class="p">]]]</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 1:</span>
  McCarthy 1960, p.17 (see <a href="#fn:1" class="footnote-ref" role="doc-noteref">fn. [1]</a>) [Nb.: not actually `prolog' but highlights better as]
</div>
<p>Translated into slightly more familiar Lisp style (with added
named-function-making <code>label</code>'s), it is:<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup></p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">label</span> <span class="nf">eval</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">e</span> <span class="nv">a</span><span class="p">)</span>
         <span class="p">(</span><span class="nb">cond</span>
           <span class="p">((</span><span class="kt">atom</span> <span class="nv">e</span><span class="p">)</span> <span class="p">(</span><span class="nf">assoc</span> <span class="nv">e</span> <span class="nv">a</span><span class="p">))</span>
           <span class="p">((</span><span class="kt">atom</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">e</span><span class="p">))</span>
            <span class="p">(</span><span class="nb">cond</span>
              <span class="p">((</span><span class="nf">eq</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">e</span><span class="p">)</span> <span class="ss">&#39;quote</span><span class="p">)</span> <span class="p">(</span><span class="nf">cadr</span> <span class="nv">e</span><span class="p">))</span>
              <span class="p">((</span><span class="nf">eq</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">e</span><span class="p">)</span> <span class="ss">&#39;atom</span><span class="p">)</span>  <span class="p">(</span><span class="kt">atom</span>  <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nf">cadr</span> <span class="nv">e</span><span class="p">)</span> <span class="nv">a</span><span class="p">)))</span>
              <span class="p">((</span><span class="nf">eq</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">e</span><span class="p">)</span> <span class="ss">&#39;eq</span><span class="p">)</span>    <span class="p">(</span><span class="nf">eq</span>    <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nf">cadr</span> <span class="nv">e</span><span class="p">)</span> <span class="nv">a</span><span class="p">)</span>
                                          <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nf">caddr</span> <span class="nv">e</span><span class="p">)</span> <span class="nv">a</span><span class="p">)))</span>
              <span class="p">((</span><span class="nf">eq</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">e</span><span class="p">)</span> <span class="ss">&#39;car</span><span class="p">)</span>   <span class="p">(</span><span class="nf">car</span>   <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nf">cadr</span> <span class="nv">e</span><span class="p">)</span> <span class="nv">a</span><span class="p">)))</span>
              <span class="p">((</span><span class="nf">eq</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">e</span><span class="p">)</span> <span class="ss">&#39;cons</span><span class="p">)</span>  <span class="p">(</span><span class="nc">cons</span>  <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nf">cadr</span> <span class="nv">e</span><span class="p">)</span> <span class="nv">a</span><span class="p">)</span>
                                          <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nf">caddr</span> <span class="nv">e</span><span class="p">)</span> <span class="nv">a</span><span class="p">)))</span>
              <span class="p">((</span><span class="nf">eq</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">e</span><span class="p">)</span> <span class="ss">&#39;cond</span><span class="p">)</span>  <span class="p">(</span><span class="nv">evcon</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">e</span><span class="p">)</span> <span class="nv">a</span><span class="p">))</span>
              <span class="p">(</span><span class="ss">&#39;t</span>                  <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nc">cons</span> <span class="p">(</span><span class="nf">assoc</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">e</span><span class="p">)</span> <span class="nv">a</span><span class="p">)</span>
                                               <span class="p">(</span><span class="nf">cdr</span> <span class="nv">e</span><span class="p">))</span>
                                         <span class="nv">a</span><span class="p">))))</span>
           <span class="p">((</span><span class="nf">eq</span> <span class="p">(</span><span class="nf">caar</span> <span class="nv">e</span><span class="p">)</span> <span class="ss">&#39;label</span><span class="p">)</span>   <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nc">cons</span> <span class="p">(</span><span class="nf">caddar</span> <span class="nv">e</span><span class="p">)</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">e</span><span class="p">))</span>
                                         <span class="p">(</span><span class="nc">cons</span> <span class="p">(</span><span class="nc">list</span> <span class="p">(</span><span class="nf">cadar</span> <span class="nv">e</span><span class="p">)</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">e</span><span class="p">))</span> <span class="nv">a</span><span class="p">)))</span>
           <span class="p">((</span><span class="nf">eq</span> <span class="p">(</span><span class="nf">caar</span> <span class="nv">e</span><span class="p">)</span> <span class="ss">&#39;lambda</span><span class="p">)</span>  <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nf">caddar</span> <span class="nv">e</span><span class="p">)</span>
                                         <span class="p">(</span><span class="nf">append</span> <span class="p">(</span><span class="nv">pair</span> <span class="p">(</span><span class="nf">cadar</span> <span class="nv">e</span><span class="p">)</span>
                                                       <span class="p">(</span><span class="nv">evlis</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">e</span><span class="p">)</span> <span class="nv">a</span><span class="p">))</span>
                                                 <span class="nv">a</span><span class="p">))))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nv">evcon</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">c</span> <span class="nv">a</span><span class="p">)</span>
         <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nf">eval</span> <span class="p">(</span><span class="nf">caar</span> <span class="nv">c</span><span class="p">)</span> <span class="nv">a</span><span class="p">)</span>
                <span class="p">(</span><span class="nf">eval</span> <span class="p">(</span><span class="nf">cadar</span> <span class="nv">c</span><span class="p">)</span> <span class="nv">a</span><span class="p">))</span>
               <span class="p">(</span><span class="ss">&#39;t</span>
                <span class="p">(</span><span class="nv">evcon</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">c</span><span class="p">)</span> <span class="nv">a</span><span class="p">)))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nv">evlis</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">m</span> <span class="nv">a</span><span class="p">)</span>
         <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nc">null</span> <span class="nv">m</span><span class="p">)</span> <span class="o">&#39;</span><span class="p">())</span>
               <span class="p">(</span><span class="ss">&#39;t</span> <span class="p">(</span><span class="nc">cons</span> <span class="p">(</span><span class="nf">eval</span>  <span class="p">(</span><span class="nf">car</span> <span class="nv">m</span><span class="p">)</span> <span class="nv">a</span><span class="p">)</span>
                         <span class="p">(</span><span class="nv">evlis</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">m</span><span class="p">)</span> <span class="nv">a</span><span class="p">))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 2:</span>
  EVAL in more recognisable lisp form
</div>
<p>The above, given the few additional definitions for convenience
immediately following, is a full LISP interpreter.<sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup></p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">label</span> <span class="nc">null</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span><span class="p">)</span>
         <span class="p">(</span><span class="nf">eq</span> <span class="nv">x</span> <span class="o">&#39;</span><span class="p">())))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nb">and</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span> <span class="nv">y</span><span class="p">)</span>
         <span class="p">(</span><span class="nb">cond</span> <span class="p">(</span><span class="nv">x</span> <span class="p">(</span><span class="nb">cond</span> <span class="p">(</span><span class="nv">y</span> <span class="ss">&#39;t</span><span class="p">)</span> <span class="p">(</span><span class="ss">&#39;t</span> <span class="o">&#39;</span><span class="p">())))</span>
               <span class="p">(</span><span class="ss">&#39;t</span> <span class="o">&#39;</span><span class="p">()))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nf">not</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span><span class="p">)</span>
         <span class="p">(</span><span class="nb">cond</span> <span class="p">(</span><span class="nv">x</span> <span class="o">&#39;</span><span class="p">())</span>
               <span class="p">(</span><span class="ss">&#39;t</span> <span class="ss">&#39;t</span><span class="p">))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nf">append</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span> <span class="nv">y</span><span class="p">)</span>
         <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nc">null</span> <span class="nv">x</span><span class="p">)</span> <span class="nv">y</span><span class="p">)</span>
               <span class="p">(</span><span class="ss">&#39;t</span> <span class="p">(</span><span class="nc">cons</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">x</span><span class="p">)</span>
                         <span class="p">(</span><span class="nf">append</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">x</span><span class="p">)</span> <span class="nv">y</span><span class="p">))))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nv">pair</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span> <span class="nv">y</span><span class="p">)</span>
         <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nb">and</span> <span class="p">(</span><span class="nc">null</span> <span class="nv">x</span><span class="p">)</span> <span class="p">(</span><span class="nc">null</span> <span class="nv">y</span><span class="p">))</span> <span class="o">&#39;</span><span class="p">())</span>
               <span class="p">((</span><span class="nb">and</span> <span class="p">(</span><span class="nf">not</span> <span class="p">(</span><span class="kt">atom</span> <span class="nv">x</span><span class="p">))</span> <span class="p">(</span><span class="nf">not</span> <span class="p">(</span><span class="kt">atom</span> <span class="nv">y</span><span class="p">)))</span>
                <span class="p">(</span><span class="nc">cons</span> <span class="p">(</span><span class="nc">list</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">x</span><span class="p">)</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">y</span><span class="p">))</span>
                      <span class="p">(</span><span class="nv">pair</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">x</span><span class="p">)</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">y</span><span class="p">)))))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nf">assoc</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span> <span class="nv">y</span><span class="p">)</span>
         <span class="p">(</span><span class="nb">cond</span> <span class="p">((</span><span class="nf">eq</span> <span class="p">(</span><span class="nf">caar</span> <span class="nv">y</span><span class="p">)</span> <span class="nv">x</span><span class="p">)</span> <span class="p">(</span><span class="nf">cadar</span> <span class="nv">y</span><span class="p">))</span>
               <span class="p">(</span><span class="ss">&#39;t</span> <span class="p">(</span><span class="nf">assoc</span> <span class="nv">x</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">y</span><span class="p">))))))</span>
</code></pre></div><div class="src-block-caption">
  <span class="src-block-number">Code Snippet 3:</span>
  additional convenience functions for EVAL
</div>
<p>The brevity of the code combined with the details of the story of the
implementation: seemingly the theoretical code works without
translation, suggesting a sort of natural discovery. But, the nature
of even the theoretical, pre-implementation code did not exist in some
Platonic heaven of mathematics, as can be seen by the nature of some
of the operations, particularly <code>car</code> and <code>cdr</code> (often in modern Lisps,
especially Scheme and Scheme-influenced Lisps, rendered instead more
transparently as <code>first</code> and <code>rest</code>.)</p>
<h3 id="the-non-platonic-mechanics-of-1950s-ibm-mainframes">The Non-Platonic mechanics of 1950s IBM mainframes</h3>
<p>LISP was designed with the <a href="https://en.wikipedia.org/wiki/IBM_704">IBM 704</a>-style architecture in mind, and <code>car</code>
and <code>cdr</code> make some reference to particular details of the hardware,
where the IBM 704 had &ldquo;address&rdquo; and &ldquo;decrement&rdquo; fields in memory index
registers (≈locations in physical RAM), and <code>car</code> referenced the
&ldquo;address&rdquo; field of the register (so CAR = &ldquo;<strong>C</strong>-ontents of the <strong>A</strong>-ddress
part of the <strong>R</strong>-egister&rdquo;) and <code>cdr</code> the &ldquo;decrement&rdquo; field of the register
(CDR = &ldquo;<strong>C</strong>-ontents of the <strong>D</strong>-ecrement part of the <strong>R</strong>-egister&rdquo;).<sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup></p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/my-other-car-is-a-cdr.jpg" alt="Figure 3: my other car is a cdr"/> <figcaption>
                
                <p>
                    <span class="figure-number">Figure 3: </span>my other car is a cdr
                    
                        
                        </p>
                
            </figcaption></figure>

<p>Lists in Lisp are singly-linked lists, with each node in the list
having a &ldquo;value&rdquo; field and a &ldquo;next&rdquo; field, which points to the next
node; these &ldquo;value&rdquo; and &ldquo;next&rdquo; fields correspond to the <code>car</code> and
<code>cdr</code> operations. So, if we have a list like <code>(a b c)</code>, the first node in the list has a
&ldquo;value&rdquo; field of <code>a</code> and a &ldquo;next&rdquo; field pointing at another node, with
this second node actually itself being — not <code>b</code> — but rather the list
<code>(b c)</code>. A proper list in Lisp is <code>nil</code>-terminated: that is, the last item
in the list is actually <code>nil</code> (which is the empty list <code>'()</code>).</p>
<p>The operation <code>cons</code> above (for CONstructor) is a pair-forming operation
(where the pairs correspond to the &ldquo;value&rdquo; and &ldquo;next&rdquo; fields),
returning what are variously called (in different lisps) &ldquo;conses&rdquo; or
&ldquo;pairs&rdquo;. Not all conses/pairs are lists (at least in most Lisps),
since a proper <code>list</code> in Lisp is <code>nil</code>-terminated.</p>
<p>The operation <code>(cons 'a 'b)</code> will return a <code>cons</code>'ed object with
essentially a single node, where the &ldquo;value&rdquo; field will be <code>a</code> and the
&ldquo;next&rdquo; field will be <code>b</code>. Lisps will usually print such non-list conses
(sometimes called &ldquo;improper lists&rdquo;) as &ldquo;dotted lists&rdquo;, i.e., <code>(cons 'a 'b)</code> will print out as <code>(a . b)</code>.</p>
<p>The list <code>(a b c)</code> is actually the result of doing <code>(cons a (cons b (cons c nil)))</code> and would be printed in dotted-pair notation as <code>(a . (b . (c . nil)))</code> [which is equivalent to <code>(a . (b . (c . ())))</code>, since <code>nil</code> is
the empty list].</p>
<p>Unsurprisingly, a lot of early/traditional Lisp programming involves
manipulations of lists. But all modern Lisps implement other types of
data structures as well, including vectors/arrays, hash tables,
objects, and so on.</p>
<p>But, on the main topic of <code>(eq 'lisp 'lambdacalculus)</code>, others have also
pointed out the concrete hardware-connections of LISP from early days,
telling against the &ldquo;Lisp-as-pure-formal-<del>invention</del>-discovery&rdquo; or
&ldquo;Lisp as (semi-)direct implementation of lambda calculus&rdquo; notions:</p>
<blockquote>
<p>Lisp was intended to be implemented on a computer from day 0. For
their IBM 704. Actually Lisp is based on earlier programming
experience. From 56 onwards John McCarthy implemented Lisp ideas in
Fortran &amp; FLPL. Then 58 the implementation of Lisp was started. 59 a
first runnable version was there. 1960 there was the Lisp 1
implementation. The widely known paper on the Lisp implementation and
recursive function theory was published in 1960. But his original
prime motivation was not to have a notation for recursive function
theory, it was to have a list processing programming language for
their IBM 704 for AI research.</p>
<p>Lisp as designed by McCarthy was very different from lambda calculus.</p>
<p>[going on to point to the <a href="https://doi.org/10.1145/800055.802047">Stoyan (1984) Early LISP history
(1956 - 1959) paper</a>.]</p>
<p><strong>— <a href="https://moth.social/@lispm">lispm</a>&lsquo;s comment on <a href="https://www.reddit.com/r/lisp/comments/e467tk/is_it_more_correct_to_say_that_lisp_is_based_on/f99keh3/">r/lisp</a> thread on this topic</strong></p>
</blockquote>
<p>There&rsquo;s obviously much more to explore for the early history of Lisp
and the nature of its connections to lambda calculus, but this much at
least should give a general sense of the distinctions/divergences
between Lisp and lambda calculus, while not ignoring important
interconnections between them.</p>
<h3 id="reaching-the--of-the-line">Reaching the <code>'()</code> of the line</h3>
<p>And so while it&rsquo;s tempting to delve off into other interesting
features (homoiconicity!) of LISP/Lisps and their history and dialects
(the Common Lisp of Endor; Schemes, Rackets, Chickens and Guile (oh
my!), Clojure, Fennel and others), I&rsquo;d wanted to get to lambda
calculus proper much earlier in this piece already, and so we&rsquo;ll set
our (lispy) parens down for a moment, and trade in our <code>LAMBDA</code> for a <code>λ</code>.</p>
<h2 id="cattle-prodding-functions-the-lambda-calculus">Cattle-prodding functions: the lambda calculus</h2>
<p>The aforementioned Alonzo Church<sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup>, a Princeton
mathematician who supervised 31 doctoral students during his career,
influencing others important researchers (including <a href="https://en.wikipedia.org/wiki/Haskell_Curry">Haskell Curry</a> [for
whom the programming language Haskell is named; as well as the
operating of <a href="https://en.wikipedia.org/wiki/Currying">currying</a>]), developed (the) lambda calculus as part of
his research into the foundations of mathematics.</p>
<p>Lambda calculus is <a href="https://en.wikipedia.org/wiki/Turing_completeness">Turing complete</a>, thus equivalent in computation
power to a Turing machine and a universal model of computation.</p>
<p>There are very few bits of machinery in basic untyped lambda
calculus. (A reason for which it seemed to be attractive to McCarthy
as a touchstone.)</p>
<p>Lambda calculus has variables and lambdas; function application; a
reduction operation (which may follow function application); and a
convenience variable-renaming operation.</p>
<p>More specifically, we have:</p>
<ol>
<li><strong>variables</strong>, like <code>x</code>, which are characters or strings representing &ldquo;a
parameter&rdquo;.</li>
<li><strong>lambda abstraction</strong>: essentially just the definition of a function,
specifying its input (by a bound variable, say, <code>λx</code>) and returning
an output (say, <code>M</code>). E.g., the expression <code>λx.M</code> will take an input,
and replace any and all instances of <code>x</code> in the body <code>M</code><sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup> with
whatever the input was. (The <code>.</code> separates the lambda and
specification of bound variable (here <code>x</code>) (&ldquo;input taker&rdquo;) from the
body (the &ldquo;output&rdquo;).)</li>
<li><strong>function application</strong>: a representation like <code>(M N)</code>, the function <code>M</code>
applies to <code>N</code>, where both <code>M</code> and <code>N</code> are some sort of lambda terms.</li>
<li><strong>β-reduction</strong> (beta reduction): bound variables inside body of the
expression are replaced by inputs &ldquo;taken&rdquo; by the lambda
expressions. The basic form: <code>((λx.M) N)</code> → <code>(M[x := N])</code>. (That is, an
expression <code>(λx.M)</code> combining with an expression <code>N</code> returns <code>(M)</code> where
where all instances of <code>x</code> inside of <code>M</code> are replaced with <code>N</code>.)</li>
</ol>
<p>(Setting aside rule 4 for the moment.)</p>
<p>For example, we might have an expression:</p>
<p><code>λf.λx.(f x)</code></p>
<p>This would be an expression which combines, one at a time, with two
inputs, the lambdas operating from left to right, and then applying
the <code>f</code> input to the <code>x</code> input.</p>
<p>To see this in full working order, we need to specific one of the two
remaining operations (both reduction operations):</p>
<p>So, turning to rule 4 for β-reduction, taking our expression from
above and providing it with inputs, and walking through the (two)
application+β-reduction steps one at a time:</p>
<p><code>((λf.λx.(f x)) b a)</code> =</p>
<p><code>((λx.(b x)) a)</code> =</p>
<p><code>(b a)</code></p>
<p>That is, first, the leftmost lambda, <code>λf,</code> &ldquo;takes&rdquo; the leftmost argument <code>b</code>
(in &ldquo;taking&rdquo; an argument, it &ldquo;discharges&rdquo; and disappears) and the
(single) bound instance of the variable <code>f</code> in the body is replaced by
<code>b</code>. Then, the same thing happens with the remaining lambda, <code>λx</code>, and the
remaining argument, <code>a</code>. The result is a function where <code>b</code> applies to
<code>a</code>. (Though since in this case there are no more lambdas, nothing more
happens.)</p>
<p>Since <code>(b a)</code> looks somewhat unexciting/opaque, we can imagine a sort of
hybrid proper untyped lambda calculus/Lisp hybrid language — let&rsquo;s
call this toy language of ours ΛΙΣΠ — and illustrate what things might
look like there (assuming here that numbers are numbers and <code>#'*</code> is a lispy
prefix multiplication function):</p>
<p><code>((λf.λx.λy.(f x y)) #'* 6 7)</code> =</p>
<p><code>((λx.λy.(* x y)) 6 7)</code> =</p>
<p><code>((λy.(* 6 y)) 7)</code> =</p>
<p><code>(* 6 7)</code> =</p>
<p><code>42</code></p>
<p>This isn&rsquo;t how mathematics works in classical untyped lambda calculus
— because we only have the 4 rules/entities enumerated above [plus a
variable-clash reduction operation called α-reduction]<sup id="fnref:15"><a href="#fn:15" class="footnote-ref" role="doc-noteref">15</a></sup> and nothing
else<sup id="fnref:16"><a href="#fn:16" class="footnote-ref" role="doc-noteref">16</a></sup>: no integers, no stipulated mathematical operations, no <code>car</code> or
<code>cdr</code> or <code>cons</code> or <code>eq</code> or anything — we can do all of these things in
lambda calculus with the tools we have, and we&rsquo;ll explore that in
another post, but for now I just wanted to show you the toy ΛΙΣΠ
language snippet as I find something that feels a bit more familiar
and concrete can be helpful for understanding the notional unpinnings
of what&rsquo;s going on in lambda calculus.</p>
<h3 id="ok-so-there-s-no-integers-or-cars-but-what-s-all-this-about-cattle-prods">Ok, so there&rsquo;s no integers or cars, but what&rsquo;s all this about cattle prods?</h3>
<p>Well, why is <code>λ</code> / <code>LAMBDA</code> the &ldquo;function&rdquo;-making operator? Maybe just
eeny-meeny-miney-moe amongst Greek letters, but at least at one point
Church explained that [A. Church, 7 July 1964. Unpublished letter to
Harald Dickson, §2] that it came from the notation “x̂” used for
class-abstraction by Whitehead and Russell in their <em><a href="https://en.wikipedia.org/wiki/Principia_Mathematica">Principia
Mathematica</a></em> (which we&rsquo;re refer to later on), by first modifying “x̂” to
“^⁣x” to (and then for better visibility to “∧x”) to distinguish
function-abstraction from class-abstraction, and then changing “∧”
(which is similar to uppercase Greek &ldquo;Λ&rdquo;) to (lowercase Greek) “λ” for
ease of printing (and presumably to avoid confusion with other
mathematical uses of &ldquo;∧&rdquo;, e.g., logical AND).<sup id="fnref:17"><a href="#fn:17" class="footnote-ref" role="doc-noteref">17</a></sup>
[So maybe &ldquo;x̂&rdquo; ⇒ &ldquo;^⁣x&rdquo; → &ldquo;∧x&rdquo; → &ldquo;λx&rdquo;.]</p>
<p>The Greek lambda (uppercase Λ, lowercase λ) as an ortheme itself
derives ultimately from a Semitic abjad, specifically from the
Phoenician <em>lāmd</em> <strong>𐤋</strong>, which (like most letters began as a pictogram of
sorts) is considered to originate from something like an ox-goad,
i.e., a cattle prod, or else a shepherd&rsquo;s crook, i.e., a pastoral
staff. (The reconstructed Proto-Semitic word <em>*lamed-</em> means a
&ldquo;goad&rdquo;.)<sup id="fnref:18"><a href="#fn:18" class="footnote-ref" role="doc-noteref">18</a></sup></p>
<h3 id="are-we-in-platonic-heaven-yet">Are We in Platonic Heaven Yet?</h3>
<p>Lambda calculus, not being tied to any particular hardware and being a
true formula abstraction, feels like something that might have a
better claim to being something like a property of the universe that
one might discover (I admit I find it hard not to feel something of
the sort — but then I&rsquo;ve used lambda calculus for work on natural
language within a framework that wants to assign some sort of reality
to our formalisations, so it&rsquo;s hard not to be pulled in this
direction), however, Church says (of his own formalism):</p>
<blockquote>
<p>We do not attach any character of uniqueness or absolute truth to any
particular system of logic. The entities of formal logic are
abstractions, invented because of their use in describing and
systematizing facts of experience or observation, and their
properties, determined in rough outline by this intended use, depend
for their exact character on the arbitrary choice of the inventor.</p>
<p><strong>[Alonzo Church 1932:348<sup id="fnref:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup>]</strong></p>
</blockquote>
<p>(This seems part of Church&rsquo;s constructivist philosophy, in
common with Frege.)<sup id="fnref:20"><a href="#fn:20" class="footnote-ref" role="doc-noteref">20</a></sup></p>
<h2 id="what-s-next-λy-dot--equal-cdr-l--y">What&rsquo;s <code>next</code>? : <code>λy.(equal? (cdr L) y)</code></h2>
<p>We&rsquo;ve pulled at a lot of disparate threads here, trying to explore
the nature of the connections between Lisp and (the) lambda
calculus. Neither the idea that Lisp is a direct instantiation of
lambda calculus nor the idea that McCarthy was largely ignorant of
properties of lambda calculus are quite right. But that there are an
interesting interplay of connections.</p>
<p>But. This is really to set the stage for me to talk about things I&rsquo;m
interested in which draw on different aspects of Lisp(s) and lambda
calculus and formal or applied applications to do with one or the
other.</p>
<p>I was going to talk about <a href="https://en.wikipedia.org/wiki/Montague_grammar">Montague Grammar</a>, because it&rsquo;s fascinating
(and it&rsquo;s my day job), for which lambda calculus is a crucial
component.<sup id="fnref:21"><a href="#fn:21" class="footnote-ref" role="doc-noteref">21</a></sup> But we&rsquo;re at length now, so it should be
another day.</p>
<p>What I do want to look at next is a combination of Lisp and lambda
calculus, in various ways, starting with attempts to implement aspects
of lambda calculus in Emacs Lisp, and the challenges therein.</p>
<p>[fingers crossed that <code>(next 'blog)</code> does not eval to <code>undefined</code>.]</p>
<p>(Update: <code>(eval (next 'blog))</code>: <a href="https://babbagefiles.xyz/lambda-calculus-and-lisp-02-recursion/">Part 2: Recursion Excursion</a>.)</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>McCarthy, John. 1960. Recursive functions of symbolic
expressions and their computation by machine. <em>Communications of the
Association for Computing Machinery</em> 3(4):184-195. [<a href="https://web.archive.org/web/20131006003734/http://www-formal.stanford.edu/jmc/recursive.html">available at the
Wayback Archive</a>; page nos. refer to those of the PDF at the link,
a reformatted version in LaTeX, not those of the original publication.] <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>On Lisp&rsquo;s mystical aura, see <a href="https://twobithistory.org/2018/10/14/lisp.html">How Lisp Became God&rsquo;s
Own Programming Language</a> (and the associated <a href="https://news.ycombinator.com/item?id=23163596">Hacker News discussion</a>). <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="https://danielsz.github.io/blog/2019-08-05T21_14.html">&ldquo;Lisp ≠ Lambda Calculus&rdquo; | Daniel Szmulewicz: Perfumed
nightmare (A blog for the somnambulisp)</a> <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>McCarthy, John. 1978b. Transcript of presentation. History of Lisp. In <em>History of
programming languages</em>, ed. Richard L. Wexelblat, 185–191. New York:
Association for Computing Machinery. <a href="https://dl.acm.org/doi/10.1145/800025.1198361">https://dl.acm.org/doi/10.1145/800025.1198361</a> <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>McCarthy, John. 1978a. History of Lisp. In <em>History of
programming languages</em>, ed. Richard L. Wexelblat, 173–185. New York:
Association for Computing Machinery. <a href="https://dl.acm.org/doi/10.1145/800025.1198360">https://dl.acm.org/doi/10.1145/800025.1198360</a> <a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p>Graham, Paul. 2002. The Roots of Lisp. Ms.,
<a href="https://paulgraham.com/rootsoflisp.html">https://paulgraham.com/rootsoflisp.html</a>. [<a href="https://web.archive.org/web/20240222160957/http://slackwise.net/files/docs/The%20Roots%20of%20Lisp.pdf">PDF version</a>] <a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7" role="doc-endnote">
<p>See, e.g., Graham&rsquo;s 2001 <a href="https://www.paulgraham.com/avg.html">&ldquo;Beating the Averages&rdquo;</a>. <a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8" role="doc-endnote">
<p>On the &ldquo;Curse of Lisp&rdquo;, see for instance Rudolf
Winestock&rsquo;s <a href="https://winestockwebdesign.com/Essays/Lisp_Curse.html">&ldquo;The Lisp Curse&rdquo;</a>; but also <a href="https://old.reddit.com/r/Clojure/comments/qq0xh9/what_is_the_curse_of_lisp_in_simple_terms/">this 2021 discussion on
r/clojure</a>, where it&rsquo;s mentioned that Winestock said in 2017 in <a href="https://news.ycombinator.com/item?id=14480157">a
comment</a> on (one of the innumerable) Hacker News threads on the topic
&ldquo;I wrote that essay five years ago. Nowadays, just use Clojure or
Racket and ignore what I&rsquo;ve written.&rdquo; <a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9" role="doc-endnote">
<p>J. McCarthy: LISP History. Talk at MIT, Spring or Summer
1974 (Written from tape 7/10/75, unpublished: cited in Stoyan,
Herbert. 1984. Early LISP history (1956 - 1959). <em>LFP &lsquo;84: Proceedings
of the 1984 ACM Symposium on LISP and functional programming</em>,
eds. Robert S. Boyer, Edward S. Schneider, Guy L. Steele, 299–310. New
York: Association for Computing
Machinery. [<a href="https://doi.org/10.1145/800055.802047">https://doi.org/10.1145/800055.802047</a>]) (Quote from p.307.) <a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10" role="doc-endnote">
<p>Descriptively:</p>
<ol>
<li><code>(quote x)</code>  returns (the symbol) <code>x</code> (rather than the value of <code>x</code>).</li>
<li><code>(atom x)</code>  returns <code>t</code> if the value of <code>x</code> is an atom or else <code>'()</code>.</li>
<li><code>(eq x y)</code>  returns <code>t</code> if the values of <code>x</code> and <code>y</code> are the same atom, or both the empty list; otherwise returns <code>()</code>.</li>
<li><code>(car x)</code>  expects the value of <code>x</code> to be <code>cons</code> expression of some sort, like a list, and returns its first element.</li>
<li><code>(cdr x)</code>  expects the value of <code>x</code> to be <code>cons</code> expression of some sort, like a list, and returns everything after the first element.</li>
<li><code>(cons x y)</code>  returns an object composed of a link between <code>x</code> and <code>y</code>; this is how lists are formed, if the second argument of the innermost <code>cons</code> is <code>()</code>.</li>
<li><code>(cond (p₁ e₁)…(pₙ eₙ))</code>  evaluates the <code>p</code> expressions in order until one returns <code>t</code>, at which point it returns the co-indexed <code>e</code>.</li>
<li><code>((lambda (p₁…pₙ) e) a₁…aₙ)</code>  evaluates each <code>aᵢ</code> expression and replaces the occurrence of all occurrences of each <code>pᵢ</code> in <code>e</code> with the value of the corresponding <code>aᵢ</code> expression, and then evaluates <code>e</code>.</li>
<li><code>(label f (lambda (p₁…pₙ) e))</code>  makes all occurrences of the symbol <code>f</code> behave like the following <code>(lambda (p₁…pₙ) e)</code> expression, including inside of <code>e</code>.</li>
</ol>
 <a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></li>
<li id="fn:11" role="doc-endnote">
<p>With just tad bits more sugar to reduce <code>car</code> / <code>cdr</code>
recursion spam:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">label</span> <span class="nf">caar</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span><span class="p">)</span>
         <span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">x</span><span class="p">))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nf">cadr</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span><span class="p">)</span>
         <span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">x</span><span class="p">))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nf">caddr</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span><span class="p">)</span>
         <span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nf">cdr</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">x</span><span class="p">)))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nf">cadar</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span><span class="p">)</span>
         <span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nf">cdr</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">x</span><span class="p">)))))</span>

<span class="p">(</span><span class="nv">label</span> <span class="nf">caddar</span>
       <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">x</span><span class="p">)</span>
         <span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nf">cdr</span> <span class="p">(</span><span class="nf">cdr</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">x</span><span class="p">))))))</span>
</code></pre></div> <a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></li>
<li id="fn:12" role="doc-endnote">
<p>See McCarthy, John, Paul W. Abrahams, Daniel J. Edwards, Timothy</p>
<ol>
<li>Hart, and Michael I. Levin. 1962. /LISP 1.5 Programmer&rsquo;s</li>
</ol>
<p>Manual/. Cambridge, Mass.: M.I.T. Press [<a href="https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf">pdf</a>], at p.36f and also
McCarthy (1960:26–7), as well as the <a href="https://en.wikipedia.org/wiki/CAR_and_CDR">Wikipedia page on CAR and CDR</a>. <a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13" role="doc-endnote">
<p>Church is also well-known for his proof that the
Entscheidungsproblem is undecidable (Church&rsquo;s theorem), the
<a href="https://en.wikipedia.org/wiki/Church%E2%80%93Turing_thesis">Church-Turing thesis</a>, the <a href="https://en.wikipedia.org/wiki/Frege%E2%80%93Church_ontology">Frege-Church ontology</a>, amongst much else. I
don&rsquo;t know any particularly interesting details of his personal life
(he was no Richard Montague, about whom more later); he was apparently a
lifelong Presbyterian. <a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:14" role="doc-endnote">
<p>The <code>M</code> here can be standing in for something more complex,
e.g., the whole expression might really be <code>λx.(a (b d))</code>, where <code>M</code> here
is a place-holder for <code>(a (b d))</code>. <a href="#fnref:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:15" role="doc-endnote">
<p>The <strong>α-conversion</strong> (fifth) rule is: bound variables can be
substituted with different bound variables. So, <code>λx.M[x]</code> can be
α-converted to <code>λy.M[y]</code>. Bound variable names have no significance
except as place-holders, so these two expressions are equivalent. The
reason for doing this is to avoid name-clashes (see <a href="https://martinfowler.com/bliki/TwoHardThings.html">Two Hard Things</a>?)
when combining expressions.</p>
<p>E.g., if we wanted to combine <code>λxλy.(x y)</code>
with itself, e.g. <code>((λxλy.(x y)) (λxλy.(x y)))</code> (this is of the function
application form <code>(M N)</code>), we could do α-conversion so we don&rsquo;t end up
with accidental variable capture.</p>
<p>If we didn&rsquo;t this would happen:</p>
<p><code>((λxλy.(x y)) (λxλy.(x y)))</code> =</p>
<p><code>((λy.((λxλy.(x y)) y))</code> =</p>
<p><code>(λy.(λy.(y y))</code></p>
<p>Since variables are arbitrary names, whose point is distinctionness,
we need instead to α-convert on one of the expressions:</p>
<p><code>λxλy.(x y)</code> = (by α-conversion)</p>
<p><code>λaλb.(a b)</code></p>
<p>And then we can combine as we tried to before, and correctly arrive at:</p>
<p><code>((λxλy.(x y)) (λaλb.(a b)))</code> =</p>
<p><code>(λy.((λaλb.(a b)) y))</code> =</p>
<p><code>λy.(λb.(y b))</code></p>
<p>(We&rsquo;ll talk more about α-conversion and its implement in a later post.) <a href="#fnref:15" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:16" role="doc-endnote">
<p>Well, there&rsquo;s also (optional/debated) sixth rule:
<strong>η-conversion</strong>, which touches on some more philosophical concerns
(though ones which will eventually concern us too), to do with
<a href="https://en.wikipedia.org/wiki/Extensionality">extensionality</a> (vs. <a href="https://en.wikipedia.org/wiki/Intension">intensionality</a>) [essentionally <a href="https://en.wikipedia.org/wiki/Sense_and_reference">Frege&rsquo;s Sinn und
Bedeutung</a>].</p>
<p>η-conversion says that in a case where <code>(f x) = (g x)</code> for all possible
values of <code>x</code>, then <code>f</code> = <code>g</code>. It&rsquo;s not always something which is
implemented, and it raises questions around extensionality vs
intensionality vs hyperintensionality we&rsquo;ll probably touch on a later
point.</p>
<p>But, for a taste, by η-conversion (because it&rsquo;s about extensionality,
roughly what&rsquo;s true in the world rather than conceptually), the
following two expressions are identical (<code>G</code> has an extra component
involving an identity function):</p>
<p><code>F = λx.x</code></p>
<p><code>G = λx.(λy.y)x</code></p>
<p>Do they conceptuality (intensionally) count as the same function, <code>F</code>
and <code>G</code>? No, perhaps, but their extensions are the same (they&rsquo;re always
produce the same results), and so <code>F</code> counts as the α-reduction of <code>G</code>. <a href="#fnref:16" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:17" role="doc-endnote">
<p>On the Church λ story, see Cardone, Felice &amp; Hindley,</p>
<ol>
<li>Roger, 2006. History of Lambda-calculus and Combinatory Logic. In</li>
</ol>
<p>Gabbay and Woods (eds.), <em>Handbook of the History of Logic</em>,
vol. 5. Elsevier. [<a href="https://web.archive.org/web/20210506154120/http://www.users.waitrose.com/~hindley/SomePapers_PDFs/2006CarHin,HistlamRp.pdf">pdf</a>] On uses of ∧ as notation in formal theories,
see <a href="https://en.wikipedia.org/wiki/Wedge_(symbol)#Use">here</a>. <a href="#fnref:17" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:18" role="doc-endnote">
<p>See <a href="https://en.wikipedia.org/wiki/Lamedh">Wikipedia on Lamedh</a>. <a href="#fnref:18" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:19" role="doc-endnote">
<p>Church, Alonzo. 1932. A set of postulates for the
foundation of logic. <em>Annals of mathematics</em> 33(2):346-366. [<a href="https://web.archive.org/web/20240123184324/https://raw.githubusercontent.com/emintham/Papers/master/Church-%20A%20Set%20of%20Postulates%20for%20the%20Foundation%20of%20Logic.pdf">pdf</a>] <a href="#fnref:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:20" role="doc-endnote">
<p>Thanks to <a href="https://philpeople.org/profiles/e-j-slade">Dr E.J. Slade</a> for discussion on this point
(Church&rsquo;s constructivism, and the connection with Frege&rsquo;s positions). <a href="#fnref:20" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:21" role="doc-endnote">
<p>And one I&rsquo;ve worked on implementing <strong>in a Lisp</strong> at various
points, e.g., <a href="https://gitlab.com/emacsomancer/frege">Frege</a>, the beginnings of a Racket-based DSL for natural
language semantics. <a href="#fnref:21" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lambdacalculus" term="lambdacalculus" label="lambdacalculus" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[So you need kill (or do something else to) something in Linux]]></title>
            <link href="https://babbagefiles.xyz/you-need-to-kill-or-do-something-in-linux/"/>
            <id>https://babbagefiles.xyz/you-need-to-kill-or-do-something-in-linux/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2025-02-15T23:02:00-06:00</published>
            <updated>2025-02-16T13:40:25-06:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="or-what-if-you-can-t-just-click-the-bad-thing-with-the-skull-until-it-dies">(Or, what if you can&rsquo;t just click the bad thing with the skull until it dies?)</h2>
<p>Sometimes you need to kill something in Linux. Sometimes it makes
sense to use some sort interactive process monitor, like the process
table in Plasma&rsquo;s System Monitor, or <code>top</code> or <code>htop</code> or <code>bottom</code> or some
other sort of top.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> (Or, if you&rsquo;re in an X11 environment rather
than a Wayland one, you could use <code>xkill</code>.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>)</p>
<h3 id="killing-with-killall">killing with <code>killall</code></h3>
<p>You can often get by with <a href="https://en.wikipedia.org/wiki/Killall"><code>killall</code></a>, e.g., if you want to kill all
running Firefox applications:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">killall firefox
</code></pre></div><h3 id="killing-with-pkill">killing with <code>pkill</code></h3>
<p>Or you could use <a href="https://en.wikipedia.org/wiki/Pkill"><code>pkill</code></a> (which has a number of options) in much the
same way.</p>
<p>These sorts of approaches don&rsquo;t always work. Sometimes (this is true
in Guix<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> a lot) processes are named in ways that <code>killall</code> or <code>pkill</code> don&rsquo;t
match.</p>
<h3 id="listing-processes-and-process-ids-with-ps">listing processes and process ids with <code>ps</code></h3>
<p>You can list running processes in Linux with <code>ps -ef</code>, and this will
show you all running processes. It&rsquo;s too much, obviously, there are a
lot of things going on in your system. So, you can filter it by
&ldquo;piping&rdquo; the output of <code>ps -ef</code> through <a href="https://en.wikipedia.org/wiki/Grep"><code>grep</code></a>, e.g., <code>ps -ef | grep -i emacs</code>, which might show you something like:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">emacsomancer    <span class="m">15188</span> <span class="m">26384</span>  <span class="m">0</span> 21:05 ?        00:00:02 /gnu/store/5vkx1cf1d2k9dj974vgd77yx0fdis284-emacs-pdf-tools-1.1.0/bin/epdfinfo
emacsomancer    <span class="m">23294</span> <span class="m">26384</span>  <span class="m">0</span> 21:33 ?        00:00:00 /home/emacsomancer/.guix-home/profile/bin/emacs --quick --batch --load /home/emacsomancer/.emacs.d/eln-cache/30.0.93-e51375a4/el-job-child-8a892b0e-c7b5e3df.eln --eval <span class="o">(</span>el-job-child--work <span class="c1">#&#39;org-node-parser--collect-dangerously nil)</span>
emacsomancer    <span class="m">23550</span>  <span class="m">2250</span>  <span class="m">0</span> 21:34 pts/4    00:00:00 grep --color<span class="o">=</span>auto -i emacs
emacsomancer    <span class="m">26384</span>     <span class="m">1</span>  <span class="m">5</span> 19:47 ?        00:05:42 /home/emacsomancer/.guix-home/profile/bin/emacs --daemon --debug-init
emacsomancer    <span class="m">26513</span> <span class="m">26384</span>  <span class="m">0</span> 19:47 ?        00:00:03 /home/emacsomancer/.guix-home/profile/bin/emacs --quick --batch --load /home/emacsomancer/.emacs.d/eln-cache/30.0.93-e51375a4/el-job-child-8a892b0e-c7b5e3df.eln --eval <span class="o">(</span>el-job-child--work <span class="c1">#&#39;org-node-parser--collect-dangerously t)</span>
emacsomancer    <span class="m">26514</span> <span class="m">26384</span>  <span class="m">0</span> 19:47 ?        00:00:02 /home/emacsomancer/.guix-home/profile/bin/emacs --quick --batch --load /home/emacsomancer/.emacs.d/eln-cache/30.0.93-e51375a4/el-job-child-8a892b0e-c7b5e3df.eln --eval <span class="o">(</span>el-job-child--work <span class="c1">#&#39;org-node-parser--collect-dangerously t)</span>
emacsomancer    <span class="m">26515</span> <span class="m">26384</span>  <span class="m">0</span> 19:47 ?        00:00:02 /home/emacsomancer/.guix-home/profile/bin/emacs --quick --batch --load /home/emacsomancer/.emacs.d/eln-cache/30.0.93-e51375a4/el-job-child-8a892b0e-c7b5e3df.eln --eval <span class="o">(</span>el-job-child--work <span class="c1">#&#39;org-node-parser--collect-dangerously t)</span>
emacsomancer    <span class="m">26516</span> <span class="m">26384</span>  <span class="m">0</span> 19:47 ?        00:00:01 /home/emacsomancer/.guix-home/profile/bin/emacs --quick --batch --load /home/emacsomancer/.emacs.d/eln-cache/30.0.93-e51375a4/el-job-child-8a892b0e-c7b5e3df.eln --eval <span class="o">(</span>el-job-child--work <span class="c1">#&#39;org-node-parser--collect-dangerously t)</span>
emacsomancer    <span class="m">26517</span> <span class="m">26384</span>  <span class="m">0</span> 19:47 ?        00:00:02 /home/emacsomancer/.guix-home/profile/bin/emacs --quick --batch --load /home/emacsomancer/.emacs.d/eln-cache/30.0.93-e51375a4/el-job-child-8a892b0e-c7b5e3df.eln --eval <span class="o">(</span>el-job-child--work <span class="c1">#&#39;org-node-parser--collect-dangerously t)</span>
emacsomancer    <span class="m">26518</span> <span class="m">26384</span>  <span class="m">0</span> 19:47 ?        00:00:02 /home/emacsomancer/.guix-home/profile/bin/emacs --quick --batch --load /home/emacsomancer/.emacs.d/eln-cache/30.0.93-e51375a4/el-job-child-8a892b0e-c7b5e3df.eln --eval <span class="o">(</span>el-job-child--work <span class="c1">#&#39;org-node-parser--collect-dangerously t)</span>
emacsomancer    <span class="m">26519</span> <span class="m">26384</span>  <span class="m">0</span> 19:47 ?        00:00:01 /home/emacsomancer/.guix-home/profile/bin/emacs --quick --batch --load /home/emacsomancer/.emacs.d/eln-cache/30.0.93-e51375a4/el-job-child-8a892b0e-c7b5e3df.eln --eval <span class="o">(</span>el-job-child--work <span class="c1">#&#39;org-node-parser--collect-dangerously t)</span>
emacsomancer    <span class="m">26750</span> <span class="m">23962</span>  <span class="m">0</span> 19:48 tty8     00:00:00 /home/emacsomancer/.guix-home/profile/bin/emacsclient -c
emacsomancer    <span class="m">26761</span> <span class="m">23962</span>  <span class="m">0</span> 19:48 tty8     00:00:00 /home/emacsomancer/.guix-home/profile/bin/emacsclient -c -e <span class="o">(</span>mu4e<span class="o">)</span>
</code></pre></div><p>Since you&rsquo;ve piped <code>ps</code> through <code>grep</code>, you&rsquo;re missing the explanatory
column headers row (because that row doesn&rsquo;t contain the string &ldquo;emacs&rdquo;
and so has been filtered out by <code>grep</code>), which are:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">UID        PID  PPID  C STIME TTY          TIME CMD
</code></pre></div><p>So, then, picking one of the entries above, let&rsquo;s line them up:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">UID             PID    PPID  C STIME TTY      TIME     CMD
emacsomancer    <span class="m">26384</span>     <span class="m">1</span>  <span class="m">5</span> 19:47 ?        00:05:42 /home/emacsomancer/.guix-home/profile/bin/emacs --daemon --debug-init
</code></pre></div><p>The UID is just the name of &ldquo;user&rdquo; who executed the process (probably
you). The CMD is what the process actually is, which is important. The
other important piece here is the PID, which is the Process ID or
<a href="https://en.wikipedia.org/wiki/Process_identifier">Processor identifier</a>, for with this you can have your system kill (or
do other things) to a very specific process (usually an application,
or a process it&rsquo;s spun off). The other fields aren&rsquo;t relevant for us
here.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<h3 id="kill--or-how-to-kill-things-nicely"><code>kill</code> (or, how to kill things nicely)</h3>
<p>With the PID, you can do various things with a process, such as
<a href="https://en.wikipedia.org/wiki/Kill_(command)"><code>kill</code></a>. This could be a simple <code>kill 26384</code> to kill the <code>emacs --daemon</code>
process.</p>
<p>Sometimes, processes don&rsquo;t want to die and a simple <code>kill</code> won&rsquo;t work.
<code>kill</code> is equivalent to <code>kill -TERM</code> or <code>kill -15</code>, which essentially nicely
ask the process to stop. This is often good, because if the process
has some sort of &ldquo;exit things&rdquo; it does, like saving backups of open
files, it will do those things first before stopping.</p>
<p>But sometimes the reason you&rsquo;re in the command line in the first place
is because a process is hung, and so the simple <code>kill</code> won&rsquo;t do any
good.</p>
<h3 id="kill-9--or-what-if-politely-killing-doesn-t-work"><code>kill -9</code> (or, what if politely killing doesn&rsquo;t work)</h3>
<p>In that case you can do <code>kill -9</code>, which is &ldquo;the unsafe way of brutally
murdering a process. It&rsquo;s equivalent to pulling the power cord, and
may cause data corruption.&quot;<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> This often not what you want
to do, but is useful for when you need to force something to stop
immediately or when it&rsquo;s not responding to polite <code>kill</code>'s. (Though
<code>kill -9</code> / <code>SIGKILL</code> won&rsquo;t kill <a href="https://en.wikipedia.org/wiki/Zombie_process">zombies</a>, because they&rsquo;re dead already and
are just waiting for their parent processes to reap them.)</p>
<h3 id="advanced-pkill--or-what-if-there-are-too-many-things-to-kill-one-by-one">Advanced <code>pkill</code> (or, what if there are too many things to kill one by one?)</h3>
<p>If you recall the <code>ps -ef | grep -i emacs</code> output from above, there are
often a bunch of associated processes. I&rsquo;ve often ended up having to
try to run <code>kill</code> (or <code>kill -9</code>) on a bunch of PIDs one by one until
finally the application shut down (cases where <code>killall</code> didn&rsquo;t work for
one reason or other).</p>
<p>We can instead use our process lister (<code>ps -ef</code>) with piping to
<code>pkill</code>. E.g., like this in the case of killing all processes named
&ldquo;emacs&rdquo;:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">ps -ef <span class="p">|</span> pkill -f emacs
</code></pre></div><p>Careful, because, especially if the &ldquo;name&rdquo; is short, it might be a
substring of other running processes. (I.e., you probably <strong>don&rsquo;t</strong> want to
try something like <code>ps -ef | pkill -f e</code>. It would kill <code>emacs --daemon</code>,
true, but it would also kill any other process with an <code>e</code> in it.)</p>
<p>You can check first by piping <code>ps -ef</code> through <a href="https://en.wikipedia.org/wiki/Pgrep"><code>pgrep</code></a>:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">ps -ef <span class="p">|</span> pgrep -l emacs <span class="c1"># show the list of all the processes (and their names) to be killed first</span>
</code></pre></div><p>to see a list of the names of the processes that would be killed (for
our <code>emacs</code> example).</p>
<p>Or, if you need a bit more information, do instead:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">ps -ef <span class="p">|</span> pgrep -a emacs <span class="c1"># show the list of all the processes (and their full command line, not just name) to be killed first</span>
</code></pre></div><p>to see the full command line (including but not limited to the
process&rsquo;s name).</p>
<p>And, just like <code>kill</code> above, <code>pkill</code> is by default the &ldquo;polite kill&rdquo;. If
you&rsquo;re dealing with stubborn processes, you can add a <code>-9</code> to order
<code>SIGKILL</code>, e.g.:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">ps -ef <span class="p">|</span> pkill -9 -f emacs
</code></pre></div><p>(If you had a completely hung Emacs, say.)</p>
<h2 id="what-if-you-need-to-do-something-other-than-killing-a-bunch-of-things">What if you need to do something other than killing a bunch of things?</h2>
<p>This is all well and good if all you need to do is <code>kill</code>, but sometimes
there are other things to do.</p>
<p>For instance, I finally got <a href="https://en.wikipedia.org/wiki/Mullvad">Mullvad VPN</a>&lsquo;s own <a href="https://github.com/mullvad/mullvadvpn-app">graphical client</a> to work
on Guix,<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> but for some reason the graphical interface <a href="https://en.wikipedia.org/wiki/Split_tunneling">split
tunnelling</a> doesn&rsquo;t work for me on Guix (usually the Mullvad interface
would allow you open a specific application outside of the VPN tunnel
(i.e. on your regular connection)). Fortunately, Mullvad also has
a command-line interface and one can add currently running
applications to be excluded from the VPN tunnel (this is also useful
even if your Mullvad GUI split-tunnelling is working for not having to
shut down and re-open applications if you want them excluded from your
VPN tunnel). But it does it by PID, e.g.:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">mullvad split-tunnel add <span class="m">26384</span>
</code></pre></div><p>(if we were excluding the <code>emacs --daemon</code> PID from the above example
from the VPN tunnel, say.)</p>
<p>One of the obvious things one might want to exclude from a VPN tunnel
is a particular browser (e.g., there&rsquo;s a site that doesn&rsquo;t like the
VPN, so you open up a second browser outside of the VPN to use to
access that site).</p>
<p>But browsers often involve a number of processes, and I found myself
having to manually run the <code>mullvad split-tunnel add</code> command on ten
different PIDs, and check mullvad.net each time to see if that was the
one I needed or not.</p>
<p>This is both time-consuming and frustrating and, just like <code>kill</code>'ing, we
can instead do it at scale.</p>
<p>Now, the <code>mullvad split-tunnel</code> command itself doesn&rsquo;t have a built-in
facility to either add processes by name or more than one in a single
shot, but we can write a loop over a list of PIDs and have the loop
execute <code>mullvad split tunnel add ...</code> for each one.</p>
<p>In <a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)">Bash</a> (&ldquo;the Bourne Again SHell&rdquo;), if you wanted to exclude Firefox
from the Mullvad VPN tunnel, you could do:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="k">for</span> pid in <span class="k">$(</span>pgrep -u <span class="k">$(</span>id -u<span class="k">)</span> -f <span class="s2">&#34;firefox&#34;</span><span class="k">)</span><span class="p">;</span> <span class="k">do</span> mullvad split-tunnel add <span class="s2">&#34;</span><span class="nv">$pid</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">done</span>
</code></pre></div><p>Just like for <code>pkill</code>, you can double-check before what would be added
to the tunnel exclusion by doing one of:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">ps -ef <span class="p">|</span> pgrep -l firefox <span class="c1"># show the list of all the processes (and their names) to be killed first</span>
ps -ef <span class="p">|</span> pgrep -a firefox <span class="c1"># show the list of all the processes (and their full command line, not just name) to be killed first</span>
</code></pre></div><p>If you&rsquo;re using <a href="https://fishshell.com">Fish shell</a> (&ldquo;FInally, a command line SHell for the
90s&rdquo;), instead of the Bash line above, you would use:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="k">for</span> pid in <span class="k">$(</span>pgrep -u <span class="k">$(</span>id -u<span class="k">)</span> -f <span class="s2">&#34;firefox&#34;</span><span class="k">)</span><span class="p">;</span> mullvad split-tunnel add <span class="s2">&#34;</span><span class="nv">$pid</span><span class="s2">&#34;</span><span class="p">;</span> end
</code></pre></div><h3 id="but-what-if-you-don-t-want-to-remember-arcane-incantations">But what if you don&rsquo;t want to remember arcane incantations?</h3>
<p>I don&rsquo;t, or, well, won&rsquo;t. I can probably find it in my shell history
once I&rsquo;ve run it on a machine, but we can do better than that.</p>
<p>We can instead add a function to the shell.</p>
<p>For Bash, you would add to your <code>~/.bash_profile</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="k">function</span> mullvad-split-tunnel-by-name<span class="o">()</span> <span class="o">{</span>
  <span class="k">for</span> pid in <span class="k">$(</span>pgrep -u <span class="s2">&#34;</span><span class="k">$(</span>id -u<span class="k">)</span><span class="s2">&#34;</span> -f <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span><span class="k">)</span>
  <span class="k">do</span>
      mullvad split-tunnel add <span class="s2">&#34;</span><span class="nv">$pid</span><span class="s2">&#34;</span>
      <span class="nb">printf</span> <span class="s2">&#34;added to split tunnel: %s\n&#34;</span> <span class="s2">&#34;</span><span class="k">$(</span>ps -p <span class="s2">&#34;</span><span class="nv">$pid</span><span class="s2">&#34;</span> -o <span class="nv">command</span><span class="o">=</span> <span class="p">|</span> awk <span class="s1">&#39;{print $1}&#39;</span><span class="k">)</span><span class="s2">&#34;</span>
  <span class="k">done</span>
<span class="o">}</span>
</code></pre></div><p>(You don&rsquo;t need the <code>printf</code> bit if you don&rsquo;t want, but this way you&rsquo;ll see a
proper list of the names of all of the excluded processes, which the
<code>mullvad split-tunnel add</code> command doesn&rsquo;t do by itself.)</p>
<p>Then (once your <code>.bash_profile</code> is <code>source</code>'ed)<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>, you could just
enter the following in the terminal to have all <code>firefox</code> processes
excluded from the Mullvad VPN tunnel:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">mullvad-split-tunnel-by-name firefox
</code></pre></div><p>(You can choose a shorter name for your function than
<code>mullvad-split-tunnel-by-name</code> of course, but for me such is fine with
TAB completion.)</p>
<p>And similarly for other things you wanted to exclude from the VPN
tunnel (e.g., <code>mullvad-split-tunnel-by-name chromium</code>).</p>
<p>For Fish, you would add a new file to your <code>~/.config/fish/functions/</code>
directory (say
<code>~/.config/fish/functions/mullvad-functions.fish</code>) with the
following content:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="k">function</span> mullvad-split-tunnel-by-name --description <span class="s1">&#39;adds all matching instances to mullvads split tunnel&#39;</span>
  <span class="k">for</span> pid in <span class="k">$(</span>pgrep -u <span class="k">$(</span>id -u<span class="k">)</span> -f <span class="nv">$argv</span><span class="k">)</span>
      mullvad split-tunnel add <span class="s2">&#34;</span><span class="nv">$pid</span><span class="s2">&#34;</span>
      <span class="nb">printf</span> <span class="s2">&#34;added to split tunnel: %s\n&#34;</span> <span class="s2">&#34;</span><span class="k">$(</span>ps -p <span class="nv">$pid</span> -o <span class="nv">command</span><span class="o">=</span> <span class="p">|</span> awk <span class="s1">&#39;{print $1}&#39;</span><span class="k">)</span><span class="s2">&#34;</span>
  end
end
</code></pre></div><p>(Again, the <code>printf</code> line is optional. And again you should <code>source</code> it or
restart your shell.)</p>
<h4 id="a-couple-of-other-useful-mullvad-split-tunnel-functions">A couple of other useful mullvad split-tunnel functions</h4>
<p>These can be defined in similar fashion to the above
<code>mullvad-split-tunnel-by-name</code>: one for removing things from the VPN
excluded list, and for one for showing what&rsquo;s currently on the list by
name. (since <code>mullvad split-tunnel list</code> just spits back a list of raw
PIDs, which isn&rsquo;t very helpful for identifying what&rsquo;s actually being
split tunnelled&hellip;.)</p>
<!--list-separator-->
<ul>
<li>
<p>removing processes from exclude list</p>
<p>Like <code>mullvad-split-tunnel-by-name</code>, but for removing excluded processes
(i.e., re-including them in VPN tunnel):</p>
<ul>
<li>
<p>In Bash:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="k">function</span> mullvad-remove-process-by-name<span class="o">()</span> <span class="o">{</span>
   <span class="k">for</span> pid in <span class="k">$(</span>pgrep -u <span class="s2">&#34;</span><span class="k">$(</span>id -u<span class="k">)</span><span class="s2">&#34;</span> -f <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span><span class="k">)</span>
   <span class="k">do</span>
       mullvad split-tunnel delete <span class="s2">&#34;</span><span class="nv">$pid</span><span class="s2">&#34;</span>
       <span class="nb">printf</span> <span class="s2">&#34;removed from split tunnel: %s\n&#34;</span> <span class="s2">&#34;</span><span class="k">$(</span>ps -p <span class="s2">&#34;</span><span class="nv">$pid</span><span class="s2">&#34;</span> -o <span class="nv">command</span><span class="o">=</span> <span class="p">|</span> awk <span class="s1">&#39;{print $1}&#39;</span><span class="k">)</span><span class="s2">&#34;</span>
   <span class="k">done</span>
<span class="o">}</span>
</code></pre></div></li>
<li>
<p>In Fish:</p>
</li>
</ul>
 <!--listend-->
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="k">function</span> mullvad-remove-process-by-name --description <span class="s1">&#39;remove all matching instances to mullvads split tunnel&#39;</span>
  <span class="k">for</span> pid in <span class="k">$(</span>pgrep -u <span class="k">$(</span>id -u<span class="k">)</span> -f <span class="nv">$argv</span><span class="k">)</span>
      mullvad split-tunnel delete <span class="s2">&#34;</span><span class="nv">$pid</span><span class="s2">&#34;</span>
      <span class="nb">printf</span> <span class="s2">&#34;removed from split tunnel: %s\n&#34;</span> <span class="s2">&#34;</span><span class="k">$(</span>ps -p <span class="nv">$pid</span> -o <span class="nv">command</span><span class="o">=</span> <span class="p">|</span> awk <span class="s1">&#39;{print $1}&#39;</span><span class="k">)</span><span class="s2">&#34;</span>
  end
end
</code></pre></div></li>
</ul>
<!--list-separator-->
<ul>
<li>
<p>listing processes in exclude list by name rather than PID</p>
<p>Checking on what&rsquo;s currently split out from the VPN tunnel: this
provide a listing of processes currently excluded from the VPN tunnel
by name (in a couple of options):</p>
<ul>
<li>In Bash:</li>
</ul>
 <!--listend-->
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="c1"># list full command line info associated with each PID in list</span>
<span class="k">function</span> mullvad-list-excluded-processes-by-long-name<span class="o">()</span> <span class="o">{</span>
    <span class="nv">mullvad_vpn_exclude_array</span><span class="o">=(</span> <span class="k">$(</span>mullvad split-tunnel list<span class="k">)</span> <span class="o">)</span>
    <span class="nv">mvre</span><span class="o">=</span><span class="s1">&#39;^[0-9]+$&#39;</span>
    <span class="nb">printf</span> <span class="s2">&#34;Processes excluded from VPN tunnel:\n\n&#34;</span>
    <span class="k">for</span> key in <span class="s2">&#34;</span><span class="si">${</span><span class="p">!mullvad_vpn_exclude_array[@]</span><span class="si">}</span><span class="s2">&#34;</span>
    <span class="k">do</span>
        <span class="k">if</span>  <span class="o">[[</span> <span class="si">${</span><span class="nv">mullvad_vpn_exclude_array</span><span class="p">[</span><span class="nv">$key</span><span class="p">]</span><span class="si">}</span> <span class="o">=</span>~ <span class="nv">$mvre</span> <span class="o">]]</span>
        <span class="k">then</span>
            <span class="nb">printf</span> <span class="s2">&#34;PID %s = %s\n&#34;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">mullvad_vpn_exclude_array</span><span class="p">[</span><span class="nv">$key</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="k">$(</span>ps -p <span class="s2">&#34;</span><span class="si">${</span><span class="nv">mullvad_vpn_exclude_array</span><span class="p">[</span><span class="nv">$key</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span> -o <span class="nv">command</span><span class="o">=</span><span class="k">)</span><span class="s2">&#34;</span>
        <span class="k">fi</span>
    <span class="k">done</span>
<span class="o">}</span>

<span class="c1"># list just process name info associated with each PID in list</span>
<span class="k">function</span> mullvad-list-excluded-processes-by-short-name<span class="o">()</span> <span class="o">{</span>
    <span class="nv">mullvad_vpn_exclude_array</span><span class="o">=(</span> <span class="k">$(</span>mullvad split-tunnel list<span class="k">)</span> <span class="o">)</span>
    <span class="nv">mvre</span><span class="o">=</span><span class="s1">&#39;^[0-9]+$&#39;</span>
    <span class="nb">printf</span> <span class="s2">&#34;Processes excluded from VPN tunnel:\n\n&#34;</span>
    <span class="k">for</span> key in <span class="s2">&#34;</span><span class="si">${</span><span class="p">!mullvad_vpn_exclude_array[@]</span><span class="si">}</span><span class="s2">&#34;</span>
    <span class="k">do</span>
        <span class="k">if</span>  <span class="o">[[</span> <span class="si">${</span><span class="nv">mullvad_vpn_exclude_array</span><span class="p">[</span><span class="nv">$key</span><span class="p">]</span><span class="si">}</span> <span class="o">=</span>~ <span class="nv">$mvre</span> <span class="o">]]</span>
        <span class="k">then</span>
            <span class="nb">printf</span> <span class="s2">&#34;PID %s = %s\n&#34;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">mullvad_vpn_exclude_array</span><span class="p">[</span><span class="nv">$key</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="k">$(</span>ps -p <span class="s2">&#34;</span><span class="si">${</span><span class="nv">mullvad_vpn_exclude_array</span><span class="p">[</span><span class="nv">$key</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span> -o <span class="nv">comm</span><span class="o">=</span><span class="k">)</span><span class="s2">&#34;</span>
        <span class="k">fi</span>
    <span class="k">done</span>
<span class="o">}</span>
</code></pre></div><ul>
<li>Similarly for Fish:
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="k">function</span> mullvad-list-excluded-processes-by-long-name --description <span class="s1">&#39;lists excluded processes in mullvads split tunnel by long command name&#39;</span>
    <span class="nb">printf</span> <span class="s2">&#34;Processes excluded from VPN tunnel:\n&#34;</span>
    <span class="k">for</span> key in <span class="k">$(</span>mullvad split-tunnel list<span class="k">)</span>
        <span class="k">if</span> string match -qr <span class="s1">&#39;^[0-9]+$&#39;</span> -- <span class="s2">&#34;</span><span class="nv">$key</span><span class="s2">&#34;</span>
             <span class="nb">printf</span> <span class="s2">&#34;PID %s: %s\n&#34;</span> <span class="s2">&#34;</span><span class="nv">$key</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="k">$(</span>ps -p <span class="nv">$key</span> -o <span class="nv">command</span><span class="o">=</span><span class="k">)</span><span class="s2">&#34;</span>
        end
    end
end

<span class="k">function</span> mullvad-list-excluded-processes-by-short-name --description <span class="s1">&#39;lists excluded processes in mullvads split tunnel by short process name&#39;</span>
    <span class="nb">echo</span> <span class="s2">&#34;Processes excluded from VPN tunnel:&#34;</span>
    <span class="k">for</span> key in <span class="k">$(</span>mullvad split-tunnel list<span class="k">)</span>
        <span class="k">if</span> string match -qr <span class="s1">&#39;^[0-9]+$&#39;</span> -- <span class="s2">&#34;</span><span class="nv">$key</span><span class="s2">&#34;</span>
             <span class="nb">printf</span> <span class="s2">&#34;PID %s: %s\n&#34;</span> <span class="s2">&#34;</span><span class="nv">$key</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="k">$(</span>ps -p <span class="nv">$key</span> -o <span class="nv">comm</span><span class="o">=</span><span class="k">)</span><span class="s2">&#34;</span>
        end
    end
end
</code></pre></div></li>
</ul>
</li>
</ul>
<h2 id="beyond-tunnelling-and-killing">Beyond tunnelling and killing</h2>
<p>These techniques — including defining various named speciality
functions (in whatever shell) — could obviously be adapted for other
similar cases of wanting to run a command on multiple processes
sharing (all or part of) a name, whenever you find you&rsquo;re tired of
wasting time manually mucking about with heaps of PIDs.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>For list of good Linux system <code>top</code>'s and related, see, e.g.,
<a href="https://github.com/luong-komorebi/Awesome-Linux-Software#system-info--monitoring">https://github.com/luong-komorebi/Awesome-Linux-Software#system-info--monitoring</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>The <a href="https://en.wikipedia.org/wiki/Xkill">Wikipedia page</a> for <code>xkill</code> notes: &lsquo;Xkill has been cited as
an example of a program with a simple and appealing user
interface. Its mode of operation has been summed up as &ldquo;Just click the
bad thing with the skull and it dies.&quot;&rsquo; <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>A Scheme-based package manager+Linux distribution (<a href="https://en.wikipedia.org/wiki/GNU_Guix">Wikipedia:
Guix</a>). See elsewhere on this blog: <a href="https://babbagefiles.xyz/categories/guix/">#guix</a>. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>The PPID is the Process ID of parent process. You can see that
the PID we&rsquo;re looking at here is <code>26384</code>, if you look at the longer list
of the output of <code>ps -ef | grep -i emacs</code> above, you can see that this
is the PPID of a number of the other listed processes, because they
were started by this <code>emacs --daemon</code> process. (—which itself has a PPID
of <code>1</code> because I started it from the terminal myself, and that seems to
count as being started by Process 1, the first process started during
the booting of the system, which is usually the <a href="https://en.wikipedia.org/wiki/Init">init system</a>,
presumably because it&rsquo;s the ancestor of all other processes and
&ldquo;adopts&rdquo; any &ldquo;orphaned&rdquo; or otherwise apparently unsupervised
processes.) <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>Quote from:
<a href="https://stackoverflow.com/a/43725403/570251">https://stackoverflow.com/a/43725403/570251</a>. <code>-9</code> is equivalent to
<code>-SIGKILL</code>, on which see <a href="https://en.wikipedia.org/wiki/Signal_(IPC)#SIGKILL">here</a>, whereas the plain <code>kill</code> or <code>kill -15</code> is
<code>-SIGTERM</code>, on which see <a href="https://en.wikipedia.org/wiki/Signal_(IPC)#SIGTERM">here</a>. <a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p>A working package definition for the Mullvad VPN desktop
client on Guix is <a href="https://gitlab.com/emacsomancer/guix-awesomejit/-/blob/main/awesomejit/packages/mullvad.scm">here</a>. Using Mullvad on Guix has been a longtime
bugbear for me — so much so that I once wrote my frontend for it in
Common Lisp: <a href="https://gitlab.com/emacsomancer/volemad">Volemad</a>. <a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7" role="doc-endnote">
<p>I.e., <code>source ~/.bash_profile</code>. <a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/linux" term="linux" label="linux" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/shell" term="shell" label="shell" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Using Emacs and Org-Roam/Org-Node on Android (with Termux Extra Keys)]]></title>
            <link href="https://babbagefiles.xyz/termux-extra-keys-emacs-org-roam-node-android/"/>
            <id>https://babbagefiles.xyz/termux-extra-keys-emacs-org-roam-node-android/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2025-01-30T14:15:00-06:00</published>
            <updated>2025-02-03T11:42:23-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>My main use of Emacs on Android (via Termux) is to be able to access
and add to my Org-roam library of notes. This post is primarily about
some Termux features which improve the user experience for this use
case. [Update <span class="timestamp-wrapper"><span class="timestamp">[2025-02-03 Mon]</span></span>: added some screenshots throughout to
give a sense of what it looks like.]</p>
<h2 id="trying-to-cope-with-emacs-on-mobile">Trying to Cope with Emacs on mobile</h2>
<p>I&rsquo;ve tried a number of different solutions for managing sync&rsquo;ed Org
files on mobile<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and some of these are useful for some
purposes, but to being able to access and add to my Org-Roam notes,
I&rsquo;ve found I really need a full-blooded Emacs
instance.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>And dealing with interacting with Emacs on a touchscreen
interface/touchscreen keyboard is a bit of a nightmare.</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/John_Tenniel_-_Playing_cards_-_in_The_nursery_Alice_in_Wonderland.jpg"/> </figure>

<p>Here, we&rsquo;ll try to improve it a little. Into the rabbit-hole&hellip;.</p>
<h2 id="note-about-using-termux-for-emacs-on-android">Note about using Termux for Emacs on Android</h2>
<p>You&rsquo;re probably better off installing Termux from <a href="https://f-droid.org/en/packages/com.termux/">F-Droid</a> rather than
from the Google Play Store [see the note from the official Termux
maintainers at:
<a href="https://github.com/termux/termux-app/discussions/4000">https://github.com/termux/termux-app/discussions/4000</a>]. This is
especially true is you want to use Emacs in Termux for managing
Org-roam files which are shared/kept in sync with your Emacs on
desktop machines, which you can do with <a href="https://syncthing.net/">Syncthing</a> (use Catfriend1&rsquo;s
Syncthing-fork [<a href="https://github.com/Catfriend1/syncthing-android">Github</a>]/[<a href="https://f-droid.org/en/packages/com.github.catfriend1.syncthingandroid/">F-Droid</a>] on Android), in order to be able to
grant Termux (and thus Emacs) access to your shared Org files.</p>
<h2 id="termux-extra-keys">Termux Extra Keys</h2>
<p>Termux has a very useful feature adding <a href="https://wiki.termux.com/wiki/Touch_Keyboard#Extra_Keys_Row">extra keys</a> above the system
keyboard. These are quite useful, including &lsquo;sticky&rsquo; <code>CTRL</code> and <code>ALT</code>
keys. (In addition, one can hold <code>volume-down</code> and press a key for
<code>CTRL</code> + [that-key] or <code>volume-up</code> and press a key for <code>ALT</code> + [that-key];
with <code>volume-up</code> + <code>t</code> inputting <code>TAB</code>. At least on my phone, using the
volume keys as modifiers is fairly awkward/uncomfortable, so I prefer
these extra keys.)</p>
<p>[Nb.: These keys are toggled on and off by pressing <code>volume-up + k</code> or
<code>volume-up + q</code>. This is important to note, for I once toggled them off
by accident and couldn&rsquo;t understand what had happened to them for
several months.]</p>
<p>Also worth mentioning is the fact that:</p>
<ul>
<li>swipe left on extra keys brings up swipe-compatible/regular keyboard
typing</li>
<li>swipe right on regular keyboard typing area (where extra keys was)
will get you back to extra keys</li>
</ul>
<p>The first of these lets you use any of your keyboard&rsquo;s regular
features and so can be quite useful.</p>
<p>See the <a href="https://wiki.termux.com/wiki/Touch_Keyboard#Extra_Keys_Row">Termux manual on Extra Keys</a> for a description and basic
configuration and usage instructions.</p>
<h2 id="extra-keys-for-better-emacs-navigation">Extra Keys for better Emacs navigation</h2>
<p>Here, I explore the possibilities offered by the advanced
configuration options for Extra Keys for navigating Emacs in Termux,
especially <a href="https://www.orgroam.com">Org-Roam</a> or <a href="https://github.com/meedstrom/org-node">Org-node</a> <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, to create faster/easier ways of
calling commonly used commands since the user-interface to Emacs with
an on-screen keyboard is often somewhat frustrating compared to the
using Emacs with a physical keyboard. In addition to extra tappable
keys, Extra Keys also allows for different outcomes when the user does
swipe-up on a key, including the outputting of key sequences, which
can include modifier keys like <code>CTRL</code>. This allows the possibility of
loading them with Emacs key sequences which would otherwise be much
slower to access with an onscreen keyboard.</p>
<p>What I wanted was:</p>
<ul>
<li>
<p>An easy, single-key (no modifier) way of doing forward and backward
searches. It is painful to either hold <code>volume-down</code> or have to keep
on toggling the Extra Keys&rsquo; <code>CTRL</code> for each next/previous result. So I
recruited <code>♡</code> and <code>♤</code> for forward and back searches, respectively
(because ♡ is sort of pointed down and ♤ is sort of pointing up),
and bound these appropriately in my Emacs configuration (see below).</p>
</li>
<li>
<p>other convenient single-key buffer navigation interface features, e.g.,
arrow keys and page-up and page-down</p>
</li>
<li>
<p>some convenience things like closing all but the current window
(<code>delete-other-windows</code> = <code>C-x 1</code>), opening up iBuffer, switch-buffer</p>
</li>
<li>
<p>slashes and <code>-</code> and <code>~</code> available immediately to tap insert (not under
my keyboard&rsquo;s symbol toggle or long-hold)</p>
</li>
<li>
<p>ability to call some Org-mode/Org-node commands quickly: including
finding nodes, inserting links to nodes, capturing new dailies,
committing captures, going to today&rsquo;s daily entry, moving forward
and backwards through the dailies, toggling the org-roam backlinks
buffer</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/termux-ycombinator-id-node-search-y_comb.png"/> </figure>

</li>
</ul>
<h2 id="example-configuration">Example configuration</h2>
<p>The configuration outlined below is partially specific for my Emacs
configuration, especially for the <code>C-c</code> (<code>CTRL c</code>) keys, but some things
involve default Emacs keybindings and it can at least serve as a model
for what one can do. To configure Extra Keys, you need to modify the
<code>extra-keys</code> part of <code>~/.termux/termux.properties</code> in this fashion:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">extra-keys <span class="o">=</span> <span class="o">[[</span> <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;ESC&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL x 1&#34;</span>, display: <span class="s2">&#34;C-x 1&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;CTRL&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c&#34;</span>, display: <span class="s2">&#34;C-c&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;ALT&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;ALT x&#34;</span>, display: <span class="s2">&#34;M-x&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;♡&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL x r b&#34;</span>, display: <span class="s2">&#34;bookmarks&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;~&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c n d&#34;</span>, display: <span class="s2">&#34;today&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;UP&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c n n&#34;</span>, display: <span class="s2">&#34;new note&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;-&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c CTRL k&#34;</span>, display: <span class="s2">&#34;cancel note&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;PGUP&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c n y&#34;</span>, display: <span class="s2">&#34;prev note&#34;</span><span class="o">}}]</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">[</span> <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;TAB&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL x b&#34;</span>, display: <span class="s2">&#34;alttab&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;/&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c n f&#34;</span>, display: <span class="s2">&#34;find node&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;\\\\&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c n i&#34;</span>, display: <span class="s2">&#34;insert link&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;♤&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL x CTRL b&#34;</span>, display: <span class="s2">&#34;ibuffer&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;LEFT&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c n l&#34;</span>, display: <span class="s2">&#34;backlinks&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;DOWN&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c CTRL c&#34;</span>, display: <span class="s2">&#34;commit&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;RIGHT&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c n v&#34;</span>, display: <span class="s2">&#34;goto-date&#34;</span><span class="o">}}</span>, <span class="se">\
</span><span class="se"></span>  <span class="o">{</span>key: <span class="s1">&#39;PGDN&#39;</span>, popup: <span class="o">{</span>macro: <span class="s2">&#34;CTRL c n t&#34;</span>, display: <span class="s2">&#34;next note&#34;</span><span class="o">}}]]</span>
</code></pre></div>


<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/termux-ibuffer.png"/> </figure>

<p>Here is a rough summary of what this does:</p>
<table>
<thead>
<tr>
<th><strong>key</strong></th>
<th><strong>do on key tap</strong></th>
<th><strong>do on key swipe up</strong></th>
<th><strong>display on swipe up</strong></th>
<th><strong>what key swipe up does</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ESC</code></td>
<td>&lsquo;esc&rsquo;</td>
<td><code>C-x 1</code></td>
<td>&ldquo;C-x 1&rdquo;</td>
<td><code>delete-other-windows</code></td>
</tr>
<tr>
<td><code>CTRL</code></td>
<td>&lsquo;ctrl&rsquo; (<code>C-</code>) toggle</td>
<td><code>C-c</code></td>
<td>&ldquo;C-c&rdquo;</td>
<td>start <code>C-c</code> prefix command</td>
</tr>
<tr>
<td><code>ALT</code></td>
<td>&lsquo;alt&rsquo; (<code>M-</code>) toggle</td>
<td><code>M-x</code></td>
<td>&ldquo;M-x&rdquo;</td>
<td><code>M-x</code></td>
</tr>
<tr>
<td><code>♡</code></td>
<td><code>isearch-forward</code></td>
<td><code>C-x r b</code></td>
<td>&ldquo;bookmarks&rdquo;</td>
<td><code>bookmark-jump</code></td>
</tr>
<tr>
<td><code>~</code></td>
<td>&lsquo;~&rsquo;</td>
<td><code>C-c n d</code></td>
<td>&ldquo;today&rdquo;</td>
<td><code>org-node-goto-today</code></td>
</tr>
<tr>
<td><code>↑</code></td>
<td>&lsquo;up&rsquo;</td>
<td><code>C-c n n</code></td>
<td>&ldquo;new note&rdquo;</td>
<td><code>org-roam-dailies-capture-today</code></td>
</tr>
<tr>
<td><code>-</code></td>
<td>&lsquo;-&rsquo;</td>
<td><code>C-c C-k</code></td>
<td>&ldquo;cancel note&rdquo;</td>
<td><code>C-c C-k</code> to cancel capture</td>
</tr>
<tr>
<td><code>PGUP</code></td>
<td>&lsquo;pageup&rsquo;</td>
<td><code>C-c n y</code></td>
<td>&ldquo;prev note&rdquo;</td>
<td><code>org-node-goto-next-day</code></td>
</tr>
<tr>
<td><code>TAB</code></td>
<td>&lsquo;tab key&rsquo;</td>
<td><code>C-x b</code></td>
<td>&ldquo;alttab&rdquo;</td>
<td><code>consult-buffer</code> (<code>switch-to-buffer</code>)</td>
</tr>
<tr>
<td><code>/</code></td>
<td>&lsquo;/&rsquo;</td>
<td><code>C-c n f</code></td>
<td>&ldquo;find node&rdquo;</td>
<td><code>org-node-find</code></td>
</tr>
<tr>
<td><code>\</code></td>
<td>&lsquo;\&rsquo;</td>
<td><code>C-c n i</code></td>
<td>&ldquo;insert link&rdquo;</td>
<td><code>org-node-insert-link*</code></td>
</tr>
<tr>
<td><code>♤</code></td>
<td><code>isearch-backward</code></td>
<td><code>C-x C-b</code></td>
<td>&ldquo;ibuffer&rdquo;</td>
<td><code>ibuffer</code></td>
</tr>
<tr>
<td><code>←</code></td>
<td>&lsquo;left&rsquo;</td>
<td><code>C-c n l</code></td>
<td>&ldquo;backlinks&rdquo;</td>
<td><code>org-roam-buffer-toggle</code></td>
</tr>
<tr>
<td><code>↓</code></td>
<td>&lsquo;down&rsquo;</td>
<td><code>C-c C-c</code></td>
<td>&ldquo;commit&rdquo;</td>
<td><code>C-c C-c</code> to &lsquo;commit&rsquo;</td>
</tr>
<tr>
<td><code>→</code></td>
<td>&lsquo;right&rsquo;</td>
<td><code>C-c n v</code></td>
<td>&ldquo;goto-date&rdquo;</td>
<td><code>org-node-goto-daily</code></td>
</tr>
<tr>
<td><code>PGDN</code></td>
<td>&lsquo;pagedown&rsquo;</td>
<td><code>C-c n t</code></td>
<td>&ldquo;next note&rdquo;</td>
<td><code>org-node-goto-prev-day</code></td>
</tr>
</tbody>
</table>
<p>In case you do use Org-node (and, if you&rsquo;re trying to do Org-roam
things on Android, I would again recommend considering Org-node, if
only for making interactions fast enough to not be completely
frustrating), you could implement the following interactive functions:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">org-node-goto-daily</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">org-node-seq--jump</span> <span class="s">&#34;d&#34;</span><span class="p">))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">org-node-goto-today</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">org-node-seq-goto</span> <span class="s">&#34;d&#34;</span> <span class="p">(</span><span class="nf">format-time-string</span> <span class="s">&#34;%F&#34;</span><span class="p">)))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">org-node-goto-next-day</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">interactive</span> <span class="no">nil</span> <span class="nv">org-mode</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">org-node-seq--goto-next</span> <span class="s">&#34;d&#34;</span><span class="p">))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">org-node-goto-prev-day</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">interactive</span> <span class="no">nil</span> <span class="nv">org-mode</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">org-node-seq--goto-previous</span> <span class="s">&#34;d&#34;</span><span class="p">))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">org-roam-dailies-capture-today</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">org-roam-dailies-capture-today</span> <span class="no">nil</span> <span class="s">&#34;d&#34;</span><span class="p">))</span>
</code></pre></div><p>And then bind:</p>
<ul>
<li><code>org-node-insert-link*</code> to &ldquo;C-c n i&rdquo;</li>
<li><code>org-node-find</code> to &ldquo;C-c n f&rdquo;</li>
<li><code>org-node-goto-daily</code> to &ldquo;C-c n v&rdquo;</li>
<li><code>org-node-goto-today</code> to &ldquo;C-c n d&rdquo;</li>
<li><code>org-node-goto-next-day</code> to &ldquo;C-c n t&rdquo;  ('[t]omorrow&rsquo;)</li>
<li><code>org-node-goto-prev-day</code> to &ldquo;C-c n y&rdquo; ('[y]esterday&rsquo;)</li>
<li><code>org-roam-dailies-capture-today</code> to &ldquo;C-c n n&rdquo;</li>
<li><code>org-roam-buffer-toggle</code> to &ldquo;C-c n l&rdquo;</li>
</ul>
<p>Most of the other keybindings implemented above are default Emacs
bindings.</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/termux-org-node-id-search-clo_s.png"/> </figure>

<p>In terms of how this improves the Termux Emacs experience, consider
that you could open up Emacs in Termux and then proceed by:</p>
<ul>
<li>swiping up on <code>↑</code> to open up a new Org-[roam/node] note in today&rsquo;s
daily journal</li>
<li>then swiping left on the extra keys pane to get into normal keyboard
entry mode</li>
<li>swipe/glide-typing the text of your note quickly, and pressing enter</li>
<li>swiping right on the now clear virtual keyboard pane to bring back
the extra keys</li>
<li>swiping up on <code>\</code> to insert (&lsquo;splice in&rsquo;) a org-id link to another
Org-roam node or two</li>
<li>swiping up on <code>↓</code> to commit/complete your daily note</li>
<li>swiping up on <code>PGUP</code> to see yesterday&rsquo;s Org-roam daily journal</li>
<li>swiping up on <code>/</code> to search for your &ldquo;Termux&rdquo; Org-roam node to add
notes about Extra Keys to your Termux Org-roam node/file</li>
<li>tapping <code>♡</code> to search through the entry to see all the places you made
reference to &ldquo;extra keys&rdquo;</li>
<li>swiping up on <code>←</code> to see what backlinks you have for &ldquo;Termux&rdquo;</li>
<li>swiping up on <code>ESC</code> to get back to single window view</li>
<li>swiping up on <code>♤</code> to get to IBuffer to be able to manage your buffers</li>
<li>swiping up on <code>↑</code> to open another new daily note in today&rsquo;s journal</li>
<li>changing your mind and swiping up on <code>-</code> to cancel the note capture</li>
<li>&hellip;..</li>
</ul>
<p>This is all a lot faster than having to tap <code>CTRL</code> and then type <code>c</code> <code>n</code> <code>n</code>
to start a new entry in today&rsquo;s Org-roam daily journal, typing it out
(tapping <code>CTRL</code> and then <code>c n i</code> each time you want to link text to
another node), and then tapping <code>CTRL</code> and then <code>c</code> and then <code>CTRL</code> and then
<code>c</code> to commit it, and so on.</p>
<p>For a bonus reduction in modifier and key tapping, consider using a
modal mode in Emacs: I particularly like <a href="https://github.com/meow-edit/meow">meow</a>.</p>
<h2 id="appendix-key-mnemonics">Appendix: Key Mnemonics</h2>
<p>A potentially useful/emblematic list of mnemonics for the keys. [I use
☝️ to indicate swiping up on a key below.] Some of these mnemonics are
stretches, but there are only so many keys and a particular set of
things one wants, so stretches are sometimes required.</p>
<ul>
<li><code>♡</code> is a forwards/downwards search because the heart points down</li>
<li><code>♤</code> is a backwards/upwards search because the spade points up [and we
use these pointy characters indicating card suits in preference to
others because I&rsquo;m not going to be typing them on Android ever]</li>
<li>(all other single tap keys just do what it says on the tin,
inserting characters, toggling modifiers, &amp;c.)</li>
<li>☝️ <code>♡</code> is bookmarks because these are &lsquo;favourites&rsquo; because you &lsquo;♡&rsquo; them</li>
<li>☝️ <code>♤</code> is IBuffer because if you have icons enabled in your IBuffer<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
then you&rsquo;ll see icons and ♤ is like an icon (I know this one is a
stretch, but&hellip;.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>)</li>
<li>☝️ <code>ESC</code> is <code>delete-other-windows</code> because you&rsquo;re &lsquo;escaping&rsquo; from them</li>
<li>☝️ <code>CTRL</code> is <code>C-c</code> because <code>C-c</code> starts with CTRL</li>
<li>☝️ <code>ALT</code> is <code>M-x</code> because <code>M-x</code> starts with ALT</li>
<li>☝️ <code>~</code> is <code>org-node-goto-today</code> because in Unix <code>~</code> is &ldquo;user&rsquo;s home
directory&rdquo; and today&rsquo;s daily Org-roam entry is a sort of a &lsquo;home&rsquo;
(it makes sense to me anyway)</li>
<li>☝️ <code>-</code> is <code>C-c C-k</code> is which &lsquo;cancel&rsquo; in lots of places (like capture
templates) and <code>-</code> is a negative sign and so can be understood like
&ldquo;don&rsquo;t do it!&rdquo;</li>
<li>☝️ <code>↑</code> is <code>org-roam-dailies-capture-today</code> because this is the function
to capture a new Org-roam daily note and ↑ is like &ldquo;opening up
something&rdquo;</li>
<li>☝️ <code>↓</code> is <code>C-c C-c</code> which is in many places in Emacs something like
&lsquo;commit&rsquo;, and for capture templates it is the &lsquo;finish capture&rsquo;
binding and if ↑ is opening a new note, then ↓ is finishing it,
putting it &lsquo;down&rsquo;</li>
<li>☝️ <code>←</code> is open up Org-roam backlinks buffer because ← is pointing
&lsquo;backwards&rsquo;</li>
<li>☝️ <code>→</code> is <code>org-node-goto-daily</code> because we&rsquo;re looking &lsquo;forward&rsquo; towards a
specific date and going to it</li>
<li>☝️ <code>PGUP</code> is <code>org-node-goto-prev-day</code> because we&rsquo;re going
&lsquo;up&rsquo;/&lsquo;backwards&rsquo; in the calendar (of daily Org-roam journal entries)</li>
<li>☝️ <code>PGDN</code> is <code>org-node-goto-prev-day</code> because we&rsquo;re going
&lsquo;down&rsquo;/&lsquo;forwards&rsquo; in the calendar (of daily Org-roam journal
entries)</li>
<li>☝️ <code>TAB</code> is <code>consult-buffer</code> / <code>switch-to-buffer</code> because this function is
sort of like <code>Alt-TAB</code> in many window environments</li>
<li>☝️ <code>/</code> is <code>org-node-find</code> because <code>/</code> is the search key in Vim, and also
associated with searches elsewhere in Unixland (the marker of
beginning/end of regexs, often used for searches) [and because <code>/</code> is
the opposite slash from <code>\</code>, which we&rsquo;re going to use for the
complementary operation to insert links]</li>
<li>☝️ <code>\</code> is <code>org-node-insert-link*</code> because in many environments <code>\</code> is some
sort of &lsquo;splice&rsquo; indicator<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>, and inserting a link to a node is
also sort of a splice [and because <code>\</code> is the opposite slash from <code>/</code>
which performs a complementary operation]</li>
</ul>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>E.g., <a href="https://www.orgzly.com/">orgzly</a>, <a href="https://organice.200ok.ch/">organice</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>There is a <a href="https://f-droid.org/packages/org.gnu.emacs/">full GUI Emacs now on Android</a>, but I
don&rsquo;t know how to get it to be able to access files shared via
Syncthing, and, anyway, you wouldn&rsquo;t be able to do the sort of
convenient keyboard hacks there I&rsquo;m going to discuss in this post. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>I highly recommend looking at Martin Edström&rsquo;s <a href="https://github.com/meedstrom/org-node">Org-node</a> package if
you&rsquo;re trying to manage Org-roam notes on Android; the performance
differences between the two packages are especially noticeable on
Android (orders of magnitude<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>). And the two packages can live
alongside each other, so you can keep your existing Org-roam set-up
going while you set-up/experiment with Org-node. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>With a package like
<a href="https://github.com/seagle0128/all-the-icons-ibuffer">https://github.com/seagle0128/all-the-icons-ibuffer</a>. On IBuffer
itself, see <a href="https://www.emacswiki.org/emacs/IbufferMode">https://www.emacswiki.org/emacs/IbufferMode</a>. <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>Well, okay, so here&rsquo;s another one: think of
IBuffer as trying to right your buffer organisation like Two, Five,
and Seven (of Spades) in Alice in Wonderland trying sort out the
Queen of Hearts&rsquo; rose-bushes (by painting the roses red):
<img src="/ox-hugo/Two_Five_and_Seven_of_Spades_Painting_the_Rosebushes.jpg" alt=""> <a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p>From <a href="https://port70.net/~nsz/c/c11/n1570.html#5.1.1.2">https://port70.net/~nsz/c/c11/n1570.html#5.1.1.2</a>:</p>
<blockquote>
<p>Each instance of a backslash character (\) immediately followed by a
new-line character is deleted, <strong>splicing</strong> physical source lines to form
logical source lines. Only the last backslash on any physical source
line shall be eligible for being part of such a <strong>splice</strong>. A source file
that is not empty shall end in a new-line character, which shall not
be immediately preceded by a backslash character before any such
<strong>splicing</strong> takes place.</p>
</blockquote>
 <a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></li>
<li id="fn:7" role="doc-endnote">
<p>Android 10/11 seems to have introduced
which restricts
how regular applications/users can interact with the filesystem, for
&lsquo;security&rsquo; reasons, implemented via <a href="https://en.wikipedia.org/wiki/Filesystem_in_Userspace">Filesystem in Userspace (FUSE)</a>,
which makes operations which interact with the file system sometimes
very sluggish. Accessing directories with lots of files, as one might
have in an Org-roam directory, can be amazingly slow. The increases
exponentially with the number of Org-roam files.</p>
<p>Org-node is implemented in such a way
[cf. <a href="https://github.com/meedstrom/org-node/issues/26">https://github.com/meedstrom/org-node/issues/26</a>] that it
caches/stores and accesses many things in ways that avoid directly
accessing the file-system (especially in <a href="https://github.com/meedstrom/org-node-fakeroam">org-node-fakeroam</a>), where
Org-roam does not. <a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/orgroam" term="orgroam" label="orgroam" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/android" term="android" label="android" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Guix, XIM, Emacs, Multi_key, Shft+SPC]]></title>
            <link href="https://babbagefiles.xyz/guix-xim-emacs-multikey-shft-spc/"/>
            <id>https://babbagefiles.xyz/guix-xim-emacs-multikey-shft-spc/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2025-01-30T12:22:00-06:00</published>
            <updated>2025-12-21T22:01:03-06:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>— Describe in single words only the good things that come into your mind
about&hellip; <code>&lt;Multi_key&gt;</code>.</p>
<p>— <code>&lt;Multi_key&gt;</code>?</p>
<p>— Yeah.</p>
<p>— Let me tell you about <code>&lt;Multi_key&gt;</code>&hellip;.</p>
<p><code>&quot;&lt;Multi_key&gt; is undefined&quot;</code></p>
</blockquote>
<p>A bit out of order, but things tangle, a problem I&rsquo;m having on my Guix
machine with Emacs.</p>
<p>It&rsquo;s connected with the Lucid toolkit<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but only in an
indirect causal sort of way:— I&rsquo;m using the standard GTK3 toolkit on
this particular build of Emacs on Guix. (A custom build, admittedly,
which might be part of the issue, but where exactly, I&rsquo;m not sure.)
I&rsquo;ll probably say some other things connected to this later, but for
the moment forget about Lucid. It&rsquo;s just a GTK3 toolkit build.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>So, skipping over large swathes of story, I&rsquo;ve got a seemingly
mostly-functioning build of Emacs 30.0.93 running on my Guix laptop
(which finally has a functioning StumpWM environment again), and I go
to type <em>æ</em> or <em>þ</em> or <em>á</em> or <em>λ</em> or something using <code>Right_Alt</code> as a compose key
(defined by <code>xmodmap</code>) and Emacs, without blinking an eye, flashes at
me, saying <code>&quot;&lt;Multi_key&gt; is undefined&quot;</code>.</p>
<p>I try various things to do with <a href="https://en.wikipedia.org/wiki/X_Input_Method">XIM</a> and environment variables<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> and
none of it works. I finally come across a post on a FreeBSD bugs
tracker<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> which suggests a possible fix of setting <code>(setq x-gtk-use-native-input 't)</code>.</p>
<p>And, though it feels like a hack for something that&rsquo;s gone wrong in a
deeper way, it seems to work, and I can type <em>ɔ</em> and <em>Þ</em> and <em>γ</em> and so
on.</p>
<p>But then I&rsquo;m reading a PDF (using <a href="https://github.com/vedang/pdf-tools">PDF Tools</a>) and I go to page back up
to read an earlier bit by pressing Shift and SPACE and I find myself
paging down to the end of the document instead.</p>
<p>The &ldquo;native input&rdquo; method apparently won&rsquo;t recognise Shft+SPACE, but
just sees SPACE and so pages down.</p>
<p>So if <code>x-gtk-use-native-input</code> is <code>nil</code>, I can&rsquo;t use the compose key in
Emacs, and if it&rsquo;s <code>t</code> then I can&rsquo;t scroll properly in PDFs.</p>
<p>I thought vaguely about writing a macro to process the <a href="https://github.com/kragen/xcompose/">&lsquo;kragen&rsquo; XCompose
definitions</a> into a bunch of things of the form:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nf">define-key</span> <span class="nv">global-map</span> <span class="p">[(</span><span class="nv">Multi_key</span><span class="p">)</span> <span class="p">(</span><span class="nv">s</span><span class="p">)</span> <span class="p">(</span><span class="nv">s</span><span class="p">)]</span> <span class="p">(</span><span class="nb">lambda</span> <span class="p">()</span>
                                                   <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
                                                 <span class="p">(</span><span class="nf">insert</span> <span class="mi">223</span><span class="p">)))</span>
</code></pre></div><p>and then have my init.el load the whole thing in. But I don&rsquo;t think
that would quite work anyway, since sometimes I want to say insert a special
character as part of a search and not insert it into a text buffer.</p>
<p>So I came up with a different stupid solution. It&rsquo;s not perfect, but
it&rsquo;s at least somewhat working while I try to figure out what went
wrong in the first place.</p>
<p>Here it is:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nf">system-name</span><span class="p">)</span> <span class="s">&#34;guix-laptop&#34;</span><span class="p">)</span>
  <span class="c1">;; on this Guix/StumpWM machine&#39;s Emacs, xcompose doesn&#39;t work:</span>
  <span class="c1">;; Emacs says &#34;I don&#39;t know what &lt;Multi_key&gt; means&#34;</span>
  <span class="c1">;; (well, it actually says &#34;&lt;Multi_key&gt; is undefined&#34;).</span>
  <span class="c1">;; As per https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=278167</span>
  <span class="c1">;; we can &#39;fix&#39; this if we set (setq x-gtk-use-native-input &#39;t).</span>
  <span class="c1">;; Then xcompose will will work again ... but now Shift-SPACE won&#39;t</span>

  <span class="c1">;; So...</span>

  <span class="p">(</span><span class="nb">setq</span> <span class="nv">x-gtk-use-native-input</span> <span class="ss">&#39;nil</span><span class="p">)</span> <span class="c1">;; make sure Shift-SPACE works...</span>

  <span class="c1">;; So, Emacs says &#34;&lt;Multi_key&gt; is undefined&#34;.</span>
  <span class="c1">;; Well, then, we can define it.</span>
  <span class="c1">;; Namely as a function which sets `x-gtk-use-native-input&#39; to `t&#39;,</span>
  <span class="c1">;; and then calls an external shell-command to input &lt;Multi_key&gt;</span>
  <span class="c1">;; (so that it acts like Right_Alt was pressed), and then sets a timer running</span>
  <span class="c1">;; that turns `x-gtk-use-native-input&#39; to `nil&#39; after 10 seconds</span>
  <span class="c1">;; (so that Shift-SPACE will work again).</span>

  <span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/xinput-hack</span> <span class="p">()</span>
    <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
    <span class="c1">;; stop the timer if there is one running already:</span>
    <span class="p">(</span><span class="nb">when</span> <span class="nv">bms/xinput-timer-running</span>
      <span class="p">(</span><span class="nv">cancel-timer</span> <span class="nv">bms/xinput-timer-running</span><span class="p">))</span>
    <span class="c1">;; set the input method to allow xcompose to work:</span>
    <span class="p">(</span><span class="nb">setq</span> <span class="nv">x-gtk-use-native-input</span> <span class="ss">&#39;t</span><span class="p">)</span>
    <span class="c1">;; must have `xdotool&#39; installed:</span>
    <span class="p">(</span><span class="nv">shell-command</span> <span class="s">&#34;xdotool key Multi_key&#34;</span><span class="p">)</span>
    <span class="c1">;; record the name of the timer to stop it,</span>
    <span class="c1">;; so we don&#39;t end up with tonnes of timers somehow</span>
    <span class="p">(</span><span class="nb">setq</span> <span class="nv">bms/xinput-timer-running</span>
          <span class="p">(</span><span class="nv">run-at-time</span> <span class="mi">10</span> <span class="no">nil</span>
          <span class="c1">;; this sets it for 10 secs; maybe could be shorter,</span>
          <span class="c1">;; but you don&#39;t want it turning off in the middle of sequences</span>
                       <span class="p">(</span><span class="nb">lambda</span> <span class="p">()</span>
                         <span class="p">(</span><span class="nb">setq</span> <span class="nv">x-gtk-use-native-input</span> <span class="ss">&#39;nil</span><span class="p">)))))</span>

  <span class="c1">;; And then we bind &lt;Multi_key&gt; to that function.</span>
  <span class="c1">;; (There, now it&#39;s not undefined anymore.)</span>

  <span class="p">(</span><span class="nf">define-key</span> <span class="nv">global-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;&lt;Multi_key&gt;&#34;</span><span class="p">)</span> <span class="nf">#&#39;</span><span class="nv">bms/xinput-hack</span><span class="p">))</span>
  <span class="c1">;; A stupid solution to a stupid problem.</span>
</code></pre></div><section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>See <a href="https://www.reddit.com/r/emacs/comments/1hlj04t/emacs_using_the_lucid_toolkit_is_blazingly_fast/">Emacs Using the Lucid toolkit is blazingly fast |
r/emacs</a> for why this might be interesting. This is the whole &ldquo;out of
order&rdquo; thing, but it&rsquo;s not directly relevant for this particular post. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Though, on the plain, boring, ordinary GTK3 toolkit and its
relation to Emacs see <a href="https://web.archive.org/web/20161115194659/https://www.facebook.com/notes/daniel-colascione/buttery-smooth-emacs/10155313440066102/">&ldquo;Buttery Smooth Emacs&rdquo;</a>. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>And I&rsquo;ve already got <code>(setf (getenv &quot;GTK_IM_MODULE&quot;) &quot;xim&quot;)</code>
and <code>(setf (getenv &quot;QT_IM_MODULE&quot;) &quot;xim&quot;</code> setting environment variables
in my <code>init.lisp</code> config for StumpWM here anyway. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p><a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=278167">https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=278167</a> <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/guix" term="guix" label="guix" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Linux Terminal Emulator Features and Hardware Compatibility]]></title>
            <link href="https://babbagefiles.xyz/terminal-emulator-vtt-features-compatibility/"/>
            <id>https://babbagefiles.xyz/terminal-emulator-vtt-features-compatibility/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2022-06-28T16:57:00-05:00</published>
            <updated>2025-12-21T22:01:03-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>In a continuing series of, er, terminal-related posts, a look at some
features of a subset of terminal emulators on Linux.</p>
<p>I mainly use <a href="https://github.com/akermu/emacs-libvterm">vterm inside of Emacs</a>, usually via <a href="https://babbagefiles.xyz/categories/equake/">Equake</a>, but sometimes
I do want to spawn a terminal outside of Emacs, and so I&rsquo;ve been
curious about the properties of different terminals, including the
ability to be used across a wide range of hardware.</p>
<p>A few weeks ago [as of 28 June 2022], I came across <a href="https://tomscii.sig7.se/zutty/">Zutty</a>, which
describes itself as:</p>
<blockquote>
<p>Zutty is a terminal emulator for the X Window System, functionally similar to several other X terminal emulators such as xterm, rxvt and countless others. It is also similar to other, much more modern, GPU-accelerated terminal emulators such as Alacritty and Kitty. What really sets Zutty apart is its radically simple, yet extremely efficient rendering implementation, coupled with a sufficiently complete feature set to make it useful for a wide range of users. Zutty offers high throughput with low latency, and strives to conform to relevant (published or de-facto) standards.</p>
<p>Zutty is written in straightforward C++ and only relies on OpenGL ES 3.1 for rendering&hellip;</p>
</blockquote>
<p>The Zutty page includes a link to a blog post <a href="https://tomscii.sig7.se/2020/12/A-totally-biased-comparison-of-Zutty">&ldquo;A totally biased
comparison of Zutty (to some better-known X terminal emulators)&quot;</a>,
which includes some interesting discussion of features of different
terminal emulators, including comparison of VT support levels, and
discussion of why this could be important. A number of terminal
emulators, perhaps leaning towards &ldquo;minimalism&rdquo;, only implement
support up to VT220, or even lower, which creates inefficiencies in
certain cases:</p>
<blockquote>
<p>The difference between implementing a VT400 or VT500 terminal (provided the implementations are correct) is relatively inconsequential. The same cannot be said of the gap between these and VT220-level terminals. Programs running in a less capable virtual terminal must sometimes use longer series of basic escape sequences to achieve the results of a fewer number of more modern ones. For example, the DECLRMM control sequence (set left-right margin mode) is available from the VT420 and up, and will be used to restrict scrolling to the active part of two horizontally split tmux panes. On less capable terminals, tmux is forced to perform more work to achieve the same result. This is quite similar to how extended instructions on modern CPUs allow machine code to be more efficient than code compiled for an older machine. In other words, not implementing a modern VT variant goes directly counter to greater efficiency. And there is yet another downgrade from VT220 to those terminals that only claim to support VT100/VT102.</p>
<p>It is surprising how unambitious the newer terminals Alacritty and Kitty are in this regard. I would have hoped that new, supposedly state-of-the-art entrants would take the effort to add support for modern VT standards. Switching from xterm to Alacritty (or any other modern terminal) should not be a major downgrade.</p>
</blockquote>
<p>Inspired by this, I copy/supplement the table on that page with some
additional comparison information, and a few additional terminal
emulators. As per the Zutty post, I use <a href="https://invisible-island.net/vttest/">VTTEST</a> (originally written in
1983-1985 by Per Lindberg at the Stockholm University Computing
Center; in 1996 a new version written by Thomas E. Dickey, the
maintainer of <a href="https://en.wikipedia.org/wiki/Xterm">xterm</a>). to check for reported VT support level. The
below table includes also whether the terminal emulator is
GPU-accelerated or not, and what the mininum <a href="https://en.wikipedia.org/wiki/OpenGL">OpenGL</a> hardware support
is, as well as what display server is targeted (<a href="https://en.wikipedia.org/wiki/X11">X11</a> or <a href="https://en.wikipedia.org/wiki/Wayland_(display_server_protocol)">Wayland</a> or
both).</p>
<p>(<strong>Nb.</strong>: see <a href="./#note-on-gnome-terminal">note on gnome-terminal and VT-capability</a>.)</p>
<table>
<thead>
<tr>
<th>program</th>
<th>tested version</th>
<th>VT self-id</th>
<th>GPU-accel</th>
<th>minimum OpenGL</th>
<th>DS</th>
</tr>
</thead>
<tbody>
<tr>
<td>zutty</td>
<td>0.6</td>
<td>VT520</td>
<td>yes</td>
<td>ES 3.1</td>
<td>X11</td>
</tr>
<tr>
<td>terminology</td>
<td>1.12.1</td>
<td>VT510</td>
<td>when possible</td>
<td>n/a</td>
<td>X11</td>
</tr>
<tr>
<td>wezterm</td>
<td>20220624</td>
<td>VT500</td>
<td>yes</td>
<td>3.3 (?)</td>
<td>both</td>
</tr>
<tr>
<td>xterm</td>
<td>344</td>
<td>VT420</td>
<td>no</td>
<td>n/a</td>
<td>X11</td>
</tr>
<tr>
<td>kitty</td>
<td>0.19.3</td>
<td>VT220</td>
<td>yes</td>
<td>3.3</td>
<td>both</td>
</tr>
<tr>
<td>foot</td>
<td>1.12.1</td>
<td>VT220</td>
<td>no</td>
<td>n/a</td>
<td>wayland</td>
</tr>
<tr>
<td>alacritty</td>
<td>0.4.3</td>
<td>VT102</td>
<td>yes</td>
<td>3.3</td>
<td>both</td>
</tr>
<tr>
<td>st</td>
<td>0.8.2</td>
<td>VT102</td>
<td>no</td>
<td>n/a</td>
<td>X11</td>
</tr>
<tr>
<td>urxt</td>
<td>v9.22</td>
<td>VT102</td>
<td>no</td>
<td>n/a</td>
<td>X11</td>
</tr>
<tr>
<td>(lib)vterm</td>
<td>0.1.4</td>
<td>VT100/VT102</td>
<td>n/a</td>
<td>n/a</td>
<td>n/a</td>
</tr>
<tr>
<td>gnome-terminal</td>
<td>3.48.3</td>
<td>VT510?(see <a href="./#note-on-gnome-terminal">note</a>)</td>
<td>no?</td>
<td>n/a</td>
<td>both</td>
</tr>
</tbody>
</table>
<p><a href="https://codeberg.org/dnkl/foot">Foot</a> is another interesting new terminal emulator - targetting Wayland
only, and not GPU-accelerated, but with some design choices that make
it faster than GPU-accelerated terminal emulators in some cases, as
described in the wiki <a href="https://codeberg.org/dnkl/foot/wiki/Performance">&ldquo;When is foot fast, and when is it not?&quot;</a>. One of
the things it does is &ldquo;damage tracking&rdquo;, i.e. only rendering cells
that have been updated, whereas e.g. alacritty rerenders everything
(though, as noted, &ldquo;Alacritty renders empty cells really fast&rdquo;). But
foot only supports up to VT220 (though alacritty only up to VT102!).</p>
<p>The terminal emulator I end up using most is based on <a href="https://www.leonerd.org.uk/code/libvterm/">libvterm</a>, this
is the library underlying Emacs&rsquo; vterm, and claims to &ldquo;implement[] a
VT220 or xterm-like terminal emulator&rdquo;, but VTTEST seems to report as
VT102/VT100, at least inside of Emacs.</p>
<p>There&rsquo;s no clear &ldquo;best choice&rdquo; here, even putting vterm aside (vterm
isn&rsquo;t particularly fast, at least inside of Emacs (though it&rsquo;s faster
than other Emacs terminal emulator choices like <code>ansi-term</code>), doesn&rsquo;t
have &ldquo;very high&rdquo; VT support; but it runs inside in Emacs, which offers
me a lot of advantages). Though alacritty, <a href="https://st.suckless.org/">st</a>, and <a href="https://en.wikipedia.org/wiki/Rxvt">urxvt</a> implement
only up to VT102 level features.</p>
<p><a href="https://codeberg.org/dnkl/foot">Foot</a>, <a href="https://tomscii.sig7.se/zutty/">Zutty</a>, <a href="https://sw.kovidgoyal.net/kitty/">Kitty</a>, and <a href="https://github.com/alacritty/alacritty">Alacritty</a> are all fast, at least sometimes
(the Zutty biased comparison post above offers some additional
discussion on this, not including foot). They&rsquo;re all limited in
various ways (hardware or display server - though Zutty apparently has
<a href="https://github.com/tomszilagyi/zutty/wiki/FAQ">experimental support for software rendering</a>), with only Zutty having
&ldquo;very high&rdquo; VT support. (And Zutty has some <a href="https://github.com/tomszilagyi/zutty/wiki/FAQ">bells-and-whistles
limitations</a>: no plans for transparency support, ligatures, bitmap
images (e.g. <a href="https://en.wikipedia.org/wiki/Sixel">SIXEL</a>).)</p>
<p>Foot runs on pretty much any hardware and has some nice features, but
is Wayland only (and limited in VT level support).</p>
<p>Kitty and Alacritty require OpenGL 3.3 (on Intel hardware, this means
at least Intel HD Graphics 3000 (so 2011-era **20 ThinkPads or
later)).</p>
<p>Zutty &ldquo;runs on a wider range of graphics hardware by virtue of only
requiring <a href="https://en.wikipedia.org/wiki/OpenGL_ES">OpenGL ES</a> as opposed to “desktop” OpenGL, and its resource
demands are otherwise minimal&rdquo;, but it requires ES 3.1, which isn&rsquo;t
supported by, for instance, Intel HD Graphics 3000.</p>
<p><a href="https://github.com/borisfaure/terminology">Terminology</a>, a terminal emulator which doesn&rsquo;t seem to get talked
about as much, actually turns out to be a good choice across different
hardware. It can use GPU-acceleration, rendering using OpenGL or
OpenGL-ES2, but this is not a hard requirement. It offers VT510 level
support, and has lots of bells and whistles. I haven&rsquo;t seen many speed
test comparisons with it, but in use it feels fast. (No native Wayland
support though, if that matters to you.)</p>
<h2 id="note-on-gnome-terminal">Note on gnome-terminal:</h2>
<p>gnome-terminal <a href="https://tomscii.sig7.se/zutty/test/output/gnome-terminal/vt_06_04.png">used to self-report &ldquo;VT525&rdquo;</a> in VTTEST (back in 2022,
with v3.30.2), but self-described as VT220-compatible. But now (2025,
v.3.48.3) seems to self-report as &ldquo;VT100&rdquo;. [Actually, I&rsquo;m wrong about
this. Or, it&rsquo;s complicated: it says &ldquo;VT100&rdquo; family on one screen, and
&ldquo;VT510&rdquo; on another.]</p>
<p>The Zutty post we&rsquo;ve been talking about <a href="https://tomscii.sig7.se/2020/12/A-totally-biased-comparison-of-Zutty#gnome-terminal">identifies a whole host of
bugs in gnome-terminal</a> and comments:</p>
<blockquote>
<p>[&hellip;there&rsquo;s] <em>a steaming pile of bugs</em> in various problem areas, some of
them pretty basic (such as TAB handling). Horizontal margins (VT420
and above) seem to be completely unsupported, as well as various other
sequences from VT420, VT520 and ISO-6429 (ECMA-48) – not entirely sure
if the latter is in scope. Overall, a puzzling outcome!</p>
<p>Digging a bit further, I discovered some ambiguity on whether
gnome-terminal supports the claimed VT520 level at all: its <a href="https://www.systutorials.com/docs/linux/man/1-gnome-terminal/">manpage</a>
(not organic to the project, but contributed by Debian) has this to
say about the support level of gnome-terminal: <code>Run any application that is designed to run on VT102, VT220, and xterm terminals.</code></p>
<p>That is pretty sloppy as far as specifications go. Apparently, it
might be the case that nothing above VT220 is intentionally supported
by gnome-terminal at all, and that its primary device attributes
<a href="https://tomscii.sig7.se/zutty/test/output/gnome-terminal/vt_06_04.png">response</a> is a <em>big fat lie</em>.</p>
</blockquote>
<p>Tom (of Zutty) refers to the <a href="https://invisible-island.net/xterm/xterm.faq.html#bug_gnometerm">xterm-faq&rsquo;s page on gnome-terminal</a> (and
gnome-terminal&rsquo;s underlying <a href="https://wiki.gnome.org/Apps/Terminal/VTE">VTE terminal widget library</a>) as well,
which documents extensive issues with gnome-terminal (and VTE) from
1999 onwards, summarising:</p>
<blockquote>
<p>Unless specifically mentioned, GNOME Terminal and VTE&rsquo;s issues
generally accumulate, with occasional veering off with skin-deep
“rewrites”. Each sighting provides a new episode.</p>
</blockquote>
<p>But now (06 Nov 2025: gnome-terminal v3.48.3) it seems to only
self-report in VTTEST as &ldquo;VT100 family&rdquo; in [4.] Primary Device
Attributes (DA):</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">Report is: &lt;27&gt; <span class="o">[</span> ? <span class="m">6</span> <span class="m">1</span> <span class="p">;</span> <span class="m">1</span> <span class="p">;</span> <span class="m">2</span> <span class="m">1</span> <span class="p">;</span> <span class="m">2</span> <span class="m">2</span> <span class="p">;</span> <span class="m">2</span> <span class="m">8</span> c  VT100 family
    <span class="nv">1</span> <span class="o">=</span> <span class="m">132</span> columns
    <span class="nv">21</span> <span class="o">=</span> horizontal scrolling
    <span class="nv">22</span> <span class="o">=</span> color
    <span class="nv">28</span> <span class="o">=</span> rectangular editing
</code></pre></div><p>But as &ldquo;VT510&rdquo; in [5.] Secondary Device Attributes (DA):</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">Testing Secondary Device Attributes <span class="o">(</span>Firmware version<span class="o">)</span>

          &lt;27&gt; <span class="o">[</span> &gt; <span class="m">6</span> <span class="m">1</span> <span class="p">;</span> <span class="m">7</span> <span class="m">8</span> <span class="m">0</span> <span class="m">2</span> <span class="p">;</span> <span class="m">1</span> c
         <span class="nv">Pp</span><span class="o">=</span><span class="m">61</span> <span class="o">(</span>VT510<span class="o">)</span>
         <span class="nv">Pv</span><span class="o">=</span>7802, firmware version 780.2
         <span class="nv">Pc</span><span class="o">=</span>1, ROM cartridge registration number ok
</code></pre></div><h2 id="update-10-july-2022">Update: 10 July 2022:</h2>
<p>Added <a href="https://wezfurlong.org/wezterm/">WezTerm</a> (another one written in Rust) which I haven&rsquo;t really had
a chance to play with much, but it has good VT feature levels. It
seems to have the same OpenGL requirements as Kitty or Alacritty. This
seems a very good choice overall, with a lot of cool features,
including implementing Kitty&rsquo;s display of images in terminal protocol
(along with iTerm2&rsquo;s image display protocol), and
scripting/extensibility in Lua.</p>
<h2 id="update-06-nov-2025">Update: 06 Nov 2025:</h2>
<p>Should really add <a href="https://ghostty.org">Ghostty</a> at some point (and update things in
general), which I was trying out for a while, but recent versions of
Ghostty require higher OpenGL-capable hardware than most of my
machines have.</p>
<p>(Added <a href="./#note-on-gnome-terminal">above note on gnome-terminal</a> as well.)</p>
<h2 id="update-21-dec-2025">Update: 21 Dec 2025:</h2>
<p>Updated corrections about gnome-terminal&rsquo;s two differing reports
(VT100-family in primary attributes, but VT510 in secondary
attributes).</p>
<p>Noticed a &ldquo;new&rdquo; terminal emulator, <a href="https://gitlab.gnome.org/chergert/ptyxis">ptyxis</a>, when using a Fedora Atomic
spin. And, indeed, it bills itself as a &ldquo;terminal for a
container-oriented desktop&rdquo;. It seems to report like gnome-terminal,
which makes sense as they&rsquo;re both really using Gnome&rsquo;s VTE (discussed above).</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/terminals" term="terminals" label="terminals" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Towards a history of Quake-style drop-down terminals]]></title>
            <link href="https://babbagefiles.xyz/quake-drop-down-terminal-history/"/>
            <id>https://babbagefiles.xyz/quake-drop-down-terminal-history/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2022-06-26T20:19:00-06:00</published>
            <updated>2022-06-26T20:55:25-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>Continued work on fooling Emacs into behaving like a drop-down console
(i.e. <a href="https://babbagefiles.xyz/categories/equake/">Equake</a>), set me to thinking about the development of
Quake-style drop-down terminals.</p>
<p>The frequent label &ldquo;Quake-style&rdquo; does seem to suggest at least part of
the origin in the computer game <a href="https://en.wikipedia.org/wiki/Quake_(video_game)">Quake</a> (1996), or at least that the drop-down console in
Quake was the most prominent/remembered example of this sort of UI.[<span class="org-target" id="org-target--ddt0t"></span><a href="#org-target--ddt0b">0</a>]</p>
<p>On Linux/Unix, a number of terminal emulators have been designed with
Quake-style drop-down interaction, and other platforms now seem to
have these as well. As far as I can tell, drop-down terminal emulators
first appeared on Linux, and probably in the early 2000s. (Though I
wonder about this, both the KDE project started in 1996 (same year as
Quake) and the GNOME project shortly after and it almost feels like
someone must have thought about doing something of this sort between
1996-2000.)</p>
<p>The earliest surviving drop-down terminals on Linux seem to be
<a href="https://github.com/KDE/yakuake">Yakuake</a> (QT/KDE), <a href="https://github.com/lanoxx/tilda">Tilda</a> (GTK), and <a href="https://github.com/Guake/guake">Guake</a> (GTK).</p>
<p>Yakuake seems to have been released in the early 2000s, certainly by
2005[<span class="org-target" id="org-target--ddt1t"></span><a href="#org-target--ddt1b">1</a>], but perhaps a bit earlier. Tilda was released by 2006[<span class="org-target" id="org-target--ddt2t"></span><a href="#org-target--ddt2b">2</a>]. Guake
was explicitly inspired by the original author seeing
Yakuake[<span class="org-target" id="org-target--ddt3t"></span><a href="#org-target--ddt3b">3</a>], itself being released in 2007.</p>
<p>But &ldquo;Yakuake&rdquo; stands for &ldquo;yet another Kuake&rdquo;, and indeed the earliest
drop-down terminal I can find on Linux is <a href="https://web.archive.org/web/20040219101722/http://www.nemohackers.org/kuake.php">Kuake</a>, released probably by 2003,
with the last update in 2004[<span class="org-target" id="org-target--ddt4t"></span><a href="#org-target--ddt4b">4</a>].</p>
<p>Happy to hear about further history of early drop-down Quake-style
terminals on any platform.</p>
<p>[<span class="org-target" id="org-target--ddt0b"></span><a href="#org-target--ddt0t">0</a>] <a href="https://en.wikipedia.org/wiki/Console_(computer_games)">https://en.wikipedia.org/wiki/Console_(computer_games)</a></p>
<p>[<span class="org-target" id="org-target--ddt1b"></span><a href="#org-target--ddt1t">1</a>] <a href="https://web.archive.org/web/20051016011941/http://yakuake.uv.ro/">https://web.archive.org/web/20051016011941/http://yakuake.uv.ro/</a></p>
<p>[<span class="org-target" id="org-target--ddt2b"></span><a href="#org-target--ddt2t">2</a>] <a href="https://web.archive.org/web/20061028182927/http://tilda.sourceforge.net/wiki/index.php/Main_Page">https://web.archive.org/web/20061028182927/http://tilda.sourceforge.net/wiki/index.php/Main_Page</a></p>
<p>[<span class="org-target" id="org-target--ddt3b"></span><a href="#org-target--ddt3t">3</a>] <a href="https://news.ycombinator.com/item?id=22524552">https://news.ycombinator.com/item?id=22524552</a></p>
<p>[<span class="org-target" id="org-target--ddt4b"></span><a href="#org-target--ddt4t">4</a>]
<a href="https://web.archive.org/web/20040219101722/http://www.nemohackers.org/kuake.php">https://web.archive.org/web/20040219101722/http://www.nemohackers.org/kuake.php</a></p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/terminals" term="terminals" label="terminals" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Dealing with possessed TrackPoints on later model ThinkPads]]></title>
            <link href="https://babbagefiles.xyz/possessed-trackpoint/"/>
            <id>https://babbagefiles.xyz/possessed-trackpoint/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2022-06-18T19:51:00-06:00</published>
            <updated>2022-06-18T20:22:51-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>On a new-to-me ThinkPad T440p, I&rsquo;ve had the worst time with the
TrackPoint.</p>
<p>First, the stock configuration has a horrible touchpad -
which shouldn&rsquo;t matter if you don&rsquo;t use the touchpad, but the
horribleness of it is that the physical buttons that should be on the
top of the touchpad, and are on the touchpads of models preceding and
following the **40 line, are not there. But one <a href="https://octoperf.com/blog/2018/11/07/thinkpad-t440p-buyers-guide/#trackpad">can replace it</a>, and so
I did.</p>
<p>The T440p is nice in that servicing the fan and other internals of the
machine is a relatively easy affair compared to say an X230. Just undo
two screws on the bottom of the laptop and slide off the back panel,
and you have access to memory, drives, the CPU, and so on. And so
swapping in a different CPU was really easy and painless.</p>
<p>On the other hand, changing the touchpad was a very involved
affair. But it <a href="https://www.youtube.com/watch?v=7wM4Kqdy3_E">can be done</a>.</p>
<p>However, still the mouse-cursor experience for the machine continued
to be horrible. Firstly, there was significant &ldquo;drift&rdquo; of the
TrackPoint. I.e., even once pressure is released, it keeps moving, for
a long time.</p>
<p>However, this ends up being solvable via</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">sudo -s <span class="nb">echo</span>  <span class="s1">&#39;ACTION==&#34;add&#34;,SUBSYSTEM==&#34;input&#34;,ATTR{name}==&#34;TPPS/2 IBM TrackPoint&#34;,ATTR{device/drift_time}=&#34;30&#34;&#39;</span>  &gt; /etc/udev/rules.d/10-trackpoint.rules
</code></pre></div><p>(If you&rsquo;re trying to do this in Guix, something like:</p>
<div class="highlight"><pre class="chroma"><code class="language-scheme" data-lang="scheme"><span class="p">(</span><span class="k">define </span><span class="nv">%trackpoint-drift-rule</span>
  <span class="p">(</span><span class="nf">udev-rule</span>
    <span class="s">&#34;10-trackpoint.rules&#34;</span>
    <span class="p">(</span><span class="nb">string-append </span><span class="s">&#34;ACTION==\&#34;add\&#34;,SUBSYSTEM==\&#34;input\&#34;,ATTR{name}==\&#34;TPPS/2 IBM TrackPoint\&#34;,ATTR{device/drift_time}=\&#34;25\&#34;&#34;</span><span class="p">))</span>
</code></pre></div><div class="highlight"><pre class="chroma"><code class="language-scheme" data-lang="scheme"><span class="p">(</span><span class="k">define </span><span class="nv">%my-desktop-services</span>
   <span class="p">(</span><span class="nb">cons </span><span class="p">(</span><span class="nf">udev-rules-service</span> <span class="ss">&#39;trackpoint-drift</span> <span class="nv">%trackpoint-drift-rule</span><span class="p">)</span>
         <span class="p">(</span><span class="nf">modify-services</span> <span class="nv">%desktop-services</span>
                          <span class="o">....</span><span class="p">)</span> <span class="c1">; other modifications here</span>
         <span class="p">))</span>
</code></pre></div><div class="highlight"><pre class="chroma"><code class="language-scheme" data-lang="scheme"><span class="p">(</span><span class="nf">operating-system</span>
  <span class="o">....</span>
  <span class="p">(</span><span class="nf">services</span>
    <span class="p">(</span><span class="nf">append</span>
     <span class="p">(</span><span class="nf">list</span>
      <span class="p">(</span><span class="nf">service</span> <span class="nv">openssh-service-type</span><span class="p">)</span>
      <span class="p">(</span><span class="nf">service</span> <span class="nv">cups-service-type</span><span class="p">)</span>
      <span class="p">(</span><span class="nf">service</span> <span class="nv">nix-service-type</span><span class="p">)</span>
      <span class="o">....</span><span class="p">)</span>
     <span class="nv">%my-desktop-services</span><span class="p">))</span>
  <span class="o">....</span><span class="p">)</span>
</code></pre></div><p>instead.)</p>
<p>But, unfortunately, this doesn&rsquo;t solve what is actually the most horrible issue:
the mouse cursor sometimes, when in use, just teleports around the edges
of the screen and starts randomly clicking on things.</p>
<p>I finally turned up some discussion of this issue (though not for the
T440p specifically) at: <a href="https://bugzilla.kernel.org/show_bug.cgi?format=multiple&amp;id=209167">https://bugzilla.kernel.org/show_bug.cgi?format=multiple&amp;id=209167</a>
(The discussion suggests that it should somehow be solved in the
kernel, but that is not my experience, even running kernel 5.17.13.)</p>
<p>And found that adding <code>psmouse.proto=imps</code> to the kernel arguments and
disabling the touchpad in the stock BIOS solves the &ldquo;possessed mouse
cursor&rdquo; issue. For better or worse, it seem to make the TrackPoint be
detected as a generic PS/2 mouse. Which means that <code>drift_time</code> can no
longer be set, and I do get a little bit of drift from time to time,
but it&rsquo;s not too bad and certainly is far less maddening than the
&ldquo;possessed mouse cursor&rdquo; behaviour.</p>
<p>For Guix, the way to implement this is something along the lines of:</p>
<div class="highlight"><pre class="chroma"><code class="language-scheme" data-lang="scheme"><span class="p">(</span><span class="nf">operating-system</span>
 <span class="o">...</span>
 <span class="p">(</span><span class="nf">kernel-arguments</span> <span class="p">(</span><span class="nf">cons*</span> <span class="s">&#34;modprobe.blacklist=pcspkr,snd_pcsp&#34;</span> <span class="s">&#34;psmouse.proto=imps&#34;</span> <span class="s">&#34;acpi_osi=Linux&#34;</span> <span class="nv">%default-kernel-arguments</span><span class="p">))</span>
 <span class="o">...</span><span class="p">)</span>
</code></pre></div><p>(The other kernel arguments are simply the other ones I use, and are
not directly connected with this issue.)</p>
<p>Depending on the model of ThinkPad and/or the environment, using
<code>psmouse.proto=bare</code> instead may work better (see discussion at:
<a href="https://www.reddit.com/r/thinkpad/comments/v7vn0o/thinkpad_t440p_trackpoint_occasionally_going_crazy/icjkk0o/">https://www.reddit.com/r/thinkpad/comments/v7vn0o/thinkpad_t440p_trackpoint_occasionally_going_crazy/icjkk0o/</a>
).</p>
<p>So, perhaps not an entirely satisfactory solution, this impish
exorcism, but better than alternatives.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/thinkpad" term="thinkpad" label="thinkpad" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/linux" term="linux" label="linux" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/guix" term="guix" label="guix" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Equake: A Geas on Gnomish Smiths]]></title>
            <link href="https://babbagefiles.xyz/equake-geas-on-gnomish-smiths/"/>
            <id>https://babbagefiles.xyz/equake-geas-on-gnomish-smiths/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2022-06-08T10:54:00-06:00</published>
            <updated>2022-06-26T20:20:14-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>A new version of <a href="https://gitlab.com/emacsomancer/equake">Equake</a>, the drop-down &ldquo;terminal emulator&rdquo; for Emacs, should be hitting Melpa shortly. This version includes a number of bug fixes, and some new features.</p>
<p><a href="https://github.com/jeffkowalski">Jeff Kowalski</a> added code for a <a href="https://gitlab.com/emacsomancer/equake/-/merge_requests/8">&ldquo;close Equake frame on loss of focus
feature&rdquo;</a> (similar to the <a href="https://github.com/lanoxx/tilda">Tilda</a> feature) and a number of bug fixes and code-cleanup.</p>
<p>Further: I&rsquo;m (half-)jokingly calling this the <strong>Geas on Gnomish Smiths</strong> release as I&rsquo;ve finally figured out how to make it behave properly under GNOME Shell Wayland.</p>
<p>To my knowledge, the only other currently working drop-down terminal
for GNOME Shell Wayland is the JavaScript GNOME extension
<a href="https://github.com/amezin/gnome-shell-extension-ddterm">ddterm</a>. Wayland (at least GNOME Shell&rsquo;s Wayland) seems very
restrictive in how non-user-initiated events can affect window
properties. E.g., it is difficult to programmatically affect window focus in GNOME Shell under Wayland. The default &ldquo;hide window&rdquo; behaviour of Equake doesn&rsquo;t work.</p>
<p>However, I did have a legacy feature where closing/hiding Equake involved destroying the frame rather than hiding it. This is somewhat complicated, as re-invoking Equake then involves creating a new frame and restoring the tabs and buffer history. This is how Equake originally worked, but it is simpler (and slightly faster) just to hide the frame on &ldquo;close&rdquo; and then unhide it on &ldquo;re-open&rdquo;, and this became the default behaviour for Equake some time ago. But I left the old code in place as another option.</p>
<p>However, various changes apparently ended up with the restoration behaviour not working quite properly. I have fixed this (more or less, see <a href="https://gitlab.com/emacsomancer/equake/-/issues/26">https://gitlab.com/emacsomancer/equake/-/issues/26</a>). With that fix in place, we can then trick GNOME Shell under Wayland into behaving properly. Rather than hiding the Equake frame on &ldquo;close&rdquo;, we destroy it and then recreate it on &ldquo;open&rdquo;. This involves quite a bit of trickery behind the scenes. Essentially, opening a new window under GNOME Shell Wayland can result in that window being placed on top of any existing windows (though to do it properly, employing a it of Elisp <code>(select-frame-set-input-focus (selected-frame))</code> seems to result in the new Emacs client frame being on top (and focussed) [this is a potentially useful trick for opening regular new Emacsclient frames as well].</p>
<p>But, the usual way that Equake frames are created internally is via
Emacs <code>make-frame</code>, which doesn&rsquo;t automatically focus the new
frame. So rather than doing this, we need to have the user call <code>emacsclient -c</code> and then transform that frame into an Equake frame. A new Equake function <code>equake--transform-existing-frame-into-equake-frame</code> takes care of this. The transformation and the old code restoring tabs and history doesn&rsquo;t quite take care of everything though as the point/cursor is left by default at the top of the frame, which is not the user-expected behaviour. So an addition bit of elisp <code>(goto-char (1- (point-max)))</code> generally moves the point/cursor to the end of the buffer where the user might expect it.</p>
<p>So I have a bit of shell script as a helper function which is what one should create a keybinding for in GNOME Shell Wayland:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="cp">#!/bin/sh
</span><span class="cp"></span>
<span class="nv">equakestatus</span><span class="o">=</span><span class="k">$(</span>emacsclient -n -e <span class="s1">&#39;(frame-live-p (alist-get (equake--get-monitor) equake--frame))&#39;</span><span class="k">)</span>

<span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$equakestatus</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;nil&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
    emacsclient -c -e <span class="s2">&#34;(progn (select-frame-set-input-focus (selected-frame))
</span><span class="s2">                              (equake--transform-existing-frame-into-equake-frame)
</span><span class="s2">                              (goto-char (1- (point-max))))&#34;</span>
<span class="k">else</span>
    emacsclient -n -e <span class="s1">&#39;(progn (setq equake-use-frame-hide nil)
</span><span class="s1">                              (equake-invoke))&#39;</span>
<span class="k">fi</span>
</code></pre></div><p>This checks if there is a live Equake frame; if there is, it is hidden; otherwise a new frame is created and transformed.</p>
<p>Finally, in order to be able to get the &ldquo;Always on Top&rdquo; behaviour, the new Equake frame transmutation function calls <code>(shell-command &quot;wmctrl -r :ACTIVE: -b toggle,above&quot;)</code> (so one needs <code>wmctrl</code> installed), which seems to trigger the &ldquo;Always on Top&rdquo; feature at least for Xwayland windows.</p>
<p>And, presto, change-o, voilà, we have a version of Equake which
exhibits its normal X11 behaviour in GNOME Shell Wayland.</p>
<p>(As long as Emacs is run as an Xwayland application; otherwise the
&ldquo;always on top&rdquo; behaviour doesn&rsquo;t work properly.)</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/equake" term="equake" label="equake" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/elisp" term="elisp" label="elisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/eshell" term="eshell" label="eshell" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Social network platforms (IRC, Matrix, Discord, Slack, and others)]]></title>
            <link href="https://babbagefiles.xyz/tridactyl/"/>
            <id>https://babbagefiles.xyz/tridactyl/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2021-10-27T14:22:23-06:00</published>
            <updated>2025-03-29T22:05:05-05:00</updated>
            
            
            <content type="html"><![CDATA[<p>Following on the last post</p>
<h2 id="lisp">Lisp</h2>
<ul>
<li>Common Lisp:
<ul>
<li>&hellip;</li>
</ul>
</li>
<li></li>
</ul>
<!--listend-->
<ul>
<li>Clojure: slack
<ul>
<li>Racket: slack</li>
</ul>
</li>
</ul>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/irc" term="irc" label="irc" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/socialmedia" term="socialmedia" label="socialmedia" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/" term="" label="" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Automatically adding information to Org-roam file properties]]></title>
            <link href="https://babbagefiles.xyz/org-roam-auto-propertise/"/>
            <id>https://babbagefiles.xyz/org-roam-auto-propertise/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2021-08-10T21:12:00-06:00</published>
            <updated>2021-08-10T21:15:45-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>This expands on a feature I included in the setup for using <a href="https://www.orgroam.com/">Org-roam</a>
on Android/LineageOS in the <a href="../org-roam-on-android">last post</a>, specifically automatically
adding properties to newly created Org-roam files.</p>
<p>Since Org-roam v2 creates a top properties drawer (with an <code>:ID:</code> tag) anyway, it is nice to stick other information there as well. Specifically, information that could be useful in some situation, but which usually we don&rsquo;t want to see, like <code>:AUTHOR:</code> (it&rsquo;s probably you, and you know who you are), <code>:CREATION_TIME:</code> (and why not use Unix epoch time?), and so on. I have org drawers fold themselves automatically, so the normally-useless information doesn&rsquo;t distract me.</p>
<p>We can do this by leveraging Org-roam&rsquo;s <code>org-roam-capture-new-node-hook</code>, and some <code>org-roam-add-property</code> function calls, as below.</p>
<p>But, while we&rsquo;re at it, we might also record <strong>where</strong> a note was made from. There are a number of ways we might do this, but an easy one (only requiring <code>curl</code> and an active Internet connection) is using <a href="https://ipinfo.io/developers/data-types#geolocation-data">ipinfo.io</a>. <code>curl ipinfo.io</code> will give you a bunch of information in JSON format about your internet provider, including latitude and longitude, which will likely be at least somewhere <strong>near</strong> your present location. And <code>curl ipinfo.io/loc</code> will return just latitude,longitude.</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp">  <span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/add-other-auto-props-to-org-roam-properties</span> <span class="p">()</span>
    <span class="c1">;; if the file already exists, don&#39;t do anything, otherwise...</span>
    <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nf">file-exists-p</span> <span class="p">(</span><span class="nf">buffer-file-name</span><span class="p">))</span>
      <span class="c1">;; if there&#39;s also a CREATION_TIME property, don&#39;t modify it</span>
      <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">org-find-property</span> <span class="s">&#34;CREATION_TIME&#34;</span><span class="p">)</span>
        <span class="c1">;; otherwise, add a Unix epoch timestamp for CREATION_TIME prop</span>
        <span class="c1">;; (this is what &#34;%s&#34; does - see http://doc.endlessparentheses.com/Fun/format-time-string )</span>
        <span class="p">(</span><span class="nv">org-roam-add-property</span>
         <span class="p">(</span><span class="nf">format-time-string</span> <span class="s">&#34;%s&#34;</span>
                             <span class="p">(</span><span class="nf">nth</span> <span class="mi">5</span>
                                  <span class="p">(</span><span class="nf">file-attributes</span> <span class="p">(</span><span class="nf">buffer-file-name</span><span class="p">))))</span>
         <span class="s">&#34;CREATION_TIME&#34;</span><span class="p">))</span>
      <span class="c1">;; similarly for AUTHOR and MAIL properties</span>
      <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">org-find-property</span> <span class="s">&#34;AUTHOR&#34;</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">org-roam-add-property</span> <span class="nv">roam-user</span> <span class="s">&#34;AUTHOR&#34;</span><span class="p">))</span>
      <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">org-find-property</span> <span class="s">&#34;MAIL&#34;</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">org-roam-add-property</span> <span class="nv">roam-email</span> <span class="s">&#34;MAIL&#34;</span><span class="p">))</span>
      <span class="c1">;; also add the latitude and longitude</span>
      <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">org-find-property</span> <span class="s">&#34;LAT_LONG&#34;</span><span class="p">)</span>
        <span class="c1">;; recheck location:</span>
        <span class="p">(</span><span class="nv">bms/get-lat-long-from-ipinfo</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">org-roam-add-property</span> <span class="p">(</span><span class="nf">concat</span> <span class="p">(</span><span class="nf">number-to-string</span> <span class="nv">calendar-latitude</span><span class="p">)</span> <span class="s">&#34;,&#34;</span> <span class="p">(</span><span class="nf">number-to-string</span> <span class="nv">calendar-longitude</span><span class="p">))</span> <span class="s">&#34;LAT-LONG&#34;</span><span class="p">))))</span>

  <span class="c1">;; hook to be run whenever an org-roam capture completes</span>
  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;org-roam-capture-new-node-hook</span> <span class="nf">#&#39;</span><span class="nv">bms/add-other-auto-props-to-org-roam-properties</span><span class="p">)</span>

<span class="c1">;; function to find latitude &amp; longitude</span>
<span class="c1">;;                      (requires curl to be installed on system)</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">calendar-latitude</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">calendar-longitude</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/get-lat-long-from-ipinfo</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">let*</span>
      <span class="p">((</span><span class="nv">latlong</span> <span class="p">(</span><span class="nf">substring</span>
                 <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;curl -s &#39;ipinfo.io/loc&#39;&#34;</span><span class="p">)</span>
                   <span class="mi">0</span> <span class="mi">-1</span><span class="p">))</span>
       <span class="p">(</span><span class="nv">latlong-list</span> <span class="p">(</span><span class="nv">split-string</span> <span class="nv">latlong</span> <span class="s">&#34;,&#34;</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">setq</span> <span class="nv">calendar-latitude</span> <span class="p">(</span><span class="nf">string-to-number</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">latlong-list</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">setq</span> <span class="nv">calendar-longitude</span> <span class="p">(</span><span class="nf">string-to-number</span> <span class="p">(</span><span class="nv">cadr</span> <span class="nv">latlong-list</span><span class="p">)))))</span>
</code></pre></div><p>You might also calculate/set <code>calendar-latitude</code> and <code>calendar-longitude</code> in other ways. Including just hard-coding them for stationary machines. On Android, we could in theory make use of the Termux command <code>termux-location</code>, which queries the device&rsquo;s GPS. But unfortunately it doesn&rsquo;t always work (if it can&rsquo;t find a good connection to a GPS satellite) and even when it does work it&rsquo;s slow, so it&rsquo;s not something you&rsquo;d want to call every time you made a note. <a href="https://gitlab.freedesktop.org/geoclue/geoclue/-/wikis/home">GeoClue</a> would be another possible source.</p>
<p>(If you&rsquo;re using a VPN, you&rsquo;ll want to escape from it somehow to get something closer to your real location. How you do this will vary based on your VPN provider and other factors. (If you&rsquo;re calling from Emacs, and you use something like Mullvad, you may want to revise the <code>shell-command-to-string</code> to call up a bash session/script, then exclude that specific bash session/script from the VPN, and <em>then</em> call <code>curl</code>, so that the call references your &ldquo;real&rdquo; IP. E.g. if you&rsquo;re using Mullvad, then:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="cp">#!/bin/bash
</span><span class="cp"></span><span class="nv">PID</span><span class="o">=</span><span class="sb">`</span><span class="nb">echo</span> <span class="nv">$$</span><span class="sb">`</span>
mullvad split-tunnel pid add <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PID</span><span class="si">}</span><span class="s2">&#34;</span>
curl ipinfo.io/loc <span class="c1"># for lat/long ; `curl ipinfo.io` for full info</span>
</code></pre></div><p>might give you a start on something.))</p>
<p>Let me know if you think of other properties that could be useful to automatically add to Org-roam file properties.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/org" term="org" label="org" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/orgroam" term="orgroam" label="orgroam" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Org-roam on Android]]></title>
            <link href="https://babbagefiles.xyz/org-roam-on-android/"/>
            <id>https://babbagefiles.xyz/org-roam-on-android/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2021-08-09T20:06:00-05:00</published>
            <updated>2025-02-03T11:32:00-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>I&rsquo;ve been using the note-taking <a href="https://en.wikipedia.org/wiki/Zettelkasten">Zettelkasten</a>-ish <a href="https://www.orgroam.com/">Org-roam</a> system for a
few months and it&rsquo;s been very useful to me, just as a low-friction way
of making more notes and easily finding and/or (re)discovering notes
that I&rsquo;ve made.</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/Screenshot_20210809-201713_Termux.png"/> </figure>

<p>It&rsquo;s pretty useful to be able to have access to these notes, and be
able to quickly add notes, on mobile as well. I thought it might be
useful to include here some notes on how to do, since (especially
since <a href="https://blog.jethro.dev/posts/org_roam_v2/">v2 of Org-roam</a>) there are some hurdles.</p>
<p>On Android/LineageOS install the <a href="https://f-droid.org/">F-Droid app store</a>, and then from
there install <a href="https://f-droid.org/en/packages/com.termux/">Termux</a>. Open Termux and install four things we&rsquo;ll need
(strictly speaking you don&rsquo;t need curl and ripgrep, but they&rsquo;ll
be useful): Emacs, sqlite, curl, and ripgrep via <code>pkg install emacs sqlite curl ripgrep</code>.</p>
<p>You can then open up Emacs via <code>emacs</code> and get started.</p>
<p>I include here a commented partial version of my <code>~/.emacs.d/init.el</code>
configuration file for my Termux/Emacs Org-roam setup:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; BASIC SETUP:</span>

<span class="c1">;; package setup - bootstrap the package system</span>
<span class="p">(</span><span class="nb">require</span> <span class="ss">&#39;package</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">package-enable-at-startup</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">gnutls-algorithm-priority</span> <span class="s">&#34;NORMAL:-VERS-TLS1.3&#34;</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">package-archives</span>
      <span class="o">&#39;</span><span class="p">((</span><span class="s">&#34;GNU ELPA&#34;</span>     <span class="o">.</span> <span class="s">&#34;https://elpa.gnu.org/packages/&#34;</span><span class="p">)</span>
        <span class="p">(</span><span class="s">&#34;ORG&#34;</span>		<span class="o">.</span> <span class="s">&#34;https://orgmode.org/elpa/&#34;</span><span class="p">)</span>
        <span class="p">(</span><span class="s">&#34;MELPA Stable&#34;</span> <span class="o">.</span> <span class="s">&#34;https://stable.melpa.org/packages/&#34;</span><span class="p">)</span>
        <span class="p">(</span><span class="s">&#34;MELPA&#34;</span>        <span class="o">.</span> <span class="s">&#34;https://melpa.org/packages/&#34;</span><span class="p">))</span>
      <span class="nv">package-archive-priorities</span>
      <span class="o">&#39;</span><span class="p">((</span><span class="s">&#34;ORG&#34;</span>		<span class="o">.</span> <span class="mi">20</span><span class="p">)</span>
        <span class="p">(</span><span class="s">&#34;MELPA&#34;</span>        <span class="o">.</span> <span class="mi">15</span><span class="p">)</span>
        <span class="p">(</span><span class="s">&#34;MELPA Stable&#34;</span> <span class="o">.</span> <span class="mi">10</span><span class="p">)</span>
        <span class="p">(</span><span class="s">&#34;GNU ELPA&#34;</span>     <span class="o">.</span> <span class="mi">5</span><span class="p">)))</span>

<span class="p">(</span><span class="nv">package-initialize</span><span class="p">)</span>

<span class="c1">;; Bootstrap `use-package&#39;</span>
<span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">package-installed-p</span> <span class="ss">&#39;use-package</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">package-refresh-contents</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">package-install</span> <span class="ss">&#39;use-package</span><span class="p">))</span>

<span class="p">(</span><span class="nb">eval-when-compile</span>
  <span class="p">(</span><span class="nb">require</span> <span class="ss">&#39;use-package</span><span class="p">))</span>

<span class="c1">;; for Termux-specific things; useful if you want to share</span>
<span class="c1">;; configs across platforms</span>
<span class="p">(</span><span class="nb">defvar</span> <span class="nv">termux-p</span>
  <span class="p">(</span><span class="nv">not</span> <span class="p">(</span><span class="nf">null</span> <span class="p">(</span><span class="nv">getenv</span> <span class="s">&#34;ANDROID_ROOT&#34;</span><span class="p">)))</span>
  <span class="s">&#34;If non-nil, GNU Emacs is running on Termux.&#34;</span><span class="p">)</span>

<span class="p">(</span><span class="nb">when</span> <span class="nv">termux-p</span>
  <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">package-installed-p</span> <span class="ss">&#39;use-package</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">package-refresh-contents</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">package-install</span> <span class="ss">&#39;use-package</span><span class="p">)))</span>

<span class="c1">;; This makes Emacs in Termux use your Android browser for opening urls</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">browse-url-browser-function</span> <span class="ss">&#39;browse-url-xdg-open</span><span class="p">)</span>

<span class="c1">;; mouse</span>
<span class="c1">;; enable mouse reporting for terminal emulators</span>
<span class="c1">;; this lets you scroll around by swiping</span>
<span class="p">(</span><span class="nb">unless</span> <span class="nf">window-system</span>
  <span class="p">(</span><span class="nv">xterm-mouse-mode</span> <span class="mi">1</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">[</span><span class="nv">mouse-4</span><span class="p">]</span> <span class="p">(</span><span class="nb">lambda</span> <span class="p">()</span>
                              <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
                              <span class="p">(</span><span class="nf">scroll-down</span> <span class="mi">1</span><span class="p">)))</span>
  <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">[</span><span class="nv">mouse-5</span><span class="p">]</span> <span class="p">(</span><span class="nb">lambda</span> <span class="p">()</span>
                              <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
                              <span class="p">(</span><span class="nf">scroll-up</span> <span class="mi">1</span><span class="p">))))</span>

<span class="c1">;; ORG</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">org</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:ensure</span> <span class="nv">org-plus-contrib</span>
  <span class="nb">:init</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-src-fontify-natively</span> <span class="no">t</span><span class="p">)</span>
  <span class="nb">:config</span>
  <span class="c1">;; (add-to-list &#39;auto-mode-alist &#39;(&#34;\\.org\\&#39;&#34; . org-mode))</span>
  <span class="p">(</span><span class="nf">define-key</span> <span class="nv">org-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;M-p&#34;</span><span class="p">)</span> <span class="ss">&#39;org-metaup</span><span class="p">)</span>
  <span class="p">(</span><span class="nf">define-key</span> <span class="nv">org-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;M-n&#34;</span><span class="p">)</span> <span class="ss">&#39;org-metadown</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-catch-invisible-edits</span> <span class="ss">&#39;show-and-error</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-cycle-separator-lines</span> <span class="mi">-1</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-return-follows-link</span> <span class="no">t</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-export-with-toc</span> <span class="ss">&#39;nil</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-startup-folded</span> <span class="ss">&#39;content</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-ellipsis</span> <span class="s">&#34;⇣&#34;</span><span class="p">)</span>
  <span class="c1">;; **** use regular android apps to view pdfs &amp; images *****</span>
  <span class="p">(</span><span class="nb">when</span> <span class="nv">termux-p</span>
    <span class="p">(</span><span class="nv">add-to-list</span> <span class="ss">&#39;org-file-apps</span> <span class="o">&#39;</span><span class="p">(</span><span class="s">&#34;\\.pdf\\&#39;&#34;</span> <span class="o">.</span> <span class="s">&#34;termux-open %s&#34;</span><span class="p">))</span>
    <span class="p">(</span><span class="nv">add-to-list</span> <span class="ss">&#39;org-file-apps</span> <span class="o">&#39;</span><span class="p">(</span><span class="s">&#34;\\.png\\&#39;&#34;</span> <span class="o">.</span> <span class="s">&#34;termux-open %s&#34;</span><span class="p">))</span>
    <span class="p">(</span><span class="nv">add-to-list</span> <span class="ss">&#39;org-file-apps</span> <span class="o">&#39;</span><span class="p">(</span><span class="s">&#34;\\.jpg\\&#39;&#34;</span> <span class="o">.</span> <span class="s">&#34;termux-open %s&#34;</span><span class="p">))</span>
    <span class="p">(</span><span class="nv">add-to-list</span> <span class="ss">&#39;org-file-apps</span> <span class="o">&#39;</span><span class="p">(</span><span class="s">&#34;\\.jpeg\\&#39;&#34;</span> <span class="o">.</span> <span class="s">&#34;termux-open %s&#34;</span><span class="p">)))</span>
  <span class="c1">;; needed for &lt;s etc. expansion of code-blocks</span>
  <span class="p">(</span><span class="nb">require</span> <span class="ss">&#39;org-tempo</span><span class="p">))</span>

<span class="c1">;; define our Org-roam user and their email (set to your desired name/email)</span>
<span class="p">(</span><span class="nb">defvar</span> <span class="nv">roam-user</span> <span class="s">&#34;Some User&#34;</span>
  <span class="s">&#34;The name of the Org-roam note author.&#34;</span><span class="p">)</span>
<span class="p">(</span><span class="nb">defvar</span> <span class="nv">roam-email</span> <span class="s">&#34;roman@mode.org&#34;</span>
  <span class="s">&#34;The public email of that author.&#34;</span><span class="p">)</span>

<span class="p">(</span><span class="nb">setq</span> <span class="nv">org-roam-v2-ack</span> <span class="no">t</span><span class="p">)</span>

<span class="c1">;; we need this package for v2 of Org-oram</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">emacsql-sqlite3</span>
  <span class="nb">:ensure</span> <span class="no">t</span><span class="p">)</span>

<span class="c1">;; If you&#39;ve replicated my setup; otherwise change to the Termux</span>
<span class="c1">;; local path.</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">org-roam-directory</span> <span class="p">(</span><span class="nv">file-truename</span> <span class="s">&#34;~/Documents/Org/org-roam/&#34;</span><span class="p">))</span>

<span class="c1">;; org-roam</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">org-roam</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:custom</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-roam-db-location</span> <span class="p">(</span><span class="nv">file-truename</span> <span class="s">&#34;~&#34;</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">org-roam-directory</span> <span class="p">(</span><span class="nv">file-truename</span> <span class="s">&#34;~/Documents/Org/org-roam/&#34;</span><span class="p">))</span>
  <span class="nb">:bind</span> <span class="p">((</span><span class="s">&#34;C-c n l&#34;</span> <span class="o">.</span> <span class="nv">org-roam-buffer-toggle</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c n f&#34;</span> <span class="o">.</span> <span class="nv">org-roam-node-find</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c n r&#34;</span> <span class="o">.</span> <span class="nv">org-roam-graph</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c n i&#34;</span> <span class="o">.</span> <span class="nv">org-roam-node-insert</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c n c&#34;</span> <span class="o">.</span> <span class="nv">org-roam-capture</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c n g&#34;</span> <span class="o">.</span> <span class="nv">org-id-get-create</span><span class="p">)</span>
         <span class="c1">;; Dailies</span>
         <span class="p">(</span><span class="s">&#34;C-c n n&#34;</span> <span class="o">.</span> <span class="nv">org-roam-dailies-capture-today</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c n d&#34;</span> <span class="o">.</span> <span class="nv">org-roam-dailies-goto-today</span><span class="p">)</span> <span class="c1">; find toDay</span>
         <span class="p">(</span><span class="s">&#34;C-c n v&#34;</span> <span class="o">.</span> <span class="nv">org-roam-dailies-goto-date</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c n f&#34;</span> <span class="o">.</span> <span class="nv">org-roam-dailies-goto-next-note</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c n b&#34;</span> <span class="o">.</span> <span class="nv">org-roam-dailies-goto-previous-note</span><span class="p">))</span>
  <span class="nb">:config</span>

  <span class="c1">;; this is a chunglak&#39;s hack to get sqlite to work on Android with org-roam v2:</span>
  <span class="c1">;; from: https://github.com/org-roam/org-roam/issues/1605#issuecomment-885997237</span>
  <span class="p">(</span><span class="nb">defun</span> <span class="nv">org-roam-db</span> <span class="p">()</span>
    <span class="s">&#34;Entrypoint to the Org-roam sqlite database.
</span><span class="s">Initializes and stores the database, and the database connection.
</span><span class="s">Performs a database upgrade when required.&#34;</span>
    <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nb">and</span> <span class="p">(</span><span class="nv">org-roam-db--get-connection</span><span class="p">)</span>
                 <span class="p">(</span><span class="nv">emacsql-live-p</span> <span class="p">(</span><span class="nv">org-roam-db--get-connection</span><span class="p">)))</span>
      <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">init-db</span> <span class="p">(</span><span class="nv">not</span> <span class="p">(</span><span class="nf">file-exists-p</span> <span class="nv">org-roam-db-location</span><span class="p">))))</span>
        <span class="p">(</span><span class="nv">make-directory</span> <span class="p">(</span><span class="nf">file-name-directory</span> <span class="nv">org-roam-db-location</span><span class="p">)</span> <span class="no">t</span><span class="p">)</span>
        <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">conn</span> <span class="p">(</span><span class="nv">emacsql-sqlite3</span> <span class="nv">org-roam-db-location</span><span class="p">)))</span>
          <span class="p">(</span><span class="nv">emacsql</span> <span class="nv">conn</span> <span class="p">[</span><span class="nb">:pragma</span> <span class="p">(</span><span class="nf">=</span> <span class="nv">foreign_keys</span> <span class="nv">ON</span><span class="p">)])</span>
          <span class="p">(</span><span class="nf">set-process-query-on-exit-flag</span> <span class="p">(</span><span class="nv">emacsql-process</span> <span class="nv">conn</span><span class="p">)</span> <span class="no">nil</span><span class="p">)</span>
          <span class="p">(</span><span class="nf">puthash</span> <span class="p">(</span><span class="nf">expand-file-name</span> <span class="nv">org-roam-directory</span><span class="p">)</span>
                   <span class="nv">conn</span>
                   <span class="nv">org-roam-db--connection</span><span class="p">)</span>
          <span class="p">(</span><span class="nb">when</span> <span class="nv">init-db</span>
            <span class="p">(</span><span class="nv">org-roam-db--init</span> <span class="nv">conn</span><span class="p">))</span>
          <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">version</span> <span class="p">(</span><span class="nv">caar</span> <span class="p">(</span><span class="nv">emacsql</span> <span class="nv">conn</span> <span class="s">&#34;PRAGMA user_version&#34;</span><span class="p">)))</span>
                 <span class="p">(</span><span class="nv">version</span> <span class="p">(</span><span class="nv">org-roam-db--upgrade-maybe</span> <span class="nv">conn</span> <span class="nv">version</span><span class="p">)))</span>
            <span class="p">(</span><span class="nb">cond</span>
             <span class="p">((</span><span class="nf">&gt;</span> <span class="nv">version</span> <span class="nv">org-roam-db-version</span><span class="p">)</span>
              <span class="p">(</span><span class="nv">emacsql-close</span> <span class="nv">conn</span><span class="p">)</span>
              <span class="p">(</span><span class="ne">user-error</span>
               <span class="s">&#34;The Org-roam database was created with a newer Org-roam version.  &#34;</span>
               <span class="s">&#34;You need to update the Org-roam package&#34;</span><span class="p">))</span>
             <span class="p">((</span><span class="nf">&lt;</span> <span class="nv">version</span> <span class="nv">org-roam-db-version</span><span class="p">)</span>
              <span class="p">(</span><span class="nv">emacsql-close</span> <span class="nv">conn</span><span class="p">)</span>
              <span class="p">(</span><span class="ne">error</span> <span class="s">&#34;BUG: The Org-roam database scheme changed %s&#34;</span>
                     <span class="s">&#34;and there is no upgrade path&#34;</span><span class="p">)))))))</span>
    <span class="p">(</span><span class="nv">org-roam-db--get-connection</span><span class="p">))</span>
  <span class="p">(</span><span class="nb">defun</span> <span class="nv">org-roam-db--init</span> <span class="p">(</span><span class="nv">db</span><span class="p">)</span>
    <span class="s">&#34;Initialize database DB with the correct schema and user version.&#34;</span>
    <span class="p">(</span><span class="nv">emacsql-with-transaction</span> <span class="nv">db</span>
      <span class="p">(</span><span class="nv">emacsql</span> <span class="nv">db</span> <span class="s">&#34;PRAGMA foreign_keys = ON&#34;</span><span class="p">)</span> <span class="c1">;; added</span>
      <span class="p">(</span><span class="nv">emacsql</span> <span class="nv">db</span> <span class="p">[</span><span class="nb">:pragma</span> <span class="p">(</span><span class="nf">=</span> <span class="nv">foreign_keys</span> <span class="nv">ON</span><span class="p">)])</span>
      <span class="p">(</span><span class="nb">pcase-dolist</span> <span class="p">(</span><span class="o">`</span><span class="p">(</span><span class="o">,</span><span class="nv">table</span> <span class="o">,</span><span class="nv">schema</span><span class="p">)</span> <span class="nv">org-roam-db--table-schemata</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">emacsql</span> <span class="nv">db</span> <span class="p">[</span><span class="nb">:create-table</span> <span class="nv">$i1</span> <span class="nv">$S2</span><span class="p">]</span> <span class="nv">table</span> <span class="nv">schema</span><span class="p">))</span>
      <span class="p">(</span><span class="nb">pcase-dolist</span> <span class="p">(</span><span class="o">`</span><span class="p">(</span><span class="o">,</span><span class="nv">index-name</span> <span class="o">,</span><span class="nv">table</span> <span class="o">,</span><span class="nv">columns</span><span class="p">)</span> <span class="nv">org-roam-db--table-indices</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">emacsql</span> <span class="nv">db</span> <span class="p">[</span><span class="nb">:create-index</span> <span class="nv">$i1</span> <span class="nb">:on</span> <span class="nv">$i2</span> <span class="nv">$S3</span><span class="p">]</span> <span class="nv">index-name</span> <span class="nv">table</span> <span class="nv">columns</span><span class="p">))</span>
      <span class="p">(</span><span class="nv">emacsql</span> <span class="nv">db</span> <span class="p">(</span><span class="nf">format</span> <span class="s">&#34;PRAGMA user_version = %s&#34;</span> <span class="nv">org-roam-db-version</span><span class="p">))))</span>
  <span class="c1">;; end chunglak hack</span>

  <span class="p">(</span><span class="nv">org-roam-setup</span><span class="p">)</span>
  <span class="c1">;; If using org-roam-protocol</span>
  <span class="p">(</span><span class="nb">require</span> <span class="ss">&#39;org-roam-protocol</span><span class="p">))</span>

<span class="c1">;; These are my capture templates:</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">org-roam-capture-templates</span>
      <span class="o">`</span><span class="p">((</span><span class="s">&#34;d&#34;</span> <span class="s">&#34;default&#34;</span> <span class="nv">plain</span> <span class="s">&#34;%?&#34;</span> <span class="nb">:if-new</span>
         <span class="p">(</span><span class="nv">file+head</span> <span class="s">&#34;%&lt;%Y%m%d%H%M%S&gt;-${slug}.org&#34;</span>
                    <span class="o">,</span><span class="p">(</span><span class="nf">concat</span> <span class="s">&#34;#+title: ${title}\n&#34;</span>
                             <span class="s">&#34;#+date: %U\n\n&#34;</span><span class="p">))</span>
         <span class="nb">:unnarrowed</span> <span class="no">t</span><span class="p">)))</span>

<span class="p">(</span><span class="nb">setq</span> <span class="nv">org-roam-dailies-directory</span> <span class="s">&#34;~/Documents/Org/org-roam/daily&#34;</span><span class="p">)</span>

<span class="p">(</span><span class="nb">setq</span> <span class="nv">org-roam-dailies-capture-templates</span>
      <span class="o">`</span><span class="p">((</span><span class="s">&#34;d&#34;</span> <span class="s">&#34;default&#34;</span> <span class="nv">entry</span> <span class="s">&#34;* %?&#34;</span> <span class="nb">:if-new</span>
         <span class="p">(</span><span class="nv">file+head</span> <span class="s">&#34;%(concat org-roam-dailies-directory \&#34;/%&lt;%Y-%m-%d&gt;.org\&#34;)&#34;</span>
                    <span class="o">,</span><span class="p">(</span><span class="nf">concat</span> <span class="s">&#34;#+title: %&lt;%Y-%m-%d&gt;&#34;</span> <span class="s">&#34;\n&#34;</span>
                             <span class="s">&#34;#+filetags: :daily_journal:\n\n&#34;</span><span class="p">)))))</span>

<span class="c1">;; deft - one way to search Org-roam notes, but not the fastest (see below)</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">deft</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:config</span>
  <span class="nb">:after</span> <span class="nv">org</span>
  <span class="nb">:bind</span>
  <span class="p">(</span><span class="s">&#34;C-c r d&#34;</span> <span class="o">.</span> <span class="nv">deft</span><span class="p">)</span>
  <span class="nb">:custom</span>
  <span class="p">(</span><span class="nv">deft-recursive</span> <span class="no">t</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">deft-use-filter-string-for-filename</span> <span class="no">t</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">deft-default-extension</span> <span class="s">&#34;org&#34;</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">deft-directory</span> <span class="s">&#34;~/Documents/Org/org-roam/&#34;</span><span class="p">))</span>

<span class="c1">;; Here end the basic setup, but....</span>

<span class="c1">;; SOME OTHER THINGS YOU MIGHT ADD</span>

<span class="c1">;; bars seems pointless here, but if you like, don&#39;t do this</span>
<span class="p">(</span><span class="nv">menu-bar-mode</span> <span class="mi">-1</span><span class="p">)</span>
<span class="p">(</span><span class="nv">tool-bar-mode</span> <span class="mi">-1</span><span class="p">)</span>

<span class="c1">;; You could use a different theme</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">cyberpunk-theme</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:config</span>
  <span class="p">(</span><span class="nv">load-theme</span> <span class="ss">&#39;cyberpunk</span><span class="p">))</span>

<span class="c1">;;;;;;;;;;;;;;;;;;;;;</span>
<span class="c1">;; Spell-checking ;;;</span>
<span class="c1">;;;;;;;;;;;;;;;;;;;;;</span>
<span class="p">(</span><span class="nb">require</span> <span class="ss">&#39;flymake</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">ispell-program-name</span> <span class="s">&#34;hunspell&#34;</span><span class="p">)</span> <span class="c1">; could be ispell as well, depending on your preferences</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">ispell-dictionary</span> <span class="s">&#34;en_GB&#34;</span><span class="p">)</span> <span class="c1">; this can obviously be set to any language your spell-checking program supports</span>
<span class="c1">;; I installed the en_GB ones, but these don&#39;t come in Termux by default. To add arbitrary hunspell languages, see:</span>
<span class="c1">;; https://www.reddit.com/r/termux/comments/k5o6mp/new_hunspell_dictionaries/?</span>
<span class="c1">;; in summary:</span>
<span class="c1">;; - how to add new: copy .aff and .dic files in /data/data/com.termux/files/usr/share/hunspell/</span>
<span class="c1">;; - where to get new: https://www.freeoffice.com/en/download/dictionaries</span>

<span class="p">(</span><span class="nb">dolist</span> <span class="p">(</span><span class="nv">hook</span> <span class="o">&#39;</span><span class="p">(</span><span class="nv">org-mode-hook</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">add-hook</span> <span class="nv">hook</span> <span class="p">(</span><span class="nb">lambda</span> <span class="p">()</span> <span class="p">(</span><span class="nv">flyspell-mode</span> <span class="mi">1</span><span class="p">))))</span>
<span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;org-mode-hook</span> <span class="p">(</span><span class="nb">lambda</span> <span class="p">()</span> <span class="p">(</span><span class="nb">setq</span> <span class="nv">ispell-parser</span> <span class="ss">&#39;tex</span><span class="p">)))</span> <span class="c1">; make orgmode recognise LaTeX syntax [from http://stackoverflow.com/questions/11646880/flyspell-in-org-mode-recognize-latex-syntax-like-auctex ]</span>
<span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;text-mode-hook</span> <span class="nf">#&#39;</span><span class="nv">flyspell-mode</span><span class="p">)</span>

<span class="c1">;;;;;;;;;;;;;;;</span>
<span class="c1">;; Undo-Tree ;; - a new undo package</span>
<span class="c1">;;;;;;;;;;;;;;;</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">undo-tree</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:config</span>
  <span class="c1">;; (setq undo-tree-auto-save-history 1)</span>
  <span class="c1">;; Each node in the undo tree should have a timestamp.</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">undo-tree-visualizer-timestamps</span> <span class="no">t</span><span class="p">)</span>
  <span class="c1">;; Show a diff window displaying changes between undo nodes.</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">undo-tree-visualizer-diff</span> <span class="no">t</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">global-undo-tree-mode</span><span class="p">))</span>

<span class="c1">;; display time and date in modeline, if you like</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">display-time-day-and-date</span> <span class="no">t</span><span class="p">)</span>
<span class="p">(</span><span class="nv">display-time-mode</span> <span class="mi">1</span><span class="p">)</span>

<span class="c1">;; prettier bullets</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">org-bullets</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:config</span>
  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;org-mode-hook</span> <span class="p">(</span><span class="nb">lambda</span> <span class="p">()</span> <span class="p">(</span><span class="nv">org-bullets-mode</span> <span class="mi">1</span><span class="p">)))</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-bullets-bullet-list</span> <span class="o">&#39;</span><span class="p">(</span><span class="s">&#34;⋇&#34;</span> <span class="s">&#34;∴&#34;</span> <span class="s">&#34;∵&#34;</span> <span class="s">&#34;∷&#34;</span> <span class="s">&#34;∺&#34;</span><span class="p">)))</span>

<span class="c1">;; A nice way of quickly adding links.</span>
<span class="c1">;; (Though in Termux, you first must paste from your</span>
<span class="c1">;; Android clipboard and then copy/kill via Emacs before</span>
<span class="c1">;; it&#39;ll work.)</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">org-cliplink</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:config</span>
  <span class="p">(</span><span class="nf">define-key</span> <span class="nv">org-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;C-c o c&#34;</span><span class="p">)</span> <span class="nf">#&#39;</span><span class="nv">org-cliplink</span><span class="p">))</span>

<span class="c1">;; This is also not needed, but adds some (dubiously) useful properties</span>
<span class="c1">;; to the Org-roam file&#39;s property drawer.</span>

<span class="c1">;; First, set up a system for getting location</span>
<span class="c1">;; (we could also try to leverage termux&#39;s built-in</span>
<span class="c1">;; GPS location abilities via `termux-location`, but</span>
<span class="c1">;; it seems a bit slow and doesn&#39;t even always work if</span>
<span class="c1">;; your device can&#39;t get a good satellite connection.)</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">calendar-latitude</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">calendar-longitude</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/get-lat-long-from-ipinfo</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">let*</span>
      <span class="p">((</span><span class="nv">latlong</span> <span class="p">(</span><span class="nf">substring</span>
                 <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;curl -s &#39;ipinfo.io/loc&#39;&#34;</span><span class="p">)</span> <span class="mi">0</span> <span class="mi">-1</span><span class="p">))</span>
       <span class="p">(</span><span class="nv">latlong-list</span> <span class="p">(</span><span class="nv">split-string</span> <span class="nv">latlong</span> <span class="s">&#34;,&#34;</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">setq</span> <span class="nv">calendar-latitude</span> <span class="p">(</span><span class="nf">string-to-number</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">latlong-list</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">setq</span> <span class="nv">calendar-longitude</span> <span class="p">(</span><span class="nf">string-to-number</span> <span class="p">(</span><span class="nv">cadr</span> <span class="nv">latlong-list</span><span class="p">)))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/add-other-auto-props-to-org-roam-properties</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nf">file-exists-p</span> <span class="p">(</span><span class="nf">buffer-file-name</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">org-find-property</span> <span class="s">&#34;CREATION_TIME&#34;</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">org-roam-add-property</span> <span class="p">(</span><span class="nf">format-time-string</span> <span class="s">&#34;%s&#34;</span>
                                                 <span class="p">(</span><span class="nf">nth</span> <span class="mi">5</span>
                                                      <span class="p">(</span><span class="nf">file-attributes</span> <span class="p">(</span><span class="nf">buffer-file-name</span><span class="p">))))</span>
                             <span class="s">&#34;CREATION_TIME&#34;</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">org-find-property</span> <span class="s">&#34;AUTHOR&#34;</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">org-roam-add-property</span> <span class="nv">roam-user</span> <span class="s">&#34;AUTHOR&#34;</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">org-find-property</span> <span class="s">&#34;MAIL&#34;</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">org-roam-add-property</span> <span class="nv">roam-email</span> <span class="s">&#34;MAIL&#34;</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">org-find-property</span> <span class="s">&#34;LAT_LONG&#34;</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">bms/get-lat-long-from-ipinfo</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">org-roam-add-property</span> <span class="p">(</span><span class="nf">concat</span> <span class="p">(</span><span class="nf">number-to-string</span> <span class="nv">calendar-latitude</span><span class="p">)</span> <span class="s">&#34;,&#34;</span> <span class="p">(</span><span class="nf">number-to-string</span> <span class="nv">calendar-longitude</span><span class="p">))</span> <span class="s">&#34;LAT-LONG&#34;</span><span class="p">))))</span>

<span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;org-roam-capture-new-node-hook</span> <span class="nf">#&#39;</span><span class="nv">bms/add-other-auto-props-to-org-roam-properties</span><span class="p">)</span>

<span class="c1">;; You could use Ivy or Helm or the default, but I</span>
<span class="c1">;; like Selectrum, Consult &amp; friends. Plus we can leverage</span>
<span class="c1">;; Consult for a nice alternative to deft for note-searching.</span>
<span class="c1">;; You&#39;ll need this to use my ripgrep note searching feature below.</span>

<span class="c1">;; selectrum</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">selectrum</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:config</span>
  <span class="p">(</span><span class="nv">selectrum-mode</span> <span class="mi">+1</span><span class="p">))</span>

<span class="c1">;; ;; prescient  - T9</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">prescient</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:config</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">prescient-persist-mode</span> <span class="no">t</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">prescient-filter-method</span> <span class="o">&#39;</span><span class="p">(</span><span class="nv">literal</span> <span class="nv">regexp</span> <span class="nv">initialism</span> <span class="nv">fuzzy</span><span class="p">)))</span> <span class="c1">;; added fuzzy</span>

<span class="p">(</span><span class="nb">use-package</span> <span class="nv">orderless</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:init</span> <span class="p">(</span><span class="nv">icomplete-mode</span><span class="p">)</span>                <span class="c1">; optional but recommended!</span>
  <span class="nb">:custom</span> <span class="p">(</span><span class="nv">completion-styles</span> <span class="o">&#39;</span><span class="p">(</span><span class="nv">orderless</span><span class="p">))</span>
  <span class="nb">:config</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">orderless-matching-styles</span> <span class="o">&#39;</span><span class="p">(</span><span class="nv">orderless-flex</span><span class="p">))</span>
  <span class="c1">;; This means that the company-capf backend will automatically use orderless, but following issue exists:</span>
  <span class="c1">;; Pressing SPC takes you out of completion, so with the default separator you are limited to one component,</span>
  <span class="c1">;; which is no fun. To fix this add a separator that is allowed to occur in identifiers, for example, for</span>
  <span class="c1">;; Emacs Lisp code you could use an ampersand:</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">orderless-component-separator</span> <span class="s">&#34;[ &amp;]&#34;</span><span class="p">)</span>

  <span class="c1">;; The matching portions of candidates aren’t highlighted. But while you can’t get different faces for</span>
  <span class="c1">;; different components, you can at least get the matches highlighted in the sole available face with this configuration</span>
  <span class="p">(</span><span class="nb">defun</span> <span class="nv">just-one-face</span> <span class="p">(</span><span class="nv">fn</span> <span class="kp">&amp;rest</span> <span class="nv">args</span><span class="p">)</span>
    <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">orderless-match-faces</span> <span class="p">[</span><span class="nv">completions-common-part</span><span class="p">]))</span>
      <span class="p">(</span><span class="nf">apply</span> <span class="nv">fn</span> <span class="nv">args</span><span class="p">)))</span>

  <span class="p">(</span><span class="nv">advice-add</span> <span class="ss">&#39;company-capf--candidates</span> <span class="nb">:around</span> <span class="nf">#&#39;</span><span class="nv">just-one-face</span><span class="p">))</span>

<span class="p">(</span><span class="nb">use-package</span> <span class="nv">selectrum-prescient</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:config</span>
  <span class="c1">;; to make sorting and filtering more intelligent</span>
  <span class="p">(</span><span class="nv">selectrum-prescient-mode</span> <span class="mi">+1</span><span class="p">)</span>

  <span class="c1">;; Filtering with orderless</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">selectrum-refine-candidates-function</span> <span class="nf">#&#39;</span><span class="nv">orderless-filter</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">selectrum-highlight-candidates-function</span> <span class="nf">#&#39;</span><span class="nv">orderless-highlight-matches</span><span class="p">)</span>

  <span class="c1">;; If you also configure `completion-styles` for orderless you might want to use the</span>
  <span class="c1">;; following advice because orderless isn&#39;t well suited for initial gathering of</span>
  <span class="c1">;; candidates by completion in region.</span>
  <span class="p">(</span><span class="nv">advice-add</span> <span class="nf">#&#39;</span><span class="nv">completion--category-override</span> <span class="nb">:filter-return</span>
              <span class="p">(</span><span class="nb">defun</span> <span class="nv">completion-in-region-style-setup+</span> <span class="p">(</span><span class="nv">res</span><span class="p">)</span>
                <span class="s">&#34;Fallback to default styles for region completions with orderless.&#34;</span>
                <span class="p">(</span><span class="nb">or</span> <span class="nv">res</span>
                    <span class="c1">;; Don&#39;t use orderless for initial candidate gathering.</span>
                    <span class="p">(</span><span class="nb">and</span> <span class="nv">completion-in-region-mode-predicate</span>
                         <span class="p">(</span><span class="nv">not</span> <span class="p">(</span><span class="nf">minibufferp</span><span class="p">))</span>
                         <span class="p">(</span><span class="nf">equal</span> <span class="o">&#39;</span><span class="p">(</span><span class="nv">orderless</span><span class="p">)</span> <span class="nv">completion-styles</span><span class="p">)</span>
                         <span class="o">&#39;</span><span class="p">(</span><span class="nv">basic</span> <span class="nv">partial-completion</span> <span class="nv">emacs22</span><span class="p">)))))</span>

  <span class="c1">;; Minibuffer-actions with embark</span>

  <span class="c1">;; You should bind embark commands like embark-act, embark-act-noexit</span>
  <span class="c1">;; and embark-export in minibuffer-local-map (as embark commands are not selectrum specific).</span>
  <span class="c1">;; For available commands and other embark configurations see the embark documentation and its wiki.</span>

  <span class="p">(</span><span class="nb">defun</span> <span class="nv">current-candidate+category</span> <span class="p">()</span>
    <span class="p">(</span><span class="nb">when</span> <span class="nv">selectrum-is-active</span>
      <span class="p">(</span><span class="nf">cons</span> <span class="p">(</span><span class="nv">selectrum--get-meta</span> <span class="ss">&#39;category</span><span class="p">)</span>
            <span class="p">(</span><span class="nv">selectrum-get-current-candidate</span><span class="p">))))</span>

  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;embark-target-finders</span> <span class="nf">#&#39;</span><span class="nv">current-candidate+category</span><span class="p">)</span>

  <span class="p">(</span><span class="nb">defun</span> <span class="nv">current-candidates+category</span> <span class="p">()</span>
    <span class="p">(</span><span class="nb">when</span> <span class="nv">selectrum-is-active</span>
      <span class="p">(</span><span class="nf">cons</span> <span class="p">(</span><span class="nv">selectrum--get-meta</span> <span class="ss">&#39;category</span><span class="p">)</span>
            <span class="p">(</span><span class="nv">selectrum-get-current-candidates</span>
             <span class="c1">;; Pass relative file names for dired.</span>
             <span class="nv">minibuffer-completing-file-name</span><span class="p">))))</span>

  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;embark-candidate-collectors</span> <span class="nf">#&#39;</span><span class="nv">current-candidates+category</span><span class="p">)</span>
  <span class="c1">;; No unnecessary computation delay after injection.</span>
  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;embark-setup-hook</span> <span class="ss">&#39;selectrum-set-selected-candidate</span><span class="p">)</span>

  <span class="c1">;; The following is not selectrum specific but included here for convenience.</span>
  <span class="c1">;; If you don&#39;t want to use which-key as a key prompter skip the following code.</span>

  <span class="p">(</span><span class="nb">setq</span> <span class="nv">embark-action-indicator</span>
        <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="nv">map</span><span class="p">)</span> <span class="p">(</span><span class="nv">which-key--show-keymap</span> <span class="s">&#34;Embark&#34;</span> <span class="nv">map</span> <span class="no">nil</span> <span class="no">nil</span> <span class="ss">&#39;no-paging</span><span class="p">)</span>
          <span class="nf">#&#39;</span><span class="nv">which-key--hide-popup-ignore-command</span><span class="p">)</span>
        <span class="nv">embark-become-indicator</span> <span class="nv">embark-action-indicator</span><span class="p">)</span>

  <span class="c1">;; to save your command history on disk, so the sorting gets more</span>
  <span class="c1">;; intelligent over time</span>
  <span class="p">(</span><span class="nv">prescient-persist-mode</span> <span class="mi">+1</span><span class="p">))</span>

<span class="c1">;; Example configuration for Consult</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">consult</span>
  <span class="c1">;; Replace bindings. Lazily loaded due by `use-package&#39;.</span>
  <span class="nb">:bind</span> <span class="p">((</span><span class="s">&#34;C-x M-:&#34;</span> <span class="o">.</span> <span class="nv">consult-complex-command</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c h&#34;</span> <span class="o">.</span> <span class="nv">consult-history</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-c m&#34;</span> <span class="o">.</span> <span class="nv">consult-mode-command</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-x b&#34;</span> <span class="o">.</span> <span class="nv">consult-buffer</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-x 4 b&#34;</span> <span class="o">.</span> <span class="nv">consult-buffer-other-window</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-x 5 b&#34;</span> <span class="o">.</span> <span class="nv">consult-buffer-other-frame</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-x r x&#34;</span> <span class="o">.</span> <span class="nv">consult-register</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;C-x r b&#34;</span> <span class="o">.</span> <span class="nv">consult-bookmark</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;M-g g&#34;</span> <span class="o">.</span> <span class="nv">consult-goto-line</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;M-g M-g&#34;</span> <span class="o">.</span> <span class="nv">consult-goto-line</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;M-g o&#34;</span> <span class="o">.</span> <span class="nv">consult-outline</span><span class="p">)</span>       <span class="c1">;; &#34;M-s o&#34; is a good alternative.</span>
         <span class="p">(</span><span class="s">&#34;M-g l&#34;</span> <span class="o">.</span> <span class="nv">consult-line</span><span class="p">)</span>          <span class="c1">;; &#34;M-s l&#34; is a good alternative.</span>
         <span class="p">(</span><span class="s">&#34;M-g m&#34;</span> <span class="o">.</span> <span class="nv">consult-mark</span><span class="p">)</span>          <span class="c1">;; I recommend to bind Consult navigation</span>
         <span class="p">(</span><span class="s">&#34;M-g k&#34;</span> <span class="o">.</span> <span class="nv">consult-global-mark</span><span class="p">)</span>   <span class="c1">;; commands under the &#34;M-g&#34; prefix.</span>
         <span class="p">(</span><span class="s">&#34;M-g r&#34;</span> <span class="o">.</span> <span class="nv">consult-ripgrep</span><span class="p">)</span>      <span class="c1">;; or consult-grep, consult-ripgrep</span>
         <span class="p">(</span><span class="s">&#34;M-g f&#34;</span> <span class="o">.</span> <span class="nv">consult-find</span><span class="p">)</span>          <span class="c1">;; or consult-locate, my-fdfind</span>
         <span class="p">(</span><span class="s">&#34;M-g i&#34;</span> <span class="o">.</span> <span class="nv">consult-project-imenu</span><span class="p">)</span> <span class="c1">;; or consult-imenu</span>
         <span class="p">(</span><span class="s">&#34;M-g e&#34;</span> <span class="o">.</span> <span class="nv">consult-error</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;M-s m&#34;</span> <span class="o">.</span> <span class="nv">consult-multi-occur</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;M-y&#34;</span> <span class="o">.</span> <span class="nv">consult-yank-pop</span><span class="p">)</span>
         <span class="p">(</span><span class="s">&#34;&lt;help&gt; a&#34;</span> <span class="o">.</span> <span class="nv">consult-apropos</span><span class="p">))</span>

  <span class="c1">;; The :init configuration is always executed (Not lazy!)</span>
  <span class="nb">:init</span>

  <span class="c1">;; Custom command wrappers. It is generally encouraged to write your own</span>
  <span class="c1">;; commands based on the Consult commands. Some commands have arguments which</span>
  <span class="c1">;; allow tweaking. Furthermore global configuration variables can be set</span>
  <span class="c1">;; locally in a let-binding.</span>
  <span class="p">(</span><span class="nb">defun</span> <span class="nv">my-fdfind</span> <span class="p">(</span><span class="kp">&amp;optional</span> <span class="nv">dir</span><span class="p">)</span>
    <span class="p">(</span><span class="nb">interactive</span> <span class="s">&#34;P&#34;</span><span class="p">)</span>
    <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">consult-find-command</span> <span class="o">&#39;</span><span class="p">(</span><span class="s">&#34;fdfind&#34;</span> <span class="s">&#34;--color=never&#34;</span> <span class="s">&#34;--full-path&#34;</span><span class="p">)))</span>
      <span class="p">(</span><span class="nv">consult-find</span> <span class="nv">dir</span><span class="p">)))</span>

  <span class="c1">;; Replace `multi-occur&#39; with `consult-multi-occur&#39;, which is a drop-in replacement.</span>
  <span class="p">(</span><span class="nf">fset</span> <span class="ss">&#39;multi-occur</span> <span class="nf">#&#39;</span><span class="nv">consult-multi-occur</span><span class="p">)</span>

  <span class="c1">;; Configure other variables and modes in the :config section, after lazily loading the package</span>
  <span class="nb">:config</span>

  <span class="c1">;; Configure preview. Note that the preview-key can also be configured on a</span>
  <span class="c1">;; per-command basis via `consult-config&#39;.</span>
  <span class="c1">;; (setq consult-preview-key &#39;any) ;; any key triggers preview, the default</span>

  <span class="c1">;; Optionally configure narrowing key.</span>
  <span class="c1">;; Both &lt; and C-+ work reasonably well.</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">consult-narrow-key</span> <span class="s">&#34;&lt;&#34;</span><span class="p">)</span> <span class="c1">;; (kbd &#34;C-+&#34;)</span>
  <span class="c1">;; Optionally make narrowing help available in the minibuffer.</span>
  <span class="c1">;; Probably not needed if you are using which-key.</span>
  <span class="c1">;; (define-key consult-narrow-map (vconcat consult-narrow-key &#34;?&#34;) #&#39;consult-narrow-help)</span>

  <span class="c1">;; Optional configure a view library to be used by `consult-buffer&#39;.</span>
  <span class="c1">;; The view library must provide two functions, one to open the view by name,</span>
  <span class="c1">;; and one function which must return a list of views as strings.</span>
  <span class="c1">;; Example: https://github.com/minad/bookmark-view/</span>
  <span class="c1">;; (setq consult-view-open-function #&#39;bookmark-jump</span>
  <span class="c1">;;       consult-view-list-function #&#39;bookmark-view-names)</span>

  <span class="c1">;; Optionally configure a function which returns the project root directory</span>
  <span class="c1">;; (autoload &#39;projectile-project-root &#34;projectile&#34;)</span>
  <span class="c1">;; (setq consult-project-root-function #&#39;projectile-project-root)</span>
  <span class="p">)</span>

<span class="c1">;; Optionally add the `consult-flycheck&#39; command.</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">consult-flycheck</span>
  <span class="nb">:bind</span> <span class="p">(</span><span class="nb">:map</span> <span class="nv">flycheck-command-map</span>
              <span class="p">(</span><span class="s">&#34;!&#34;</span> <span class="o">.</span> <span class="nv">consult-flycheck</span><span class="p">)))</span>

<span class="c1">;; Optionally enable richer annotations using the Marginalia package</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">marginalia</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="c1">;; The :init configuration is always executed (Not lazy!)</span>
  <span class="nb">:init</span>
  <span class="c1">;; Must be in the :init section of use-package such that the mode gets</span>
  <span class="c1">;; enabled right away. Note that this forces loading the package.</span>
  <span class="p">(</span><span class="nv">marginalia-mode</span><span class="p">))</span>

<span class="p">(</span><span class="nb">use-package</span> <span class="nv">embark</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:bind</span>
  <span class="p">(</span><span class="s">&#34;C-S-a&#34;</span> <span class="o">.</span> <span class="nv">embark-act</span><span class="p">)</span>               <span class="c1">; pick some comfortable binding</span>
  <span class="nb">:config</span>
  <span class="c1">;; For Selectrum users:</span>
  <span class="p">(</span><span class="nb">defun</span> <span class="nv">current-candidate+category</span> <span class="p">()</span>
    <span class="p">(</span><span class="nb">when</span> <span class="nv">selectrum-is-active</span>
      <span class="p">(</span><span class="nf">cons</span> <span class="p">(</span><span class="nv">selectrum--get-meta</span> <span class="ss">&#39;category</span><span class="p">)</span>
            <span class="p">(</span><span class="nv">selectrum-get-current-candidate</span><span class="p">))))</span>

  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;embark-target-finders</span> <span class="nf">#&#39;</span><span class="nv">current-candidate+category</span><span class="p">)</span>

  <span class="p">(</span><span class="nb">defun</span> <span class="nv">current-candidates+category</span> <span class="p">()</span>
    <span class="p">(</span><span class="nb">when</span> <span class="nv">selectrum-is-active</span>
      <span class="p">(</span><span class="nf">cons</span> <span class="p">(</span><span class="nv">selectrum--get-meta</span> <span class="ss">&#39;category</span><span class="p">)</span>
            <span class="p">(</span><span class="nv">selectrum-get-current-candidates</span>
             <span class="c1">;; Pass relative file names for dired.</span>
             <span class="nv">minibuffer-completing-file-name</span><span class="p">))))</span>

  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;embark-candidate-collectors</span> <span class="nf">#&#39;</span><span class="nv">current-candidates+category</span><span class="p">)</span>

  <span class="c1">;; No unnecessary computation delay after injection.</span>
  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;embark-setup-hook</span> <span class="ss">&#39;selectrum-set-selected-candidate</span><span class="p">))</span>

<span class="c1">;; org-roam-rg-search - this is a much faster way to search Org-roam notes:</span>
<span class="c1">;; requires the Selectrum+Consult setup immediately preceding.</span>
<span class="c1">;; Use C-c r r to search notes via consult&#39;s ripgrep interface</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/org-roam-rg-search</span> <span class="p">()</span>
  <span class="s">&#34;Search org-roam directory using consult-ripgrep. With live-preview.&#34;</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">consult-ripgrep</span> <span class="s">&#34;rg --null --ignore-case --type org --line-buffered --color=always --max-columns=500 --no-heading --line-number . -e ARG OPTS&#34;</span><span class="p">))</span>
    <span class="p">(</span><span class="nv">consult-ripgrep</span> <span class="nv">org-roam-directory</span><span class="p">)))</span>

<span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;C-c rr&#34;</span><span class="p">)</span> <span class="ss">&#39;bms/org-roam-rg-search</span><span class="p">)</span>

<span class="c1">;; speed-keys - see https://github.com/alhassy/emacs.d#manipulating-sections</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">org-use-speed-commands</span> <span class="no">t</span><span class="p">)</span>

<span class="c1">;; On an org-heading, C-a goes to after the star, heading markers. To use speed keys, run C-a C-a to get to the star markers.</span>
<span class="c1">;; C-e goes to the end of the heading, not including the tags.</span>
<span class="p">(</span><span class="nb">setq</span> <span class="nv">org-special-ctrl-a/e</span> <span class="no">t</span><span class="p">)</span>

<span class="c1">;;drag images into orgmode</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">org-download</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:config</span>
  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;dired-mode-hook</span> <span class="ss">&#39;org-download-enable</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;C-c o i&#34;</span><span class="p">)</span> <span class="nf">#&#39;</span><span class="nv">org-download-yank</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-download-method</span> <span class="ss">&#39;attach</span><span class="p">))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/org-attach-insert-link</span> <span class="p">(</span><span class="kp">&amp;optional</span> <span class="nv">in-emacs</span><span class="p">)</span>
  <span class="s">&#34;Insert attachment from list.&#34;</span>
  <span class="p">(</span><span class="nb">interactive</span> <span class="s">&#34;P&#34;</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">attach-dir</span> <span class="p">(</span><span class="nv">org-attach-dir</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">if</span> <span class="nv">attach-dir</span>
        <span class="p">(</span><span class="nb">let*</span> <span class="p">((</span><span class="nv">file</span> <span class="p">(</span><span class="nb">pcase</span> <span class="p">(</span><span class="nv">org-attach-file-list</span> <span class="nv">attach-dir</span><span class="p">)</span>
                       <span class="p">(</span><span class="o">`</span><span class="p">(</span><span class="o">,</span><span class="nv">file</span><span class="p">)</span> <span class="nv">file</span><span class="p">)</span>
                       <span class="p">(</span><span class="nv">files</span> <span class="p">(</span><span class="nf">completing-read</span> <span class="s">&#34;Insert attachment: &#34;</span>
                                               <span class="p">(</span><span class="nf">mapcar</span> <span class="nf">#&#39;list</span> <span class="nv">files</span><span class="p">)</span> <span class="no">nil</span> <span class="no">t</span><span class="p">))))</span>
               <span class="p">(</span><span class="nv">path</span> <span class="p">(</span><span class="nf">expand-file-name</span> <span class="nv">file</span> <span class="nv">attach-dir</span><span class="p">))</span>
               <span class="p">(</span><span class="nv">desc</span> <span class="p">(</span><span class="nf">file-name-nondirectory</span> <span class="nv">path</span><span class="p">)))</span>
          <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">initial-input</span>
                 <span class="p">(</span><span class="nb">cond</span>
                  <span class="p">((</span><span class="nv">not</span> <span class="nv">org-link-make-description-function</span><span class="p">)</span> <span class="nv">desc</span><span class="p">)</span>
                  <span class="p">(</span><span class="no">t</span> <span class="p">(</span><span class="nb">condition-case</span> <span class="no">nil</span>
                         <span class="p">(</span><span class="nf">funcall</span> <span class="nv">org-link-make-description-function</span> <span class="nv">link</span> <span class="nv">desc</span><span class="p">)</span>
                       <span class="p">(</span><span class="ne">error</span>
                        <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;Can&#39;t get link description from %S&#34;</span>
                                 <span class="p">(</span><span class="nf">symbol-name</span> <span class="nv">org-link-make-description-function</span><span class="p">))</span>
                        <span class="p">(</span><span class="nv">sit-for</span> <span class="mi">2</span><span class="p">)</span>
                        <span class="no">nil</span><span class="p">))))))</span>
            <span class="p">(</span><span class="nb">setq</span> <span class="nv">desc</span> <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nv">called-interactively-p</span> <span class="ss">&#39;any</span><span class="p">)</span>
                           <span class="p">(</span><span class="nf">read-string</span> <span class="s">&#34;Description: &#34;</span> <span class="nv">initial-input</span><span class="p">)</span>
                         <span class="nv">initial-input</span><span class="p">))</span>
            <span class="p">(</span><span class="nv">org-insert-link</span> <span class="no">nil</span> <span class="nv">path</span> <span class="p">(</span><span class="nf">concat</span> <span class="s">&#34;attachment:&#34;</span> <span class="nv">desc</span><span class="p">))))</span>
      <span class="p">(</span><span class="ne">error</span> <span class="s">&#34;No attachment directory exist&#34;</span><span class="p">))))</span>
<span class="p">(</span><span class="nf">define-key</span> <span class="nv">org-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;C-c o l&#34;</span><span class="p">)</span> <span class="nf">#&#39;</span><span class="nv">bms/org-attach-insert-link</span><span class="p">)</span>

<span class="c1">;; in case you want some things not in melpa</span>
<span class="c1">;; you&#39;ll need it for the remaining things below</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">quelpa</span>
  <span class="nb">:ensure</span> <span class="no">t</span><span class="p">)</span>

<span class="p">(</span><span class="nb">use-package</span> <span class="nv">quelpa-use-package</span>
  <span class="nb">:ensure</span> <span class="no">t</span><span class="p">)</span>

<span class="c1">;; A bit of sugar for the visual appearance of Org syntax</span>
<span class="c1">;; Use if you like.</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">org-appear</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:quelpa</span> <span class="p">(</span><span class="nv">org-appear</span> <span class="nb">:fetcher</span> <span class="nv">github</span> <span class="nb">:repo</span> <span class="s">&#34;awth13/org-appear&#34;</span><span class="p">)</span>
  <span class="nb">:config</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">org-hide-emphasis-markers</span> <span class="no">t</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;org-mode-hook</span> <span class="ss">&#39;org-appear-mode</span><span class="p">))</span>

<span class="c1">;; You don&#39;t need this, but it&#39;s cool and it does work on Android:</span>
<span class="c1">;; see https://github.com/org-roam/org-roam-ui for features</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">org-roam-ui</span>
  <span class="nb">:ensure</span> <span class="no">t</span>
  <span class="nb">:quelpa</span> <span class="p">(</span><span class="nv">org-roam-ui</span> <span class="nb">:fetcher</span> <span class="nv">github</span> <span class="nb">:repo</span> <span class="s">&#34;org-roam/org-roam-ui&#34;</span> <span class="nb">:branch</span> <span class="s">&#34;main&#34;</span> <span class="nb">:files</span> <span class="p">(</span><span class="s">&#34;*.el&#34;</span> <span class="s">&#34;out&#34;</span><span class="p">))</span>
  <span class="nb">:after</span> <span class="nv">org-roam</span>
  <span class="nb">:hook</span> <span class="p">(</span><span class="nv">org-roam</span> <span class="o">.</span> <span class="nv">org-roam-ui-mode</span><span class="p">))</span>
</code></pre></div><p>An excellent way of keeping Org notes (and files more generally) in
sync between desktop, laptop, and mobile devices is <a href="https://syncthing.net/">Syncthing</a>. On
Android I recommend using the <a href="https://f-droid.org/en/packages/com.github.catfriend1.syncthingandroid/">Syncthing-Fork</a> app (via F-Droid), which has various improvements over the default Syncthing app on Android, including better file-access features. (On iOS there is now a third-party solution for syncing via Syncthing: <a href="https://www.mobiussync.com/">Möbius-Sync</a>. I have no idea how to use Emacs/Org-mode on iOS though, but I recall hearing about some ways of running a Linux shell on iOS like <a href="https://ish.app/">iSH</a>, so possibly there&rsquo;s some way.)</p>
<p>I have Syncthing sync my Org files to a directory in my main &ldquo;home&rdquo;
directory on Android <code>Documents/Org</code> and then in Termux created a
<code>Documents</code> directory and inside of that directory created a symlink to
my actual Org directory via <code>ln -s storage/shared/Documents/Org Org</code>. I&rsquo;ve found that is easier for allowing Syncthing to have access
to the files in order to keep them in sync. (And having my Org files
live at <code>~/Documents/Org</code> in Termux mimics the directory structure on my
Linux boxes, which makes lots of things easier in terms of sharing
configurations.)</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/org" term="org" label="org" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/orgroam" term="orgroam" label="orgroam" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/android" term="android" label="android" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Dynamically set pdf-tools annotation colours]]></title>
            <link href="https://babbagefiles.xyz/pdf-tools-dynamic-highlighting-colours/"/>
            <id>https://babbagefiles.xyz/pdf-tools-dynamic-highlighting-colours/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2021-05-27T12:25:03-06:00</published>
            <updated>2021-05-27T12:38:12-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>In Emacs, <a href="https://github.com/politza/pdf-tools"><code>pdf-tools</code></a> can be used to add annotations to a PDF
document. It can be useful to have multiple annotation colours though,
and be able to set these on the fly.</p>
<p>Here&rsquo;s an example of how to do it with four colours:</p>
<div class="highlight"><pre class="chroma"><code class="language-elisp" data-lang="elisp"><span class="c1">;; annotation colours</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/pdf-annot-colour-blue</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">pdf-annot-default-markup-annotation-properties</span>
        <span class="o">&#39;</span><span class="p">((</span><span class="nv">label</span> <span class="o">.</span> <span class="s">&#34;&#34;</span><span class="p">)</span> <span class="p">(</span><span class="nv">color</span> <span class="o">.</span> <span class="s">&#34;blue&#34;</span><span class="p">)</span> <span class="p">(</span><span class="nv">popup-is-open</span><span class="p">)))</span>
  <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;%s&#34;</span> <span class="p">(</span><span class="nf">propertize</span> <span class="s">&#34;Annotation colour set to blue.&#34;</span> <span class="ss">&#39;face</span> <span class="o">&#39;</span><span class="p">(</span><span class="nb">:foreground</span> <span class="s">&#34;blue&#34;</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/pdf-annot-colour-yellow</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">pdf-annot-default-markup-annotation-properties</span>
        <span class="o">&#39;</span><span class="p">((</span><span class="nv">label</span> <span class="o">.</span> <span class="s">&#34;&#34;</span><span class="p">)</span> <span class="p">(</span><span class="nv">color</span> <span class="o">.</span> <span class="s">&#34;yellow&#34;</span><span class="p">)</span> <span class="p">(</span><span class="nv">popup-is-open</span><span class="p">)))</span>
    <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;%s&#34;</span> <span class="p">(</span><span class="nf">propertize</span> <span class="s">&#34;Annotation colour set to yellow.&#34;</span> <span class="ss">&#39;face</span> <span class="o">&#39;</span><span class="p">(</span><span class="nb">:foreground</span> <span class="s">&#34;yellow&#34;</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/pdf-annot-colour-red</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">pdf-annot-default-markup-annotation-properties</span>
        <span class="o">&#39;</span><span class="p">((</span><span class="nv">label</span> <span class="o">.</span> <span class="s">&#34;&#34;</span><span class="p">)</span> <span class="p">(</span><span class="nv">color</span> <span class="o">.</span> <span class="s">&#34;red&#34;</span><span class="p">)</span> <span class="p">(</span><span class="nv">popup-is-open</span><span class="p">)))</span>
  <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;%s&#34;</span> <span class="p">(</span><span class="nf">propertize</span> <span class="s">&#34;Annotation colour set to red.&#34;</span> <span class="ss">&#39;face</span> <span class="o">&#39;</span><span class="p">(</span><span class="nb">:foreground</span> <span class="s">&#34;red&#34;</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/pdf-annot-colour-orange</span> <span class="p">()</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">setq</span> <span class="nv">pdf-annot-default-markup-annotation-properties</span>
        <span class="o">&#39;</span><span class="p">((</span><span class="nv">label</span> <span class="o">.</span> <span class="s">&#34;&#34;</span><span class="p">)</span> <span class="p">(</span><span class="nv">color</span> <span class="o">.</span> <span class="s">&#34;orange&#34;</span><span class="p">)</span> <span class="p">(</span><span class="nv">popup-is-open</span><span class="p">)))</span>
  <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;%s&#34;</span> <span class="p">(</span><span class="nf">propertize</span> <span class="s">&#34;Annotation colour set to orange.&#34;</span> <span class="ss">&#39;face</span> <span class="o">&#39;</span><span class="p">(</span><span class="nb">:foreground</span> <span class="s">&#34;orange&#34;</span><span class="p">))))</span>

<span class="c1">;; rebind keys for pdf-tools</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">bms/pdf-tools-mode-config</span> <span class="p">()</span>
  <span class="s">&#34;Set pdf-tools keybindings.&#34;</span>
  <span class="p">(</span><span class="nv">local-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;R&#34;</span><span class="p">)</span> <span class="nf">#&#39;</span><span class="nv">bms/pdf-annot-colour-red</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">local-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;L&#34;</span><span class="p">)</span> <span class="nf">#&#39;</span><span class="nv">bms/pdf-annot-colour-blue</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">local-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;O&#34;</span><span class="p">)</span> <span class="nf">#&#39;</span><span class="nv">bms/pdf-annot-colour-orange</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">local-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;Y&#34;</span><span class="p">)</span> <span class="nf">#&#39;</span><span class="nv">bms/pdf-annot-colour-yellow</span><span class="p">))</span>

<span class="c1">;; add to pdf-view-mode-hook</span>
<span class="p">(</span><span class="nv">add-hook</span> <span class="ss">&#39;pdf-view-mode-hook</span> <span class="nf">#&#39;</span><span class="nv">bms/pdf-tools-mode-config</span><span class="p">)</span>
</code></pre></div><p>Example in use (though some of the colours might be better chosen, e.g. a
lighter shade of blue):
<img src="/ox-hugo/pdf-tools-highlighting.png" alt=""></p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Twiddler config for Emacs]]></title>
            <link href="https://babbagefiles.xyz/m-x-twiddler/"/>
            <id>https://babbagefiles.xyz/m-x-twiddler/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2020-05-30T22:00:00-06:00</published>
            <updated>2020-05-30T23:34:16-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>The <a href="https://twiddler.tekgear.com/">Twiddler</a> [<a href="https://web.archive.org/web/20191212214708/https://twiddler.tekgear.com/">here&rsquo;s archive.org&rsquo;s link</a>, as the site seems to be down
as I write this], a one-handed chording keyboard, has a longish history of
being associated with Emacs. Here&rsquo;s 1990s <a href="https://en.wikipedia.org/wiki/Alan%5FAlda">Alan Alda</a> interviewing
<a href="https://en.wikipedia.org/wiki/Thad%5FStarner">Thad Starner</a>, who&rsquo;s using a wearable-computing device foreshadowing
Google Glass, using a Twiddler mk 1 to interact with Emacs (using the
<a href="http://alumni.media.mit.edu/~rhodes/Papers/remembrance.html">Remembrance Agent</a>):</p>
<div class="org-youtube"><iframe src="https://www.youtube-nocookie.com/embed/X7DM1mT8r7c?start=2151" allowfullscreen title="YouTube Video"></iframe></div>
<p>I&rsquo;ve long been intrigued by this one-hand, non-tethered input method
and finally got a Twiddler 3. I was unfortunately stymied on progress
early on due to a loose battery (requiring cracking the entire device
open), but have got it back working and am slowly trying to integrate
it into my workflow.</p>
<p>One place where it would be particularly useful is with a mobile
device (e.g. smart phone), especially as one can run full <a href="https://endlessparentheses.com/running-emacs-on-android.html">Emacs in
Termux on Android</a>.</p>
<p>The Twiddler is a configurable device, and obviously using it with
Emacs calls for some Emacs-specific configuration. Here is my current
working configuration:</p>
<p><a href="https://gitlab.com/emacsomancer/m-x-tabspace">M-x tab-space: Emacs-centric layout for the Twiddler 3</a></p>
<p>With important Emacs combinations like <code>C-x</code>, <code>M-x</code>, <code>C-g</code> mapped to
chords (along with a chord prefix, <code>s-F</code> for my personal <a href="https://stumpwm.github.io/">StumpWM</a>
config), and some chords set up for comfortable navigating with <a href="https://github.com/emacsorphanage/god-mode">God
Mode</a> and <a href="https://github.com/abo-abo/avy">avy</a>. And lots of space for expansion.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/twiddler" term="twiddler" label="twiddler" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Group-agnostic previous-focussed-window memory in StumpWM]]></title>
            <link href="https://babbagefiles.xyz/agnostic-focus-stumpwm/"/>
            <id>https://babbagefiles.xyz/agnostic-focus-stumpwm/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2020-04-25T14:25:00-05:00</published>
            <updated>2025-01-30T12:55:43-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>I&rsquo;ve started using StumpWM&rsquo;s groups (like &ldquo;workspaces&rdquo; in other window
managers) more extensively, but this broke a behaviour I like: the
ability to easily switch back to the last focussed window, because
StumpWM&rsquo;s &ldquo;last focussed&rdquo; is group-specific. So I wasn&rsquo;t easily about
to switch quickly back and forth between two windows that were inb
different groups, which turns out to be something I frequently want to
do (e.g. switch back and forth between an emacsclient frame in my
&ldquo;emacs&rdquo; group and a Firefox instance in my &ldquo;web&rdquo; group).</p>
<p>Here&rsquo;s my fix [updated with some fixes on <span class="timestamp-wrapper"><span class="timestamp">[2025-01-27 Mon]</span></span>]:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="c1">;; Global &#39;last focussed window&#39;</span>
<span class="p">(</span><span class="nb">setf</span> <span class="vg">*global-ante-earlier-focussed-window*</span> <span class="ss">&#39;nil</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setf</span> <span class="vg">*global-earlier-focussed-window*</span> <span class="ss">&#39;nil</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setf</span> <span class="vg">*global-prev-focussed-window*</span> <span class="ss">&#39;nil</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setf</span> <span class="vg">*global-cur-focussed-window*</span> <span class="ss">&#39;nil</span><span class="p">)</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">panrecord-of-last-focussed-window</span> <span class="p">(</span><span class="nv">currwin</span> <span class="nv">lastwin</span><span class="p">)</span>
  <span class="s">&#34;Record last visited windows and their group.&#34;</span>
  <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nb">or</span> <span class="p">(</span><span class="nf">search</span> <span class="s">&#34;*EQUAKE*[&#34;</span> <span class="p">(</span><span class="nv">window-name</span> <span class="nv">currwin</span><span class="p">))</span> <span class="c1">;; don&#39;t record Equake</span>
              <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nc">cons</span> <span class="p">(</span><span class="nv">current-window</span><span class="p">)</span> <span class="p">(</span><span class="nv">current-group</span><span class="p">))</span> <span class="vg">*global-cur-focussed-window*</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nv">find-window-globally</span>
           <span class="p">(</span><span class="nf">car</span> <span class="vg">*global-earlier-focussed-window*</span><span class="p">)</span> <span class="p">(</span><span class="nv">screen-groups</span> <span class="p">(</span><span class="nv">current-screen</span><span class="p">)))</span>
      <span class="p">(</span><span class="nb">setf</span> <span class="vg">*global-ante-earlier-focussed-window*</span> <span class="vg">*global-earlier-focussed-window*</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nv">find-window-globally</span>
           <span class="p">(</span><span class="nf">car</span> <span class="vg">*global-prev-focussed-window*</span><span class="p">)</span> <span class="p">(</span><span class="nv">screen-groups</span> <span class="p">(</span><span class="nv">current-screen</span><span class="p">)))</span>
      <span class="p">(</span><span class="nb">setf</span> <span class="vg">*global-earlier-focussed-window*</span> <span class="vg">*global-prev-focussed-window*</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nv">find-window-globally</span>
           <span class="p">(</span><span class="nf">car</span> <span class="vg">*global-cur-focussed-window*</span><span class="p">)</span> <span class="p">(</span><span class="nv">screen-groups</span> <span class="p">(</span><span class="nv">current-screen</span><span class="p">)))</span>
      <span class="p">(</span><span class="nb">setf</span> <span class="vg">*global-prev-focussed-window*</span> <span class="vg">*global-cur-focussed-window*</span><span class="p">))</span>
    <span class="p">(</span><span class="nb">setf</span> <span class="vg">*global-cur-focussed-window*</span> <span class="p">(</span><span class="nc">cons</span> <span class="nv">currwin</span> <span class="p">(</span><span class="nv">current-group</span><span class="p">)))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">find-window-globally</span> <span class="p">(</span><span class="nv">window</span> <span class="nv">group-list</span><span class="p">)</span>
  <span class="s">&#34;Check for presence of window in all groups.&#34;</span>
  <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">group-list</span><span class="p">)</span> <span class="ss">&#39;nil</span><span class="p">)</span>
      <span class="ss">&#39;nil</span>
      <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">member</span> <span class="nv">window</span> <span class="p">(</span><span class="nv">group-windows</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">group-list</span><span class="p">)))</span>
          <span class="nv">window</span>
          <span class="p">(</span><span class="nv">find-window-globally</span> <span class="nv">window</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">group-list</span><span class="p">)))))</span>

<span class="p">(</span><span class="nv">defcommand</span> <span class="nv">switch-to-last-focussed-window</span> <span class="p">()</span> <span class="p">()</span>
  <span class="s">&#34;Switch to last focussed window, irrespective of which group it is in and what group we&#39;re currently in.&#34;</span>
  <span class="p">(</span><span class="k">progn</span>
    <span class="p">(</span><span class="k">if</span>
     <span class="p">(</span><span class="nb">and</span>
      <span class="p">(</span><span class="nf">not</span> <span class="p">(</span><span class="nf">equal</span> <span class="vg">*global-cur-focussed-window*</span> <span class="vg">*global-prev-focussed-window*</span><span class="p">))</span>
      <span class="p">(</span><span class="nb">or</span>
       <span class="c1">;; we&#39;re in the same group [same logic below]</span>
       <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nv">screen-groups</span> <span class="p">(</span><span class="nv">current-screen</span><span class="p">)))</span>
              <span class="p">(</span><span class="nf">cdr</span> <span class="vg">*global-prev-focussed-window*</span><span class="p">))</span>
       <span class="c1">;; or we can switch to the previous group</span>
       <span class="vg">*global-prev-focussed-window*</span><span class="p">))</span>
     <span class="p">(</span><span class="k">progn</span>
       <span class="p">(</span><span class="nv">switch-to-group</span> <span class="p">(</span><span class="nf">cdr</span> <span class="vg">*global-prev-focussed-window*</span><span class="p">))</span>
       <span class="p">(</span><span class="nv">focus-window</span> <span class="p">(</span><span class="nf">car</span> <span class="vg">*global-prev-focussed-window*</span><span class="p">)</span> <span class="no">t</span><span class="p">))</span>
     <span class="p">(</span><span class="k">if</span>
      <span class="p">(</span><span class="nb">and</span>
       <span class="p">(</span><span class="nf">not</span> <span class="p">(</span><span class="nf">equal</span> <span class="vg">*global-cur-focussed-window*</span> <span class="vg">*global-earlier-focussed-window*</span><span class="p">))</span>
       <span class="p">(</span><span class="nb">or</span>
        <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nv">screen-groups</span> <span class="p">(</span><span class="nv">current-screen</span><span class="p">)))</span>
               <span class="p">(</span><span class="nf">cdr</span> <span class="vg">*global-earlier-focussed-window*</span><span class="p">))</span>
        <span class="vg">*global-earlier-focussed-window*</span><span class="p">))</span>
      <span class="p">(</span><span class="k">progn</span>
        <span class="p">(</span><span class="nv">switch-to-group</span> <span class="p">(</span><span class="nf">cdr</span> <span class="vg">*global-earlier-focussed-window*</span><span class="p">))</span>
        <span class="p">(</span><span class="nv">focus-window</span> <span class="p">(</span><span class="nf">car</span> <span class="vg">*global-earlier-focussed-window*</span><span class="p">)</span> <span class="no">t</span><span class="p">))</span>
      <span class="p">(</span><span class="k">if</span>
       <span class="p">(</span><span class="nb">and</span>
        <span class="p">(</span><span class="nf">not</span> <span class="p">(</span><span class="nf">equal</span> <span class="vg">*global-cur-focussed-window*</span> <span class="vg">*global-ante-earlier-focussed-window*</span><span class="p">))</span>
        <span class="p">(</span><span class="nb">or</span>
         <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nv">screen-groups</span> <span class="p">(</span><span class="nv">current-screen</span><span class="p">)))</span>
                <span class="p">(</span><span class="nf">cdr</span> <span class="vg">*global-ante-earlier-focussed-window*</span><span class="p">))</span>
         <span class="vg">*global-ante-earlier-focussed-window*</span><span class="p">))</span>
       <span class="p">(</span><span class="k">progn</span>
         <span class="p">(</span><span class="nv">switch-to-group</span> <span class="p">(</span><span class="nf">cdr</span> <span class="vg">*global-ante-earlier-focussed-window*</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">focus-window</span> <span class="p">(</span><span class="nf">car</span> <span class="vg">*global-ante-earlier-focussed-window*</span><span class="p">)</span> <span class="no">t</span><span class="p">))</span>
       <span class="p">(</span><span class="nv">message</span> <span class="s">&#34;No window to switch to.&#34;</span><span class="p">))))))</span>

<span class="p">(</span><span class="nv">add-hook</span> <span class="vg">*focus-window-hook*</span> <span class="ss">&#39;panrecord-of-last-focussed-window</span><span class="p">)</span>

<span class="c1">;; my binding; set as you will</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;s-f&#34;</span><span class="p">)</span> <span class="s">&#34;switch-to-last-focussed-window&#34;</span><span class="p">)</span>
</code></pre></div><p>The <code>unless</code> statement in <code>panrecord-of-last-focussed-window</code> prevents
my drop-down terminal <a href="https://babbagefiles.xyz/categories/stumpwm">Equake</a> &ldquo;window&rdquo; from &ldquo;counting&rdquo; for history
tracking purposes.</p>
<p>The <code>switch-to-last-focussed-window</code> function essentially just switches
to the last focussed window, after making sure it still exists. (If
not, switch to the window which was focussed before that one, or the
one before that one, or else don&rsquo;t switch and display message
indicating this to the user.)</p>
<p>The last line, <code>(define-key *root-map* (kbd &quot;s-f&quot;) &quot;switch-to-last-focussed-window&quot;)</code>, means that I can double tap <code>s-f</code>
(since my StumpWM prefix key is set to <code>s-f</code> with <code>(set-prefix-key (kbd &quot;s-f&quot;))</code>) to switch to the last focussed window, no matter which group
it belongs to.</p>
<p>I continue to really enjoy the power that StumpWM&rsquo;s Common Lisp
underpinnings provides the user!</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/stumpwm" term="stumpwm" label="stumpwm" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/commonlisp" term="commonlisp" label="commonlisp" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Beautiful and Free Unicode Typefaces, for editor and printer (including a comparison of Latin Modern and Computer Modern Unicode)]]></title>
            <link href="https://babbagefiles.xyz/beautiful-and-free-typefaces/"/>
            <id>https://babbagefiles.xyz/beautiful-and-free-typefaces/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2019-08-04T02:25:00-06:00</published>
            <updated>2020-06-22T09:27:27-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>For my academic papers, I often need a typeface with a wide range of
characters and diacritic combinations. Basic diacritics are supported
by a wide range of fonts, but more specialised diacritics and
particularly combinations of diacritics only work well in a handful of
typefaces. I write my papers in TeX, which has two components: the
typeface used to set the paper in (La)TeX and the typeface/font used
inside Emacs, where I write the papers.</p>
<h2 id="typeset-typefaces">Typeset typefaces</h2>
<p>The choice of typeface is particularly relevant for using XeLaTeX or
LuaLaTeX, where the standard default unicode typeface family is <a href="https://en.wikipedia.org/wiki/Computer%5Fmodern#Latin%5FModern">Latin
Modern</a>, based on <a href="https://en.wikipedia.org/wiki/Donald%5Fknuth">Donald Knuth</a>&lsquo;s original <a href="https://en.wikipedia.org/wiki/Computer%5FModern">Computer Modern</a> family of
typefaces designed in <a href="https://en.wikipedia.org/wiki/Metafont">METAFONT</a>, and the general recommendation is to
use Latin Modern rather than the earlier <a href="http://cm-unicode.sourceforge.net/">Computer Modern Unicode</a>.</p>
<p>Generally reasons given for preferring Latin Modern include the fact
that Latin Modern involves handmade vectorisation, with revised
metrics, and additional glyph coverage, including particularly
diacritic characters; whereas the Computer Modern Unicode fonts were
converted from METAFONT sources using <a href="http://lilypond.org/mftrace/"><code>mftrace</code></a> with <a href="http://autotrace.sf.net/"><code>autotrace</code></a> backend
and <a href="http://fontforge.sf.net/"><code>fontforge</code></a> (former pfaedit) automatically.</p>
<p>Some of the revised metrics of Latin Modern include arguably better
positioning of acute and grave accents, more appropriately-sized
umlauts, and (here tastes may vary) a better looking Eszett (ß).</p>
<p><strong>[Note that throughout this post, you can right-click on the images and
select &ldquo;View Image&rdquo; to see a larger/zoomable version of the image.]</strong></p>
<p>However, in my tests, Computer Modern Unicode actually performs better
at least with respect to handling diacritics. In fact, Computer Modern
Unicode is one of a handful of typefaces which actually properly
handle a large range of diacritics and combinations of diacritics, as
can be seen in the image below, showing a variety of serif, sans
serif, and monospace typefaces, respectively, typeset in XeLaTeX:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/01-diacritictests.png"/> </figure>

<p>[The source file from which all of these samples were generated is
included below in this footnote: <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.]</p>
<p>Note that in many typefaces, including all of the Latin Modern faces,
combinations of acute accents and macrons (e.g. the first character in
the tests: <strong>ā́</strong>, also not handled well by <a href="https://iginomarini.com/fell/">IM Fell</a> Great Primer, the font
this blog <em>used to use</em> for its body text; now switched to Computer
Modern Unicode Sans) are very poorly handled, often with the accent and
macron being overlaid, rather than the accent properly stacking on top
of the macron.</p>
<p>Even fonts that properly handle this often fail for other diacritics:
note that <a href="http://www.georgduffner.at/ebgaramond/">EB Garamond</a> and <a href="https://en.wikipedia.org/wiki/Liberation%5Ffonts">Liberation Serif</a> don&rsquo;t seem to properly
handle underrings (e.g. <strong>R̥r̥</strong>, also not handled that well by IM Fell Great
Primer).</p>
<p>Even the <a href="http://libertine-fonts.org/">Linux Libertine</a> faces (here represented by their <a href="https://github.com/alif-type/libertinus">Libertinus
continuations</a>), which overall fare well, don&rsquo;t handle the acute+macron
combination well in the italic version <em><strong>ā́</strong></em>.</p>
<p>Thus the only serif faces which handle the full range of
characters/diacritics are the <a href="https://en.wikipedia.org/wiki/Didone%5F(typography)">didone</a> (a style arising in the
late 18th-century, popular in the 19th-c.) Computer Modern Unicode;
<a href="https://en.wikipedia.org/wiki/Junicode">Junicode</a> (based on a 17th-century font used for <a href="https://en.wikipedia.org/wiki/George%5FHickes%5F(divine)">George Hickes</a>&lsquo;s
<em>Linguarum Vett. Septentrionalium Thesaurus</em>); and the early
20th-century styled Times New Romanesque <a href="https://en.wikipedia.org/wiki/Noto%5Ffonts">Noto Serif</a>.</p>
<p>For the sans serif faces, in addition to the Computer Modern Unicode
Sans Serif face (in some ways a &lsquo;skeletal&rsquo;, un-didone-ised version of
Computer Modern Roman), Mozilla&rsquo;s <a href="https://en.wikipedia.org/wiki/Fira%5FSans">Fira Sans</a> (here represented by
<a href="https://bboxtype.com/typefaces/FiraGO/#!layout=specimen">FiraGO</a>, an updated/extended version), and <a href="https://fonts.google.com/specimen/Noto+Sans">Noto Sans</a> – the latter two
both being vaguely <a href="https://en.wikipedia.org/wiki/Sans-serif#Humanist">&lsquo;humanist&rsquo;</a> sans serifs, though Noto Sans perhaps
has some <a href="https://en.wikipedia.org/wiki/Sans-serif#Grotesque">&lsquo;american gothic/grotesque&rsquo;</a> features – also manage the full
range of characters/diacritics, but <a href="https://github.com/googlefonts/noto-fonts/issues/1376">doesn&rsquo;t have an italic face</a>.</p>
<p>For the monospace faces, again the Computer Modern Typewriter face (an
<a href="https://en.wikipedia.org/wiki/Slab%5Fserif">&lsquo;Egyptian&rsquo;</a> typewriter face, not unlike Courier) manages the full range
of characters/diacritics, as do Noto Sans Mono and the <a href="https://typeof.net/Iosevka/">Iosevka</a> faces
(here represented by my own customised version, Iosevka Oak Vise).</p>
<p>Not all of these typefaces are of equal beauty. Here are some examples
of running text, single sentences first:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/02-sentencetests.png"/> </figure>

<p>And full paragraphs (for a subset of the typefaces; only including
those that manage at least a majority of the characters and diacritic
combinations):<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/03-serifparas.png"/> </figure>




<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/04-sansparas.png"/> </figure>




<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/05-monoparas.png"/> </figure>

<p>Only a few typefaces handle the full range of characters and diacritic
combinations. For the serif faces:</p>
<ul>
<li>CMU Serif</li>
<li>Noto Serif</li>
<li>Junicode</li>
</ul>
<p>For the sans faces:</p>
<ul>
<li>CMU Sans Serif</li>
<li>Noto Sans</li>
<li>Fira Sans (FiraGO)</li>
</ul>
<p>For the monospaced faces:</p>
<ul>
<li>CMU Typewriter</li>
<li>Noto Sans Mono</li>
<li>Iosevka</li>
</ul>
<p>Here is a comparison only of these &ldquo;winners&rdquo; (and Noto Sans Mono
perhaps is not a winner, lacking an italic face):</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/06-winners.png"/> </figure>

<p>So, if you want to use a font family with good performance across the
full range of fonts, Computer Modern Unicode (CMU) or Noto seem like
the best bets, and the former is much more aesthetically-pleasing than
the latter (in my opinion), which also lacks an italic font for its
mono version. Junicode is also a beautiful and very functional serif
face, which I often use.</p>
<p>The Latin Modern faces turn out, despite the received wisdom, to be
inferior in terms of diacritic coverage, and switching from Latin
Modern to Computer Modern Unicode (CMU) has significantly reduced the
amount of frustration I have in trying to typeset my papers.</p>
<h2 id="editor-typefaces">Editor typefaces</h2>
<p>The editor font is the one <strong>I</strong> spend most of my time staring at, so I
also want something good here too.</p>
<p>Despite DejaVu Sans Mono apparently not properly rendering all
character/diacritic combinations in LaTeX, it handles all of these
perfectly well when used as the default font in Emacs:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/dejavusansmono-emacs.png"/> </figure>

<p><a href="https://babbagefiles.blogspot.com/2014/12/in-praise-of-dejavu-sans-mono.html">DejaVu Sans Mono was for many years my preferred font</a> for Emacs (and I
assume DejaVu Sans Mono-derived fonts like <a href="https://en.wikipedia.org/wiki/Menlo%5F(typeface)">Menlo</a> or <a href="https://sourcefoundry.org/hack/">Hack</a> will also
behave well).</p>
<p>Noto Sans Mono also works fine, but it&rsquo;s not my favourite:
<img src="/ox-hugo/notosansmono-emacs.png" alt=""></p>
<p>CMU Typewriter, despite rendering well in LaTeX, has problems with
certain combination when used as the Emacs font, and anyway doesn&rsquo;t
look as good as the other choices as an editor font for whatever
reason:<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>
<img src="/ox-hugo/cmutypewriter-emacs.png" alt=""></p>
<p>These days, I prefer to use a customised version of Iosevka, which
also handles all of the characters/diacritic combinations perfectly:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/iosevkaoakvise-emacs.png"/> </figure>

<p>I plan to write more extensively about Iosevka in another post.</p>
<h2 id="summary-best-bets">Summary: Best Bets</h2>
<p>For typesetting papers requiring a full-range of unicode roman
characters and diacritics (for non-roman, the Noto fonts are great),
the <strong>Computer Modern Unicode (CMU)</strong> faces<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
are the best bets, along with <strong>Junicode</strong>.</p>
<p>For editor work requiring a full-range of unicode roman
characters and diacritics, use a <strong>DejaVu Sans Mono</strong> font or derivative,
or one of the <strong>Iosevka</strong> variants.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://gitlab.com/emacsomancer/neobabbage_files/-/raw/master/assets/files/cmu_vs_latinmodern_tests.tex">Source file</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>I&rsquo;m not sure why Latin Modern Typewriter ends up with a ragged right margin. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Ubuntu Mono, when I tried setting it as the default font in Emacs and opening the <code>.tex</code> file from which the pdf screenshots shown here were taken, caused Emacs to crash! <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>E.g., CMU Serif, CMU Sans Serif, CMU Typewriter; there are a number of others, including also <a href="https://en.wikipedia.org/wiki/Concrete%5FRoman">CMU Concrete Roman</a>, used below for the non-title text (the title text is set in Latin Modern Dunhill): <img src="/ox-hugo/dunhill-computermodernconcrete-faces-lambdacalculusslide.png" alt=""> <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/typography" term="typography" label="typography" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/iosevka" term="iosevka" label="iosevka" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/computermodern" term="computermodern" label="computermodern" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/latex" term="latex" label="latex" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/17th-century" term="17th-century" label="17th-century" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/knuth" term="knuth" label="knuth" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Top 50 IF list 2019]]></title>
            <link href="https://babbagefiles.xyz/intfic-top-50-2019/"/>
            <id>https://babbagefiles.xyz/intfic-top-50-2019/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2019-07-30T11:32:00-06:00</published>
            <updated>2019-08-03T12:20:51-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>Emily Short <a href="https://emshort.blog/2019/07/14/a-top-20-list-of-if/">wrote a blog post a fortnight ago or so</a> discussing her
nominees for Victor Gijsbers&rsquo; <a href="https://intfiction.org/t/participate-in-the-2019-interactive-fiction-top-50/">Top 50 Interactive Fiction Games of All
Time list, 2019 edition</a>. The contest closes on the 31st of July 2019
(i.e. in 2 days, as of the day I write this), and I was thinking about
what games would be on my list. This has also resulted, perhaps more
importantly, with me having a list of games I still need to play.</p>
<p>Both lists below have the games hyperlinked to their <a href="https://ifdb.tads.org/">Interactive
Fiction Database</a> page, which generally includes reviews and game files
to download (or links/suggestions of where to get the game).</p>
<p>First, my personal list of favourites/best text adventures/interactive
fiction that I&rsquo;ve completed or at least played most (er, at least
half?) of, ordered roughly in the order I encountered them:</p>
<table>
<thead>
<tr>
<th>Game</th>
<th>Year</th>
<th>Author</th>
<th>Publisher</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=0dbnusxunq7fw5ro">Zork I</a></td>
<td>1980</td>
<td>Marc Blank &amp; Dave Lebling</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=yzzm4puxyjakk8c4">Zork II</a></td>
<td>1981</td>
<td>Dave Lebling &amp; Marc Blank</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=vu4xhul3abknifcr">Enchanter</a></td>
<td>1983</td>
<td>Marc Blank &amp; Dave Lebling</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=xe6kb3cuqwie2q38">Planetfall</a></td>
<td>1983</td>
<td>Steve Meretzky</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=ouv80gvsl32xlion">The Hitchhiker&rsquo;s Guide to the Galaxy</a></td>
<td>1984</td>
<td>Douglas Adams &amp; Steve Meretzky</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=z02joykzh66wfhcl">Wishbringer</a></td>
<td>1985</td>
<td>Brian Moriarty</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=a3ege93gjofe5q0m">The Pawn</a></td>
<td>1985</td>
<td>Rob Steggles et al.</td>
<td>Magnetic Scrolls</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=wqsmrahzozosu3r">Spellbreaker</a></td>
<td>1985</td>
<td>Dave Lebling</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=bu2v2j5sqpf4usex">The Guild of Thieves</a></td>
<td>1987</td>
<td>Rob Steggles</td>
<td>Magnetic Scrolls</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=laiyfhqfopw0x9r6">Knight Orc</a></td>
<td>1987</td>
<td>Pete Austin</td>
<td>Level 9</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=ig3zbeoqfv4v1xl8">Dunnet</a></td>
<td>1982</td>
<td>Ron Schnell</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=plvzam05bmz3enh8">Curses!</a></td>
<td>1993</td>
<td>Graham Nelson</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=2xyccw3pe0uovfad">Spider &amp; Web</a></td>
<td>1998</td>
<td>Andrew Plotkin</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=aearuuxv83plclpl">Counterfeit Monkey</a></td>
<td>2012</td>
<td>Emily Short</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=g0fl99ovcrq2sqzk">Coloratura</a></td>
<td>2013</td>
<td>Lynnea Glasser</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=etul31tqgl3n22nl">Mentula Mancanus: Apocolocyntosis</a></td>
<td>2011</td>
<td>Adam Thornton</td>
<td></td>
</tr>
</tbody>
</table>
<p>Some of these may be being boosted by nostalgia, but I think they all
pretty much hold up and are interesting/innovative in some fashion.</p>
<p>Looking over the author names, I remember when playing Infocom games
back in the 80s suspecting that &ldquo;Marc Blank&rdquo; was a
pseudonym&hellip;.i.e. Last Name: [blank].</p>
<p><a href="https://en.wikipedia.org/wiki/Dunnet_(video_game)">Dunnet</a> is an interesting entry also in that it was originally written
in Maclisp for the DECSYSTEM-20, then ported to Emacs Lisp in 1992,
and it is the Emacs version (<code>M-x dunnet</code>) that I&rsquo;m familiar with.</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/dunnet-emacs.png"/> </figure>

<p>A list of games I still need to play (never played before) or finish
(haven&rsquo;t made significant progress, though I spent a pretty long time
with the Silicon Dreams games, Anchorhead, and Varicella, but don&rsquo;t
think I&rsquo;ve made much progress (or I&rsquo;ve forgotten what progress I
made)), roughly in order of precedence (i.e. how soon I plan to play):</p>
<table>
<thead>
<tr>
<th>Game</th>
<th>Year</th>
<th>Author</th>
<th>Publisher</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=op0uw1gn1tjqmjt7">Anchorhead</a></td>
<td>1998</td>
<td>Michael Gentry</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=4x7nltu8p851tn4x">Cragne Manor</a></td>
<td>2018</td>
<td>[numerous]</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=urxrv27t7qtu52lb">Galatea</a></td>
<td>2000</td>
<td>Emily Short</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=p0cizeb3kiwzlm2p">Savoir-Faire</a></td>
<td>2002</td>
<td>Emily Short</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=u58d0mlbfwcorfi">Hadean Lands</a></td>
<td>2014</td>
<td>Andrew Plotkin</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=ywwlr3tpxnktjasd">Varicella</a></td>
<td>1999</td>
<td>Adam Cadre</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=7vtm1rq16hh3xch">Endless, Nameless</a></td>
<td>2012</td>
<td>Adam Cadre</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=9p8kh3im2j9h2881">Bronze</a></td>
<td>2006</td>
<td>Emily Short</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=x6ne0bbd2oqm6h3a">Balances</a></td>
<td>1994</td>
<td>Graham Nelson</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=28uhmejlntcbccqm">Jigsaw</a></td>
<td>1995</td>
<td>Graham Nelson</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=ez2mcyx4zi98qlkh">Blue Lacuna</a></td>
<td>2008</td>
<td>Aaron Reed</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=y9y7jozi0l76bb82">A Beauty Cold and Austere</a></td>
<td>2017</td>
<td>Mike Spivey</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=b8mb4fcwmf1hrxl">Lime Ergot</a></td>
<td>2014</td>
<td>Caleb Wilson (as Rust Blight)</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=xkai23ry99qdxce3">Suveh Nux</a></td>
<td>2007</td>
<td>David Fisher</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=m85rnpq6x77jyzc3">Delusions</a></td>
<td>1996</td>
<td>C. E. Forman</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=032krqe6bjn5au78">Slouching Towards Bedlam</a></td>
<td>2003</td>
<td>Star Foster &amp; Daniel Ravipinto</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=4h62dvooeg9ajtfa">A Mind Forever Voyaging</a></td>
<td>1985</td>
<td>Steve Meretzky</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=j18kjz80hxjtyayw">Trinity</a></td>
<td>1986</td>
<td>Brian Moriarty</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=6lgu6t1f65qrdf7o">Snowball</a> [Silicon Dreams]</td>
<td>1983</td>
<td>Mike Austin et al.</td>
<td>Level 9</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=zr48drl7ctjw0v9a">Return to Eden</a> [Silicon Dreams]</td>
<td>1984</td>
<td>Nick Austin et al.</td>
<td>Level 9</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=ohwkcbpfdtjrzrz6">The Worm in Paradise</a> [Silicon Dreams]</td>
<td>1985</td>
<td>Mike Austin et al.</td>
<td>Level 9</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=g79qfkq3m3dtffq4">The Shadow in the Cathedral</a></td>
<td>2009</td>
<td>Ian Finley &amp; John Ingold</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=jdrbw1htq4ah8q57">Make It Good</a></td>
<td>2009</td>
<td>Jon Ingold</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=00e0t7swrris5pg6">1893: A World&rsquo;s Fair Mystery</a></td>
<td>2002</td>
<td>Peter Nepstad</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=yrdxe16idkn87bo8">Red Moon</a></td>
<td>1985</td>
<td>David Williamson et al.</td>
<td>Level 9</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=hsfc7fnl40k4a30q">Shade</a></td>
<td>2000</td>
<td>Andrew Plotkin</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=9nlbhqnlyb169uge">Stationfall</a></td>
<td>1987</td>
<td>Steve Meretzky</td>
<td>Infocom</td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=0stz0hr7a98bp9mp">Rameses</a></td>
<td>2000</td>
<td>Stephen Bond</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=10387w68qlwehbyq">The Moonlit Tower</a></td>
<td>2002</td>
<td>Yoon Ha Lee</td>
<td></td>
</tr>
<tr>
<td><a href="https://ifdb.tads.org/viewgame?id=zjyxds3s57pgis3x">Bureaucracy</a></td>
<td>1987</td>
<td>Douglas Adams</td>
<td>Infocom</td>
</tr>
</tbody>
</table>
<p>All of the games listed here, in either list, are playable in either
<a href="https://melpa.org/#/malyon">Malyon</a> or <a href="http://ccxvii.net/gargoyle/">Gargoyle</a> (with the exception, I think, of Hadean Lands), an admittedly
rather arbitrary criterion.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/intfic" term="intfic" label="intfic" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/zork" term="zork" label="zork" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Running pdfpc in StumpWM]]></title>
            <link href="https://babbagefiles.xyz/pdfpc-in-stumpwm/"/>
            <id>https://babbagefiles.xyz/pdfpc-in-stumpwm/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2019-07-29T15:27:00-06:00</published>
            <updated>2019-07-29T15:30:25-06:00</updated>
            
            
            <content type="html"><![CDATA[<p><a href="https://pdfpc.github.io/"><code>pdfpc</code></a> is a fantastic application for presenting PDF slides,
including perhaps especially those produced using <a href="https://ctan.org/pkg/beamer">LaTeX Beamer</a>. It
creates two (full-screen) windows, one a presenter viewer which shows
the time elapsed and a preview of the next slide, and one the
presentation view which is what is shown to the audience. It also has
a bunch of other cool features like being able to draw on slides;
highlight areas of slides, &amp;c.</p>
<p>Here is an example, with the presenter&rsquo;s view shown on the left; the
audience&rsquo;s view on the right:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/pdfpc-screenshot01.png"/> </figure>

<p>In many environments <code>pdfpc</code> is pretty smart about getting the
presentation view to the external display, but in StumpWM both end up
getting created in the same frame in the same display.</p>
<p>(General tip: in StumpWM, after connecting/activating an external
display, you may need to run <a href="https://stumpwm.github.io/git/stumpwm-git_9.html#External-Monitors"><code>refresh-heads</code></a> to get StumpWM to display
things properly.)</p>
<p>For whatever reason, I can&rsquo;t get any of the <a href="https://stumpwm.github.io/git/stumpwm-git_9.html">Screen-related things</a> in
StumpWM to behave like I have more than one
screen. E.g. <code>*screen-list*</code> shows a singleton list even when an
external display is connected and activated:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="nv">STUMPWM&gt;</span> <span class="vg">*screen-list*</span>
<span class="p">(</span><span class="err">#</span><span class="nv">S&lt;screen</span> <span class="err">#</span><span class="nv">&lt;XLIB:SCREEN</span> <span class="ss">:0.0</span> <span class="nv">1366x768x24</span> <span class="nv">TRUE-COLOR&gt;&gt;</span><span class="p">)</span>
</code></pre></div><p>A passable solution is to use <a href="https://stumpwm.github.io/git/stumpwm-git_5.html#Rule-Based-Window-Placement"><code>define-frame-preference</code></a>, adding the
following to your <code>init.lisp</code> StumpWM configuration:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="c1">;; pdfpc rule - for &#39;Default&#39; group</span>
<span class="c1">;;  move to frame &#39;1&#39;;</span>
<span class="c1">;;  non-focussing (&#39;nil) the presentation view;</span>
<span class="c1">;;  not only matching (t) the target-group (&#39;Default&#39;);</span>
<span class="c1">;;  moving the &#39;pdfpc&#39; &#39;presentation&#39; window</span>
<span class="p">(</span><span class="nv">define-frame-preference</span> <span class="s">&#34;Default&#34;</span>
  <span class="p">(</span><span class="mi">1</span> <span class="no">nil</span> <span class="no">t</span> <span class="ss">:instance</span> <span class="s">&#34;pdfpc&#34;</span> <span class="ss">:role</span> <span class="s">&#34;presentation&#34;</span><span class="p">))</span>
</code></pre></div><p>This should properly move the presentation window to the external
display when <code>pdfpc</code> is run.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/stumpwm" term="stumpwm" label="stumpwm" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/pdfpc" term="pdfpc" label="pdfpc" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/pdf" term="pdf" label="pdf" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/presentation" term="presentation" label="presentation" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/beamer" term="beamer" label="beamer" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Guix, Nix: You are in a maze of twisty little $PATHs, some undefined]]></title>
            <link href="https://babbagefiles.xyz/guix-nix-maze-of-twisty-little-paths-undefined/"/>
            <id>https://babbagefiles.xyz/guix-nix-maze-of-twisty-little-paths-undefined/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2019-07-24T00:48:00-06:00</published>
            <updated>2019-07-24T16:58:13-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>Some notes on interactive fiction/text adventure games and PATHs in
Guix, and StumpWM.</p>
<h2 id="maze-no-dot-1">Maze no. 1</h2>
<p>There may (likely is) some way of programmatically setting the X
Windows PATH variable in Guix System (née GuixSD) via the base
configuration (e.g. <code>config.scm</code>), but I haven&rsquo;t been able to uncover
anything that works. This is relevant for being able to use locally
installed static binaries or local shell scripts via the window
manager.</p>
<p>As a window-manager-specific workaround, in StumpWM, one can
programmatically set PATH variables via <code>(setf (getenv &quot;VARIABLE_NAME&quot;) &quot;variable-value&quot;)</code>. Thus, if you store local static
binaries and shell scripts in <code>~/bin</code>, the following (which you could
include in StumpWM&rsquo;s <code>init.lisp</code>) will add that to your PATH variable:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nb">setf</span> <span class="p">(</span><span class="nv">getenv</span> <span class="s">&#34;PATH&#34;</span><span class="p">)</span> <span class="p">(</span><span class="nv">concat</span> <span class="s">&#34;/home/YOURUSERNAME/bin:&#34;</span> <span class="p">(</span><span class="nv">getenv</span> <span class="s">&#34;PATH&#34;</span><span class="p">)))</span>
</code></pre></div><p>I use this with a static Haskell binary <a href="https://github.com/erebe/greenclip">greenclip</a>, which adds clipboard
functionality to <a href="https://github.com/davatorium/rofi/">rofi</a>, and with shell scripts that give &ldquo;pretty names&rdquo;
to Flatpak run commands.</p>
<p>For example, the literate/natural-language-based programming
interactive fiction design language <a href="http://inform7.com/">Inform7</a> (which is due to be
<a href="http://inform7.com/talks/2019/06/14/narrascope.html">open-sourced sometime this year</a>) is now conveniently <a href="https://flathub.org/apps/details/com.inform7.IDE">available as a
Flatpak</a>. But the run command after installing is <code>flatpak run com.inform7.IDE</code>, which is non-ideal. So I made a simple shell script
named <code>inform7</code> placed in <code>~/bin</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="cp">#!/bin/sh
</span><span class="cp"></span>flatpak run com.inform7.IDE
</code></pre></div>


<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/inform7-guix.png"/> </figure>

<h2 id="maze-no-dot-2">Maze no. 2</h2>
<p><a href="https://nixos.org/">Nix</a> can be <a href="https://nixos.org/releases/nix/nix-1.9/manual/#ch-installing-binary">installed as a standalone package manage on top of other
distros</a>, including Guix System, which is useful for be able to obtain
software currently lacking in Guix System (including, ironically,
<a href="https://en.wikipedia.org/wiki/Hugo_(software)">Hugo</a>, used by this blog, which is present in Nix). Packages available
in Nix but not in Guix include <a href="http://ccxvii.net/gargoyle/">Gargoyle</a>, a very nice interactive
fiction front-end client that supports a number of different backends,
including Frotz and Glulxe. One of the benefits of Gargoyle is that it
&ldquo;cares about typography&rdquo;. However, Nix applications by default seem to
have trouble finding/seeing fonts, including system fonts, local
fonts, and even fonts installed via Nix.</p>
<p>This can be fixed by (1) setting the <code>FONTCONFIG_PATH</code> and
<code>FONTCONFIG_FILE</code>, e.g. in StumpWM this can be done with:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nb">setf</span> <span class="p">(</span><span class="nv">getenv</span> <span class="s">&#34;FONTCONFIG_PATH&#34;</span><span class="p">)</span> <span class="s">&#34;/home/YOURUSERNAME/.config/fontconfig/&#34;</span><span class="p">)</span>
<span class="p">(</span><span class="nb">setf</span> <span class="p">(</span><span class="nv">getenv</span> <span class="s">&#34;FONTCONFIG_FILE&#34;</span><span class="p">)</span> <span class="s">&#34;fonts.conf&#34;</span><span class="p">)</span>
</code></pre></div><p>And (2) forcing Nix to look in the right places by manual
specification in <code>~/.config/fontconfig/fonts.conf</code>, adding right
before the final <code>&lt;/fontconfig&gt;</code> (as appropriate):</p>
<pre><code class="language-nil" data-lang="nil">&lt;cachedir prefix=&quot;xdg&quot;&gt;fontconfig&lt;/cachedir&gt;
&lt;dir&gt;/home/YOURUSERNAME/.local/share/fonts/&lt;/dir&gt;
&lt;dir&gt;/home/YOURUSERNAME/.nix-profile/share/fonts/&lt;/dir&gt;
&lt;dir&gt;/home/YOURUSERNAME/.guix-profile/share/fonts/&lt;/dir&gt;
&lt;dir&gt;/usr/share/fonts&lt;/dir&gt;
</code></pre><p>And regenerating the font cache (via <code>fc-cache -fv</code>) [possibly you may
need to install Nix&rsquo;s <code>fontconfig</code> package].</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/counterfeit-gargoyle-screenshot.png"/> </figure>

]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/guix" term="guix" label="guix" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/linux" term="linux" label="linux" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/stumpwm" term="stumpwm" label="stumpwm" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/intfic" term="intfic" label="intfic" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/fonts" term="fonts" label="fonts" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/inform" term="inform" label="inform" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/nix" term="nix" label="nix" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/gargoyle" term="gargoyle" label="gargoyle" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Semi-automated installation of Void Linux on pure ZFS with full LUKS disk encryption]]></title>
            <link href="https://babbagefiles.xyz/full-zfs-full-luks-on-void-linux/"/>
            <id>https://babbagefiles.xyz/full-zfs-full-luks-on-void-linux/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2019-06-17T15:53:01-06:00</published>
            <updated>2019-06-17T16:23:39-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>After spending too much time repeatedly trying to set up a Void Linux
installation using &lsquo;pure ZFS&rsquo; and full-disk LUKS encryption, I ended
up writing up a set of BASH scripts to automate (and, effectively,
document) the installation process, and prevent me from forgetting
steps along the way. There&rsquo;s quite a number of potential stumbling
block along the way (such as <code>grub-probe</code> not knowing how to properly
find the root drive) which I figured out ways around, as well as
discovering that Void&rsquo;s <code>zfs-0.8.0</code> package was
<a href="https://github.com/void-linux/void-packages/issues/12465">missing a
<code>python3</code> dependency which caused ZFS DKMS builds to fail</a>.</p>



<figure>
    
        
            <img src="https://gitlab.com/emacsomancer/full-zfs-and-full-luks-encryption-on-void-linux/raw/master/misc/void-on-zfs.png" alt="Void Linux on ZFS" width="300"/> </figure>

<p>The scripts are more or less automated if you&rsquo;re installing in a
particular fashion. They&rsquo;ll ask for user input along the way for
configuring/customising certain things.  What isn&rsquo;t covered is a setup
with multiple vdevs or UEFI or musl, but if you want these things
you&rsquo;ll probably be able to patch the scripts accordingly and perhaps
these options could be accommodated in a future version.</p>
<p>The scripts live here:
<a href="https://gitlab.com/emacsomancer/full-zfs-and-full-luks-encryption-on-void-linux">https://gitlab.com/emacsomancer/full-zfs-and-full-luks-encryption-on-void-linux</a>
, where you&rsquo;ll find additional instructions and information.</p>
<p>(I do recommend using the <a href="https://ubuntu.com/download/desktop">Ubuntu Live ISO</a> as your installer &lsquo;host&rsquo; for
ease and reduction of the installation time: the &lsquo;host&rsquo; for the
installation doesn&rsquo;t really matter: basically it&rsquo;s just being used to
run the initial <code>cryptsetup</code> for the LUKS partition and initial ZFS
pool creation and the host for the Void chroot. The Ubuntu Live CD has
ZFS baked in, so you don&rsquo;t have to wait twice(!) for DKMS to build ZFS
modules.)</p>
<p>Using ZFS for the entire system, from <code>/</code> to <code>/home</code> to <code>/boot</code> also
has the advantage of not requiring you to decide how much space to
allocation ahead of time. With <code>/boot</code> on a separate partition, I&rsquo;ve
sometimes encountered issues of running out of space on <code>/boot</code>
because of maintaining multiple kernels, or else having to massively
overshoot in terms of how much space to give to <code>/boot</code>. A full ZFS
install avoids this issue, <strong>and</strong> allows for easy snapshots of the
<code>/boot</code> directory.</p>
<p>ZFS is a great file-system if you care about your data. ZFS is most
impressive file-system, and it has a number of other wonderful
features aside from data-integrity, and once you&rsquo;re used to it, you&rsquo;ll
want it everywhere.  For instance, the &lsquo;default&rsquo; lz4 compression is
effectively &lsquo;free&rsquo;, in terms of CPU usage (minor CPU hit for dealing
with compression is offset by the need to process smaller chunks of
data), and can be significant: on my root dataset
(<code>dozer/ROOT/system</code>) I&rsquo;m currently getting 1.79x compression: so 22.7G
of logical data is written in 13.6G, and even my dataset full of PDFs
has a more modest 1.03x compression ratio, but this means I save over
3G.</p>
<p>ZFS 0.8.0 also brings with it native encryption. I&rsquo;ve not chosen to
use this at the moment, as LUKS makes a full-disk setup easier at
this point, but native encryption could be used in conjunction with
LUKS encryption (potentially useful if, say, you want to backup up
particular ZFS datasets to a remote and not entirely trusted
machine:– natively encrypted ZFS snapshots can be sent without
decrypting the data).</p>
<p>All of these various features, such as compression and encryption can
be enabled per dataset, which allows for great flexibility. After
suffering bit rot, which filtered through and rendered pointless my
carefully maintained versioned backups, I really don&rsquo;t like trusting
my data to any other file-system.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/zfs" term="zfs" label="zfs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/linux" term="linux" label="linux" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/automation" term="automation" label="automation" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/encryption" term="encryption" label="encryption" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/luks" term="luks" label="luks" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Auto-generate “creator” PDF metadata in AUCTeX using yasnippet]]></title>
            <link href="https://babbagefiles.xyz/auto-gen-hyperref-auctex-yasnippet/"/>
            <id>https://babbagefiles.xyz/auto-gen-hyperref-auctex-yasnippet/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2019-05-29T22:59:00-06:00</published>
            <updated>2020-04-27T13:31:00-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>After struggling with some poorly-handled, apparently &ldquo;reset&rdquo; proofs
introducing heaps of errors (despite my providing a <code>.tex</code> source) for
the past few days,<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> I thought about providing automated pdf-tags
indicating creation tools used for my TeX-produced documents. Real,
professionally-typeset documents deserve to have the tools used to
produce them properly recognised in their metadata. So here&rsquo;s a
yasnippet which generates auto-populated <code>hyperref</code> options to
generate a <code>pdf-creator</code> tag indicating the version of Emacs, AUCTeX,
and distro used:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="err">#</span> <span class="nv">-*-</span> <span class="nv">mode:</span> <span class="nv">snippet</span> <span class="nv">-*-</span>
<span class="err">#</span> <span class="nv">name:</span> <span class="nv">version-hyperref</span>
<span class="err">#</span> <span class="nv">key:</span> <span class="nv">hyperv</span>
<span class="err">#</span> <span class="nv">--</span>
<span class="nv">\usepackage[pdfusetitle,</span>
 <span class="nv">pdfcreator={</span><span class="o">`</span><span class="p">(</span><span class="nv">replace-regexp-in-string</span> <span class="s">&#34;_&#34;</span> <span class="s">&#34;-&#34;</span>
  <span class="p">(</span><span class="nv">concat</span> <span class="p">(</span><span class="nv">replace-regexp-in-string</span> <span class="s">&#34;\n&#34;</span> <span class="s">&#34;&#34;</span> <span class="p">(</span><span class="nv">substring</span> <span class="p">(</span><span class="nv">emacs-version</span><span class="p">)</span> <span class="mi">0</span> <span class="p">(</span><span class="nf">1-</span> <span class="p">(</span><span class="nv">cl-search</span> <span class="s">&#34;(&#34;</span> <span class="p">(</span><span class="nv">emacs-version</span><span class="p">)))</span> <span class="p">))</span>
     <span class="s">&#34; with AUCTeX &#34;</span> <span class="p">(</span><span class="nv">pkg-info-version-info</span> <span class="ss">&#39;auctex</span><span class="p">)</span>
   <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;type -p lsb_release &gt; /dev/null&#34;</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">concat</span>
        <span class="s">&#34; on &#34;</span>
      <span class="p">(</span><span class="nv">substring</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;lsb_release -sd&#34;</span><span class="p">)</span> <span class="mi">1</span> <span class="p">(</span><span class="nf">-</span> <span class="p">(</span><span class="nf">length</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;lsb_release -sd&#34;</span><span class="p">))</span> <span class="mi">2</span><span class="p">))</span>
        <span class="s">&#34; (&#34;</span>
      <span class="p">(</span><span class="nv">substring</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;lsb_release -sr&#34;</span><span class="p">)</span> <span class="mi">0</span> <span class="p">(</span><span class="nf">-</span> <span class="p">(</span><span class="nf">length</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;lsb_release -sr&#34;</span><span class="p">))</span> <span class="mi">1</span><span class="p">))</span>
        <span class="s">&#34; &#39;&#34;</span>
      <span class="p">(</span><span class="nv">substring</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;lsb_release -sc&#34;</span><span class="p">)</span> <span class="mi">0</span> <span class="p">(</span><span class="nf">-</span> <span class="p">(</span><span class="nf">length</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;lsb_release -sc&#34;</span><span class="p">))</span> <span class="mi">1</span><span class="p">))</span>
         <span class="s">&#34;&#39;&#34;</span> <span class="s">&#34; release, using the &#34;</span>
      <span class="p">(</span><span class="nv">replace-regexp-in-string</span> <span class="s">&#34;\n$&#34;</span> <span class="s">&#34;&#34;</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;uname -r&#34;</span><span class="p">))</span> <span class="s">&#34; kernel)&#34;</span><span class="p">)</span>
     <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;type -p guix &gt; /dev/null&#34;</span><span class="p">)</span>
       <span class="p">(</span><span class="nv">concat</span>
         <span class="s">&#34;on Guix System &#34;</span>
       <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;guix system -V | awk &#39;NR==1{printf $5}&#39;&#34;</span><span class="p">)</span>
         <span class="s">&#34; (using the &#34;</span>
       <span class="p">(</span><span class="nv">replace-regexp-in-string</span> <span class="s">&#34;\n$&#34;</span> <span class="s">&#34;&#34;</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="s">&#34;uname -r&#34;</span><span class="p">))</span> <span class="s">&#34; kernel)&#34;</span><span class="p">)))))</span><span class="o">`</span><span class="nv">}]{hyperref}</span>
</code></pre></div><p>Snippets can execute elisp code placed between <code>`...`</code>. The version of
Emacs (with some of the additional less-relevant information removed
via <code>substring</code> combined with <code>cl-search</code> (for &ldquo;(&quot;, the beginning of
the additional information)), and AUCTeX can easily be done using
Emacs-internal functions, <code>(emacs-version)</code> and
<code>(pkg-info-version-info 'auctex)</code>, respectively.</p>
<p>For the operating-system information, we rely on external calls to the
shell via Emacs&rsquo;s <code>shell-command-to-string</code> function. Many distros
will have <code>lsb_release</code> application (from the <a href="https://en.wikipedia.org/wiki/Linux%5FStandard%5FBase">Linux Standard Base
project</a>) available (and presumably usually part of the
base-install). Running <code>lsb_release --help</code> reveals a helpful set of
options:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">SYNOPSIS
      lsb_release <span class="o">[</span>OPTION<span class="o">]</span>...
OPTIONS
      −v, −−version
      Display the version of the LSB specification against which the distribution is compliant.

      −i, −−id
      Display the string id of the distributor.

      −d, −−description
      Display the single line text description of the distribution.

      −r, −−release
      Display the release number of the distribution.

      −c, −−codename
      Display the codename according to the distribution release.

      −a, −−all
      Display all of the above information.

      −s, −−short
      Display all of the above information in short output format.

      −h, −−help
      Display this message.
LSB Version:	1.0
</code></pre></div><p>And we can get various bits of information about the distribution out
via passing different flags to <code>lsb_release</code> (combined with the <code>-s</code>
&ldquo;short description&rdquo; flag).</p>
<p>Some distros don&rsquo;t use the <code>lsb_release</code>, and looking at <a href="https://github.com/dylanaraps/neofetch/blob/master/neofetch">various
methods <code>neofetch</code> employs to manage to suss out the distro</a> was
instructive. I added a method to allow for detecting the version of
<a href="https://www.gnu.org/software/guix/">Guix System</a> distribution, and it could be easily extended (in
neofetch-style) to employ other methods for additional distros. (But
the <code>lsb_release</code> method should handle the majority of distros.)</p>
<p>Finally, we get can the kernel version out via the ubiquitous <code>uname</code>
application, via <code>uname -r</code>.</p>
<p>Thus calling this snippet on <a href="https://voidlinux.org/">Void Linux</a> results in a TeX block like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-latex" data-lang="latex"><span class="k">\usepackage</span>[pdfusetitle,
pdfcreator=<span class="nb">{</span>GNU Emacs 26.2 with AUCTeX 12.1.2 on Void Linux
(rolling &#39;void&#39; release, using the 5.1.5-1 kernel)<span class="nb">}</span>]<span class="nb">{</span>hyperref<span class="nb">}</span>
</code></pre></div><p>(The <code>hyperref</code> option <code>pdfusetitle</code> will make use of the values of
the TeX-defined <code>\author{...}</code> and <code>\title={...}</code> commands to populate
the author and title pdf metadata. If you do anything funky with these
fields (like adding a <code>\thanks{...}</code> command to your title), you may
need to take out the <code>pdfusetitle</code> option and manually specify these
tags via <code>pdfauthor={...}</code> etc.)</p>
<p>On <a href="https://www.gnu.org/software/guix/">Guix System</a> (née GuixSD) the result will be something like:</p>
<div class="highlight"><pre class="chroma"><code class="language-latex" data-lang="latex"><span class="k">\usepackage</span>[pdfusetitle,
pdfcreator=<span class="nb">{</span>GNU Emacs 26.2 with AUCTeX 12.1.2
on Guix System 1.0.1-1.8204295 (using the 5.1.2-gnu kernel)<span class="nb">}</span>]<span class="nb">{</span>hyperref<span class="nb">}</span>
</code></pre></div><p>An amusing side-discovery of putting this snippet together is that
<a href="https://www.archlinux.org/">Arch Linux</a> multiply – and insistently – reminds the <code>uname</code> caller of
the distro the kernel belongs to. I.e., using this snippet on my Arch
machine resulted in:</p>
<div class="highlight"><pre class="chroma"><code class="language-latex" data-lang="latex"> <span class="k">\usepackage</span>[pdfusetitle,
pdfcreator=<span class="nb">{</span>GNU Emacs 26.2 with AUCTeX 12.1.2 on
Arch Linux (rolling &#39;n/a&#39; release, using the 5.1.5-arch1-2-ARCH kernel)<span class="nb">}</span>]<span class="nb">{</span>hyperref<span class="nb">}</span>
</code></pre></div><section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>The horrifying reality of &ldquo;professional&rdquo; desktop publishing software stands in contrast to real professional documents produced in (La)TeX; and any resulting professionalism in the output of the former results from squeezing <strong>additional</strong> unpaid labour from researchers who have to correct the errors of &ldquo;professional&rdquo; typesetters. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/latex" term="latex" label="latex" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/auctex" term="auctex" label="auctex" />
                             
                                <category scheme="https://babbagefiles.xyz/tags/yasnippet" term="yasnippet" label="yasnippet" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[youtube-dl bash shell function: prefer mp4, and timestamp with download time]]></title>
            <link href="https://babbagefiles.xyz/youtube-dl-mp4-downloadtime/"/>
            <id>https://babbagefiles.xyz/youtube-dl-mp4-downloadtime/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2019-05-27T22:17:00-05:00</published>
            <updated>2025-02-15T23:06:21-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>In case it&rsquo;s useful, a quick Bash shell function which takes a single
argument (a web address that is processable by <code>youtube-dl</code>) and
returns the best quality mp4 version (in case you need to deal with a
device that doesn&rsquo;t like modern video encodings/containers), with the
<strong>download time</strong> as the file&rsquo;s modification time timestamp (useful if
you have a directory of downloaded videos and want to quickly see the
last N files you downloaded, rather than the files being sorted by
upload time).</p>
<p>Add to your <code>~/.bashrc</code> or other shell configuration file as appropriate:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">youtubemp4<span class="o">()</span> <span class="o">{</span>
    youtube-dl -f <span class="s1">&#39;bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4&#39;</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
    touch <span class="s2">&#34;</span><span class="k">$(</span>youtube-dl -f <span class="s1">&#39;bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4&#39;</span> --get-filename <span class="nv">$1</span><span class="k">)</span><span class="s2">&#34;</span>
<span class="o">}</span>
</code></pre></div><p>Then call in the command-line, e.g. <code>youtubemp4 https://www.youtube.com/watch?v=Gnnb6sjgk3A</code>.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/shell" term="shell" label="shell" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/bash" term="bash" label="bash" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/youtubedl" term="youtubedl" label="youtubedl" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Equake(!) Quake-style overlay console in StumpWM]]></title>
            <link href="https://babbagefiles.xyz/equake-in-stumpwm/"/>
            <id>https://babbagefiles.xyz/equake-in-stumpwm/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2019-05-22T22:26:00-05:00</published>
            <updated>2025-12-21T22:01:03-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>I&rsquo;ve been alternatively using both KDE Plasma 5 and StumpWM on various
machines and have got a working model for using <a href="https://babbagefiles.xyz/equake-elisp-console/">the Equake drop-down</a>
in StumpWM.</p>
<p>The StumpWM <code>#'invoke-equake</code> command hides (using StumpWM native
<code>hide-window</code>, rather than Emacs&rsquo;s <code>make-frame-invisible</code> as the
latter creates various issues in finding and fetching the Equake
window) the Equake frame if it&rsquo;s the currently active window; it
searches through all windows in all groups on the current
screen/monitor, and calls <code>emacsclient -n -e '(equake-invoke)'</code> to
create an Equake frame if no extant Equake window is found; and if an
Equake window does already exist for the current screen, it is yanked
into the current group, pulled into the current frame, and unhidden (if
necessary).</p>
<p>This seems to work pretty well, though I haven&rsquo;t figured out how to
have StumpWM &lsquo;skip&rsquo; the Equake frame when cycling through Emacs
windows. (And, of course, having a proper partial height floating
Equake window would be ideal.)</p>
<p><strong>EDIT:</strong> Here&rsquo;s a better, more Quake-like set-up, which actually makes
use of recently added partial-height floating windows in non-floating
groups:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">calc-equake-width</span> <span class="p">()</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">screen-width</span> <span class="p">(</span><span class="nf">caddr</span> <span class="p">(</span><span class="nv">with-input-from-stringp</span> <span class="p">(</span><span class="nv">s</span> <span class="p">(</span><span class="nv">run-shell-command</span> <span class="s">&#34;emacsclient -n -e &#39;(equake-find-workarea-of-current-screen (equake-calculate-mouse-location (display-monitor-attributes-list)) (display-monitor-attributes-list))&#39;&#34;</span> <span class="no">t</span><span class="p">))</span> <span class="p">(</span><span class="nf">read</span> <span class="nv">s</span><span class="p">))))</span>
        <span class="p">(</span><span class="nv">desired-width-perc</span> <span class="p">(</span><span class="nf">read-from-string</span> <span class="p">(</span><span class="nv">run-shell-command</span> <span class="s">&#34;emacsclient -n -e &#39;equake-size-width&#39;&#34;</span> <span class="no">t</span><span class="p">))))</span>
    <span class="p">(</span><span class="nf">truncate</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">screen-width</span> <span class="nv">desired-width-perc</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">calc-equake-height</span> <span class="p">()</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">screen-height</span> <span class="p">(</span><span class="nf">cadddr</span> <span class="p">(</span><span class="nb">with-input-from-string</span> <span class="p">(</span><span class="nv">s</span> <span class="p">(</span><span class="nv">run-shell-command</span> <span class="s">&#34;emacsclient -n -e &#39;(equake-find-workarea-of-current-screen (equake-calculate-mouse-location (display-monitor-attributes-list)) (display-monitor-attributes-list))&#39;&#34;</span> <span class="no">t</span><span class="p">))</span> <span class="p">(</span><span class="nf">read</span> <span class="nv">s</span><span class="p">))))</span>
        <span class="p">(</span><span class="nv">desired-height-perc</span> <span class="p">(</span><span class="nf">read-from-string</span> <span class="p">(</span><span class="nv">run-shell-command</span> <span class="s">&#34;emacsclient -n -e &#39;equake-size-height&#39;&#34;</span> <span class="no">t</span><span class="p">))))</span>
    <span class="p">(</span><span class="nf">truncate</span> <span class="p">(</span><span class="nf">*</span> <span class="nv">screen-height</span> <span class="nv">desired-height-perc</span><span class="p">))))</span>

<span class="p">(</span><span class="k">setq</span> <span class="vg">*equake-width*</span> <span class="mi">1368</span><span class="p">)</span> <span class="c1">; TODO: programmatically get screen dimensions before Emacs starts</span>
<span class="p">(</span><span class="k">setq</span> <span class="vg">*equake-height*</span> <span class="mi">768</span><span class="p">)</span>

<span class="p">(</span><span class="nv">defcommand</span> <span class="nv">invoke-equake</span> <span class="p">()</span> <span class="p">()</span>
  <span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">on-top-windows</span> <span class="p">(</span><span class="nv">group-on-top-windows</span> <span class="p">(</span><span class="nv">current-group</span><span class="p">)))</span>
         <span class="p">(</span><span class="nv">equake-on-top</span> <span class="p">(</span><span class="nv">find-equake-in-group</span> <span class="nv">on-top-windows</span><span class="p">)))</span>
    <span class="p">(</span><span class="k">if</span> <span class="nv">equake-on-top</span>
        <span class="p">(</span><span class="k">progn</span> <span class="p">(</span><span class="nb">setf</span> <span class="p">(</span><span class="nv">group-on-top-windows</span> <span class="p">(</span><span class="nv">current-group</span><span class="p">))</span> <span class="p">(</span><span class="nf">remove</span> <span class="nv">equake-on-top</span> <span class="nv">on-top-windows</span><span class="p">))</span>
               <span class="p">(</span><span class="nv">unfloat-window</span> <span class="nv">equake-on-top</span> <span class="p">(</span><span class="nv">current-group</span><span class="p">))</span>
               <span class="p">(</span><span class="nv">hide-window</span> <span class="nv">equake-on-top</span><span class="p">))</span> <span class="c1">;; then hide Equake window via native Stumpwm method.)</span>
      <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">found-equake</span> <span class="p">(</span><span class="nv">find-equake-globally</span> <span class="p">(</span><span class="nv">screen-groups</span> <span class="p">(</span><span class="nv">current-screen</span><span class="p">)))))</span> <span class="c1">; Otherwise, search all groups of current screen for Equake window:</span>
        <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">not</span> <span class="nv">found-equake</span><span class="p">)</span>          <span class="c1">; If Equake cannot be found,</span>
            <span class="p">(</span><span class="k">progn</span>
              <span class="p">(</span><span class="nv">run-shell-command</span> <span class="s">&#34;emacsclient -n -e &#39;(equake-invoke)&#39;&#34;</span><span class="p">)</span> <span class="c1">; then invoke Equake via emacs function.</span>
              <span class="p">(</span><span class="k">setq</span> <span class="vg">*equake-height*</span> <span class="p">(</span><span class="nv">calc-equake-height</span><span class="p">))</span> <span class="c1">; delay calculation of height &amp; width setting until 1st time equake invoked</span>
              <span class="p">(</span><span class="k">setq</span> <span class="vg">*equake-width*</span> <span class="p">(</span><span class="nv">calc-equake-width</span><span class="p">)))</span> <span class="c1">; (otherwise Emacs may not be fully loaded)</span>
          <span class="p">(</span><span class="k">progn</span> <span class="p">(</span><span class="nv">raise-window</span> <span class="nv">found-equake</span><span class="p">)</span>
                   <span class="p">(</span><span class="nv">move-window-to-group</span> <span class="nv">found-equake</span> <span class="p">(</span><span class="nv">current-group</span><span class="p">))</span> <span class="c1">; But if Equake window is found, move it to the current group,</span>
                   <span class="p">(</span><span class="nv">unhide-window</span> <span class="nv">found-equake</span><span class="p">)</span> <span class="c1">; unhide window, in case hidden</span>
                   <span class="p">(</span><span class="nv">float-window</span> <span class="nv">found-equake</span> <span class="p">(</span><span class="nv">current-group</span><span class="p">))</span> <span class="c1">; float window</span>
                   <span class="p">(</span><span class="nv">float-window-move-resize</span> <span class="p">(</span><span class="nv">find-equake-globally</span> <span class="p">(</span><span class="nv">screen-groups</span> <span class="p">(</span><span class="nv">current-screen</span><span class="p">)))</span> <span class="ss">:width</span> <span class="vg">*equake-width*</span> <span class="ss">:height</span> <span class="vg">*equake-height*</span><span class="p">)</span> <span class="c1">; set size</span>
                   <span class="p">(</span><span class="nv">focus-window</span> <span class="nv">found-equake</span><span class="p">)</span>
                   <span class="p">(</span><span class="nv">toggle-always-on-top</span><span class="p">)))))))</span> <span class="c1">; make on top</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">find-equake-in-group</span> <span class="p">(</span><span class="nv">windows-list</span><span class="p">)</span>
  <span class="s">&#34;Search through WINDOWS-LIST, i.e. all windows of a group, for an Equake window. Sub-component of &#39;#find-equake-globally.&#34;</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">current-searched-window</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">windows-list</span><span class="p">)))</span>
    <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">equal</span> <span class="nv">current-searched-window</span> <span class="ss">&#39;nil</span><span class="p">)</span>
        <span class="ss">&#39;nil</span>
        <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">search</span> <span class="s">&#34;*EQUAKE*[&#34;</span> <span class="p">(</span><span class="nv">window-name</span> <span class="nv">current-searched-window</span><span class="p">))</span>
            <span class="nv">current-searched-window</span>
            <span class="p">(</span><span class="nv">find-equake-in-group</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">windows-list</span><span class="p">))))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">find-equake-globally</span> <span class="p">(</span><span class="nv">group-list</span><span class="p">)</span>
  <span class="s">&#34;Recursively search through GROUP-LIST, a list of all groups on current screen, for an Equake window.&#34;</span>
  <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">group-list</span><span class="p">)</span> <span class="ss">&#39;nil</span><span class="p">)</span>
      <span class="ss">&#39;nil</span>
      <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">equake-window</span> <span class="p">(</span><span class="nv">find-equake-in-group</span> <span class="p">(</span><span class="nv">list-windows</span> <span class="p">(</span><span class="nf">car</span> <span class="nv">group-list</span><span class="p">)))))</span>
        <span class="p">(</span><span class="k">if</span> <span class="nv">equake-window</span>
            <span class="nv">equake-window</span>               <span class="c1">; stop if found and return window</span>
            <span class="p">(</span><span class="nv">find-equake-globally</span> <span class="p">(</span><span class="nf">cdr</span> <span class="nv">group-list</span><span class="p">))))))</span>

<span class="c1">;; Set the mouse focus policy;  - :click is best for proper Equake functioning</span>
<span class="p">(</span><span class="nb">setf</span> <span class="vg">*mouse-focus-policy*</span> <span class="ss">:click</span><span class="p">)</span> <span class="c1">;; also StumpWM default (I think)</span>

<span class="c1">;; Keybindings</span>
<span class="c1">;; bind to an appropriate key</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*top-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;F12&#34;</span><span class="p">)</span> <span class="s">&#34;invoke-equake&#34;</span><span class="p">)</span>
</code></pre></div><p>This is what it looks like:
<img src="https://github.com/emacsomancer/equake/raw/master/image/equake-in-stumpwm.gif" alt=""></p>
<p><a href="https://gitlab.com/emacsomancer/equake/raw/master/image/equake-in-stumpwm.webm">video
link here</a></p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/stumpwm" term="stumpwm" label="stumpwm" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/equake" term="equake" label="equake" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/eshell" term="eshell" label="eshell" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Equake: A drop-down console written in Emacs Lisp]]></title>
            <link href="https://babbagefiles.xyz/equake-elisp-console/"/>
            <id>https://babbagefiles.xyz/equake-elisp-console/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2019-01-03T19:06:00-07:00</published>
            <updated>2022-06-19T19:21:55-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>Over the holiday break I&rsquo;ve been working on developing a Quake-style
drop-down console, dubbed Equake / <code>equake</code>. It is not yet on Melpa,
but is accessible at
<a href="https://gitlab.com/emacsomancer/equake">https://gitlab.com/emacsomancer/equake</a>.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>



<figure>
    
        
            <img src="https://gitlab.com/emacsomancer/equake/raw/master/image/equake.png"/> </figure>

<p><code>equake</code>, written fully in Emacs Lisp, is designed as a &lsquo;classic&rsquo;
drop-down console interface like <a href="https://en.wikipedia.org/wiki/Yakuake">Yakuake</a>, inspired by &lsquo;cheat&rsquo; consoles
in games like <a href="https://en.wikipedia.org/wiki/Quake_(video_game)">Quake</a>. It provides access to various &lsquo;shells&rsquo;
implemented in Emacs, including <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Interactive-Shell.html#Interactive-Shell"><code>shell</code></a> (an Emacs wrapper around the
current system shell), <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Terminal-emulator.html#Terminal-emulator"><code>term</code> and <code>ansi-term</code></a>, (both terminal
emulators, emulating VT100-style ANSI escape codes, like <code>xterm</code>
does), and <a href="https://www.gnu.org/software/emacs/manual/html_mono/eshell.html"><code>eshell</code></a> (a shell written entirely in Emacs Lisp). <code>equake</code>
allows for multiple &lsquo;tabs&rsquo; (which can be running different shells),
and allows tabs to be re-ordered and renamed.</p>



<figure>
    
        
            <img src="https://gitlab.com/emacsomancer/equake/raw/master/image/equake-in-kdeplasma5.gif"/> </figure>

<p>My impetus for creating <code>equake</code> was to hijack my own workflow into
using John Wiegley&rsquo;s fantastic <code>eshell</code>, an Emacs module which
&lsquo;translates &ldquo;shell-like&rdquo; syntax into Lisp in order to exercise the
kernel in the same manner as typical system shells&rsquo;, allowing for a
similar working environment even in hostile, alien OSes like <code>w32</code>.</p>
<p><code>eshell</code> is somewhat sparsely documented, but some <a href="https://www.masteringemacs.org/article/complete-guide-mastering-eshell">useful</a> <a href="http://www.howardism.org/Technical/Emacs/eshell-fun.html">resources</a>
<a href="https://dm.reddit.com/r/emacs/comments/6y3q4k/yes_eshell_is_my_main_shell/">exist</a>, including a fairly extensive <a href="https://www.youtube.com/watch?v=RhYNu6i_uY4">video overview done by Howard
Abrams</a> which I highly recommend. It has a number of great features,
either inherently or via additional Emacs packages, including features
from <a href="https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs">Plan 9</a>&lsquo;s terminal, as well as <a href="https://github.com/dieggsy/esh-autosuggest">Fish shell-like auto-suggestions</a>.</p>
<p><code>equake</code> has been successful in my personal goal of using <code>eshell</code> for
99% of my terminal work, and I am looking forward to making further
using of a shell which can handle Lisp syntax as well.</p>
<p>Most of the <code>equake</code> code is keeping track of tabs, and frames for
multi-monitor set-ups. This is trickier than it would seem at first,
especially as each screen/monitor can have it own set of tabs (this
allows me to recreate at least a part of AwesomeWM&rsquo;s
screen-independence in other environments).  In theory <code>equake</code> should
work fairly well across platforms, as it makes use of the <code>frame.el</code>,
which includes code for MacOS and Windows (but I haven&rsquo;t test either
platform). Probably more work needs to be done to get <code>equake</code> to work
properly on tiling window managers like AwesomeWM or StumpWM. But it
seems to currently work fine in KDE Plasma and Gnome Shell (both X11
and Wayland).<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>I learned a good deal about how Emacs manages frames. I originally
used Emacs&rsquo; <code>make-frame-(in)visible</code> functions to hide/show the
<code>equake</code> frames. However, the implementation of these is very
odd. Applying <code>make-frame-invisible</code> to a frame once appears to render
it invisible, but Emacs still considers it to be visible, which means
that, for instance, <code>frame-visible-p</code> will still report the frame as
being visible and, worse, functions like <code>make-frame-visible</code> and
<code>raise-frame</code> will have no effect whatsoever upon the frame in
question, because Emacs treats it as &lsquo;visible&rsquo;.  Only a second
application of <code>make-frame-invisible</code> will register the frame as
reportably invisible to Emacs. This is easily enough worked around
simply by using a &lsquo;double tap&rsquo; of <code>make-frame-invisible</code> to the
relevant frame. However, I ran into numerous other issues in the use
of <code>make-frame-(in)visible</code>, including the fact that frames set at
less than 100% width end up re-appearing in a position other than
their original position, and frames sometimes spontaneously resize
when re-appearing or being un-fullscreened. I tried for a long time to
work around these issues, but found that even trying to force the
frames into doing what I wanted them to do via applications of
<code>set-frame-position</code> was a non-starter, as application of this
function to malpositioned frames resulted in significant lag – which
defeated the purpose of using <code>make-frame-(in)visible</code> in the first
place, which was to gain a slight performance improvement over
destroying and recreating frames. In the end, using <code>destroy-frame</code>
and <code>make-frame</code> to &lsquo;hide&rsquo; and &lsquo;show&rsquo; the drop-down console ended up
being the most performent solution.</p>
<p>Destroying and recreating frames means also worrying about remembering
the last buffer used in a frame as well as the window-history for the
frame, so these also make up a decent part of the <code>equake</code> code.</p>
<p>On single-screen set-ups, <code>equake</code>, once installed, is designed to
have a <code>equake</code> console frame toggled to drop down or be rolled up by
executing a command which invokes the <code>(equake-invoke)</code> function bound
to a key like <code>F12</code> via:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">emacsclient -n -e <span class="s1">&#39;(equake-invoke)&#39;</span>
</code></pre></div><p>Getting <code>equake</code> to work well on multi-monitor setups ended up being
rather challenging, since Emacs doesn&rsquo;t know exactly which screen is
&lsquo;active&rsquo; unless that screen also has an active Emacs frame. The
solution, which I managed to get to be nearly as fast as the simpler
non-multi-monitor solution, is to launch &lsquo;Emacs probes&rsquo; which are used
to determine which monitor is &lsquo;active&rsquo; and are then destroyed, invoked
via:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">emacsclient -n -c -e <span class="s1">&#39;(equake-invoke)&#39;</span> -F <span class="s1">&#39;((title . &#34;*transient*&#34;) (alpha . (0 . 0)) (width . (text-pixels . 0)) (height . (text-pixels . 0)) (left . 0) (top . 0))&#39;</span>
</code></pre></div><p>(The title is important as it&rsquo;s the key to being able to quickly
destroy these &lsquo;Emacs-probes&rsquo;, and the other frame-settings are there
to minimise the visibility of the probe-frame during its brief
existence.)</p>
<p>If you&rsquo;re looking for a drop-down for things other than
shells/terminals, alphapapa has a similar Emacs package designed as a
general-purpose drop-down (e.g. for Org mode buffers etc.); <a href="https://github.com/alphapapa/yequake"><code>yequake</code></a>;
and a <a href="https://github.com/alphapapa/yequake/#org-capture">specialised version for org-capture</a>.</p>
<p>I hope to add a few more features to <code>equake</code>, but at this point it
seems stable and is usable for what I wanted to use it for, being a
Lisp console for a Lisp shell. Comments and suggestions are, of
course, most welcome.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Where installation via
<a href="https://framagit.org/steckerhalter/quelpa-use-package"><code>quelpa-use-package</code></a> is described. This method is nearly as easy using
the plain <code>use-package</code> package to pull from Melpa. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Another part of the impetus for <code>equake</code> is wanting
to increase my machines&rsquo; &lsquo;Lisp quotient&rsquo; after moving a number of
machines from AwesomeWM to KDE Plasma, rather than to StumpWM as I had
originally planned. Of course, I could replace Kwin with StumpWM, and
I plan to experiment with this, but I rather like some the eye-candy
Kwin provides. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/equake" term="equake" label="equake" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/elisp" term="elisp" label="elisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/eshell" term="eshell" label="eshell" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Browsing the Web with Common Lisp]]></title>
            <link href="https://babbagefiles.xyz/next_browser-common_lisp/"/>
            <id>https://babbagefiles.xyz/next_browser-common_lisp/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2018-10-20T13:22:00-06:00</published>
            <updated>2022-06-20T11:28:31-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>I was a long-time user of <a href="http://conkeror.org/">Conkeror</a>, a highly-extensible browser with
an Emacs ethos. It <a href="https://www.freelists.org/archive/conkeror/09-2018">still exists</a>, but since the changes in the Firefox
back-end away from XULRunner, which Conkeror uses, running Conkeror
became increasingly difficult to use, so I&rsquo;ve largely switched to
just using plain Firefox.</p>
<p>However, <a href="http://john.mercouris.online/">John Mercouris</a> has been developing <a href="http://next.atlas.engineer/">Next Browser</a> (originally
styled <a href="https://dm.reddit.com/r/programming/comments/7fw57u/next_browser_a_next_generation_extensible_lisp/">nEXT Browser</a>), a browser with a Common Lisp front-end, allowing
for customisability and extensibility along Conkeror/Emacs lines:</p>



<figure>
    
        
            <img src="https://web.archive.org/web/20190226201246/https://raw.githubusercontent.com/atlas-engineer/next/master/assets/gifs/fast_navigation.gif"/> </figure>

<p>The back-ends are – if I understand correctly – planned to be Blink
for the QT port and WebkitGTK+ for the GTK port, with the Mac port of
Webkit for the Mac version. But the front-end, the user-facing side,
is Common Lisp.</p>
<p>John is currently running an <a href="https://www.indiegogo.com/projects/next-browser-nix-support#/">Indiegogo campaign to properly port</a> it to
Linux and other non-Mac Unix variants (it apparently runs well already
on the Mac, John&rsquo;s main platform it seems [there&rsquo;s no accounting for
taste ;) ]). The raised money would be used in part to pay a
professional C/C++ developer for their time.</p>
<p><a href="https://ambrevar.xyz/">Ambrevar</a> is <a href="https://github.com/atlas-engineer/next/issues/92">currently working on packaging</a> Next Browser for <a href="https://www.gnu.org/software/guix/">Guix</a>,
which is exciting and promises to add to the amount of Lisp front-end
software we&rsquo;ll be able to use. Currently I&rsquo;m running Emacs (elisp) for
the majority of my non-browser productivity (writing papers &amp; creating
class slides using <a href="https://www.gnu.org/software/auctex/">AUCTeX</a>; reading composing email with <a href="https://www.djcbsoftware.nl/code/mu/mu4e.html">mu4e</a>;
note-taking and scheduling with <a href="https://orgmode.org/">Org mode</a>; &amp;c. &amp;c.) and, at least on
one machine, <a href="https://stumpwm.github.io/">StumpWM</a> (Common Lisp window manager) for my &lsquo;desktop
environment&rsquo;; and <a href="https://www.gnu.org/software/guix/">GNU GuixSD</a> with a Guile-based package manager,
Guile-based cron (<a href="https://www.gnu.org/software/mcron/manual/html_node/index.html#Top">mcron</a>), and Guile-based init/daemon-manager
(<a href="https://www.gnu.org/software/shepherd/">Shepherd</a>). A functional, configurable, Lisp-based browser would be a
most welcome addition. As excellent as Firefox is, especially its
backend, I do really miss the halcyon days of Conkeror, and Next
Browser could represent a return to those heady days of configurable
browsing Emacs-style.</p>
<p>So, if this sort of thing appeals to you (i.e. if you like Lisp,
Emacs, and/or highly-extendable browsers), you might want to support
the Linux/Unix-port of Next Browser:
<a href="https://www.indiegogo.com/projects/next-browser-nix-support">https://www.indiegogo.com/projects/next-browser-nix-support</a></p>
<p>There&rsquo;s only about a week left in the campaign.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/commonlisp" term="commonlisp" label="commonlisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/nextbrowser" term="nextbrowser" label="nextbrowser" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/browsers" term="browsers" label="browsers" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/web" term="web" label="web" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Confusion: PDP-10 Zork]]></title>
            <link href="https://babbagefiles.xyz/zork-confusion/"/>
            <id>https://babbagefiles.xyz/zork-confusion/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2018-09-23T23:52:00-05:00</published>
            <updated>2025-12-21T22:01:03-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>I grew up playing <a href="https://en.wikipedia.org/wiki/Infocom">Infocom</a>, <a href="https://en.wikipedia.org/wiki/Magnetic_Scrolls">Magnetic Scrolls</a>, and <a href="https://en.wikipedia.org/wiki/Level_9_Computing">Level 9</a> text
adventures, with the <a href="https://en.wikipedia.org/wiki/Zork">Zork trilogy</a>, the <a href="https://en.wikipedia.org/wiki/Enchanter_(video_game)">Enchanter trilogy</a>, <a href="https://en.wikipedia.org/wiki/Planetfall">Planetfall</a>,
<a href="https://en.wikipedia.org/wiki/Wishbringer">Wishbringer</a>, <a href="https://en.wikipedia.org/wiki/The_Guild_of_Thieves">The Guild of Thieves</a>, <a href="https://en.wikipedia.org/wiki/The_Pawn">The Pawn</a>, <a href="https://en.wikipedia.org/wiki/Knight_Orc">Knight Orc</a>, and <a href="https://en.wikipedia.org/wiki/Silicon_Dreams">Silicon
Dreams</a> being particularly prominent in my memory (somewhat
re-activated through recent listening to the <a href="http://monsterfeet.com/grue/">Eaten by a Grue
podcast</a>). I would have played all of these on an Atari 8bit or ST
computer, and didn&rsquo;t have any access to anything like a mainframe, and
so never actually played the <a href="https://www.filfre.net/2012/01/zork-on-the-pdp-10/"><strong>original</strong> Zork</a>, which was written in the
<a href="https://en.wikipedia.org/wiki/MDL_(programming_language)">Lisp-derived MDL language</a> (which formed the basis for the MDL-subset
Infocom-specific <a href="https://en.wikipedia.org/wiki/Z-machine">ZIL language</a> used for their subsequent offerings) for
the <a href="https://en.wikipedia.org/wiki/PDP-10">DEC PDP-10</a>.</p>
<p>Fortunately, some years ago, Matthew Russotto created <a href="http://www.russotto.net/git/mrussotto/confusion/src/master/src/README">Confusion</a>, &ldquo;a
MDL interpreter which works just well enough to play the original Zork
all the way through&rdquo; and runs on Linux/Unix and presumably
macOS. I&rsquo;d tried to install this from Arch&rsquo;s AUR a couple of years
ago, without any luck. Inspecting the <a href="https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mdli">package build</a>, the AUR Confusion
tries to get around errors by using an ancient version of gcc. I found
that a few fairly trivial fixes were enough to get it to compile with
a recent version of gcc, and so was finally able to try the original
Zork:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/mdl-zork-white-house.jpg"/> </figure>

<p>The most fun I always had in text adventures was exploring, and I&rsquo;ve
always found Infocom&rsquo;s various &ldquo;time-limit&rdquo; elements (batteries
running out, health deteriorating, &amp;c.) somewhat irritating. I
remembered that there&rsquo;s a torch in Zork I, and that is also true in
the original Zork, and I managed to obtain it early on so as to save
my (trusty brass) lamp&rsquo;s batteries. I then cheated somewhat wildly
(though, to be fair, a lot of the puzzles I had figured out once upon
a time in Zork I or Zork II; and the bank puzzle and some of the
others I wouldn&rsquo;t have patience for anymore).</p>
<p>The original code has some bugs in it which made things more
interesting.</p>
<pre><code class="language-ascii" data-lang="ascii">Narrow Ledge
You are on a narrow ledge overlooking the inside of an old dormant
volcano.  This ledge appears to be about in the middle between the
floor below and the rim above. There is an exit here to the south.
There is a very large and extremely heavy wicker basket with a cloth
bag here. Inside the basket is a metal receptacle of some kind.
Attached to the basket on the outside is a piece of wire.
The basket contains:
 A cloth bag
 A braided wire
 A receptacle
 The receptacle contains:
  A newspaper
 A blue label
There is a small hook attached to the rock here.

&gt; untie braided wire from hook

 *ERROR*
 &quot;FIRST-ARG-WRONG-TYPE&quot;
 &quot;First arg to NTH must be structured&quot;
LISTENING-AT-LEVEL 2 PROCESS 1
Atom REP has neither LVAL nor GVAL
</code></pre><p>I had a similar issue involving the result of a disagreement with a
suspicious-looking individual holding a bag. It turns out (thanks to
Matthew Russotto for the following information) that this has to do
with issues in saving and restoring files, and (failure of) either
properly recording or decoding certain values. The balloon issue has
to do with a record about the object burning in the receptacle. It is
saved as <code>BINF!-FLAG</code>, which should be a boolean-type flag, but at
some point it became an object (recording what is burning) and
apparently isn&rsquo;t decoded properly on a restore. Saving-and-restoring
during a battle with the suspicious-looking individual produces a
similar error to the balloon-burnable error, due to the
<code>THIEF-ENGROSSED!-FLAG</code> (which apparently really is a flag) not
being saved properly. The upshot (for a player) is that you shouldn&rsquo;t
save during either of these bits of the game.</p>
<p>With these additional lurking grues out of the way, I was able to make
it to the end of the game:</p>



<figure>
    
        <img src="https://babbagefiles.xyz/ox-hugo/zork-end.png"/> </figure>

<p>Speaking of saving, MDL Zork only has a single restore file. It is
stored in the <code>MTRZORK</code> directory and is named <code>ZORK.SAVE</code>. Of course
you can create additional saves by copying out this save file to
different names.</p>
<p>If you too want to play the original Zork using the original PDP-10 MDL
source code, you should be able to build the MDL interpreter necessary
from the release at Matthew Russotto&rsquo;s site (where my minor patches
should be incorporated):
<a href="http://www.russotto.net/git/mrussotto/confusion/releases">http://www.russotto.net/git/mrussotto/confusion/releases</a> or
from the git repo with my patches at
<a href="https://github.com/emacsomancer/confusion-mdl">https://github.com/emacsomancer/confusion-mdl</a>.</p>
<p>Or, the easiest of all, install it via Guix (<a href="https://www.gnu.org/software/guix/manual/en/html_node/Binary-Installation.html#Binary-Installation">which can installed on
top of your current distro as a standalone package manager</a>), as I have
created a Guix package which was accepted upstream (the package name
in Guix is <code>confusion-mdl</code>). [note: this Guix package stopped building
some time ago and I need to figure out what needs fixing. Right now,
have a look at the mdlzork project at
<a href="https://github.com/jordanhubbard/mdlzork">https://github.com/jordanhubbard/mdlzork</a> , which provides a way to
easily play early versions of Zork online at
<a href="https://jordanhubbard.github.io/mdlzork/">https://jordanhubbard.github.io/mdlzork/</a>.]</p>
<p>Since I played through this right before designing the propositional
logic homework for my Semantics class, my students are currently
&ldquo;enjoying&rdquo; doing Zork-flavoured propositional logic translations,
e.g.:</p>
<ol>
<li>The thief only steals your things when you&rsquo;re in the cellar.</li>
<li>Your sword glows blue whenever the thief or the troll is near and
never any other time.</li>
<li>Whether or not you are lost in a maze of twisty little passages
(all alike), the grues are lurking in the darkness.</li>
<li>The grues will eat you unless you bring a light source.</li>
</ol>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/mdl" term="mdl" label="mdl" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/intfict" term="intfict" label="intfict" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/guix" term="guix" label="guix" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/zork" term="zork" label="zork" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Quake-style drop-down terminal in StumpWM]]></title>
            <link href="https://babbagefiles.xyz/quake_terminal_stumpwm/"/>
            <id>https://babbagefiles.xyz/quake_terminal_stumpwm/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2018-09-18T20:45:00-06:00</published>
            <updated>2018-09-18T21:02:50-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>One thing I&rsquo;ve missed in StumpWM is a Quake-style drop-down terminal,
like what Guake provides (and I have a Lua-one in my AwesomeWM
config). It may be that I&rsquo;m still haven&rsquo;t fully absorbed the
StumpWM-mindset and that I should be doing this a different way. But
up until now when I&rsquo;m using StumpWM I&rsquo;ve tended to end up with a heap
of terminal windows that are a pain to navigate through (I have a
<code>run-or-raise</code> command associated with xterm, but it starts from the
first xterm window and usually I want the last – something else to
figure out how to do). It&rsquo;s nice to have a &lsquo;working terminal&rsquo; that one
can quickly summon and dismiss.</p>
<p>There&rsquo;s a number of issues for creating a Quake-type drop-down in
StumpWM (at least based on my current knowledge). The first is fairly
easily dealt with - I don&rsquo;t want more than one Quake terminal around.</p>
<p><code>run-or-raise</code> will prevent multiple instances from being created:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">defcommand</span> <span class="nv">urxvt</span> <span class="p">()</span> <span class="p">()</span>
  <span class="s">&#34;Start an urxvt instance or switch to it, if it is already running.&#34;</span>
  <span class="p">(</span><span class="nv">run-or-raise</span> <span class="s">&#34;urxvt&#34;</span><span class="o">&#39;</span><span class="p">(</span><span class="ss">:title</span> <span class="s">&#34;urxvt&#34;</span><span class="p">)))</span>
</code></pre></div><p>(I&rsquo;m cheating here a bit because I&rsquo;m not quite sure how to handle
title-management. I know windows can be retitled, and I initially
tried setting the window title in the above function to &ldquo;quake&rdquo;, but
somehow I ended up also sporadically re-titling <strong>other</strong> windows as
&ldquo;quake&rdquo;, so I&rsquo;ve side-stepped the whole issue by just using urxvt for
my drop-down terminal, with my regular terminal being xterm.)</p>
<p>What we want then is a function that toggles our Quake terminal
on-and-off: essentially if the current window is not urxvt, then call
the <code>urxvt</code> function defined above, and if the current window is urxvt
then switch back to the last used window before urxvt was called.  The
basic thing we need for this is <code>(if (equal (window-title (current-window)) &quot;urxvt&quot;)</code>.</p>
<p>The trouble is that StumpWM has a nice feature which is similar to
doing <code>C-x b RET</code> in Emacs: it essentially switches to the last window
used, so it can be a nice feature for switching back and forth between
two different windows you&rsquo;re working in. The function is
<code>pull-hidden-other</code> and it is invoked with &ldquo;prefix prefix&rdquo;, which for
me is <code>s-f s-f</code> (Super+F, twice), but by default is <code>C-t C-</code> (Ctrl+t
twice). Why this creates a problem is because our Quake terminal will
muck that up because <strong>it</strong> will often end up being the last used
window, and that&rsquo;s not what I want. I want <code>pull-hidden-other</code> to
switch to the last used window that&rsquo;s <strong>not</strong> the Quake drop-down.</p>
<p>I tried a number of different things, and this may still not be the
best solution, but it works:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">defcommand</span> <span class="nv">rxvt-quake</span> <span class="p">()</span> <span class="p">()</span>
  <span class="s">&#34;Toggle rxvt-quake window.&#34;</span>
  <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nv">window-title</span> <span class="p">(</span><span class="nv">current-window</span><span class="p">))</span> <span class="s">&#34;urxvt&#34;</span><span class="p">)</span>   <span class="c1">; if the current window is quake</span>
      <span class="p">(</span><span class="k">progn</span>
	<span class="p">(</span><span class="nv">other-window</span><span class="p">)</span>    <span class="c1">; switch back to window quake was called from</span>
	<span class="p">(</span><span class="nv">select-window-by-number</span> <span class="vg">*real-other-window*</span><span class="p">)</span>  <span class="c1">; switch to the &#39;real&#39; &#34;other-window&#34;</span>
	<span class="p">(</span><span class="nv">other-window</span><span class="p">))</span>  <span class="c1">; switch back to the original window - this way after quake finishes, the original configuration is restored</span>
      <span class="p">(</span><span class="k">progn</span>          <span class="c1">; otherwise, if the current window is NOT quake</span>
      <span class="p">(</span><span class="nv">other-window</span><span class="p">)</span>   <span class="c1">; first switch the current &#34;other-window&#34;</span>
	<span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">not</span> <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nv">window-title</span> <span class="p">(</span><span class="nv">current-window</span><span class="p">))</span> <span class="s">&#34;urxvt&#34;</span><span class="p">))</span> <span class="c1">; if the current</span>
								  <span class="c1">; &#34;other-window&#34; is</span>
								  <span class="c1">; quake itself, do</span>
								  <span class="c1">; nothing</span>
	    <span class="p">(</span><span class="nb">setf</span> <span class="vg">*real-other-window*</span> <span class="p">(</span><span class="nv">window-number</span> <span class="p">(</span><span class="nv">current-window</span><span class="p">))))</span> <span class="c1">; otherwise store the window-number of the current other-window</span>
	<span class="p">(</span><span class="nv">other-window</span><span class="p">)</span> <span class="c1">; switch back to the window originally called from</span>
	<span class="p">(</span><span class="nv">urxvt</span><span class="p">))))</span> <span class="c1">; run-or-raise urxvt</span>
</code></pre></div><p>So this function indeed checks to see whether the current window is
our Quake drop-down terminal. If it&rsquo;s not, it first switches to the
currently last used window with <code>(other-window)</code> and stores the number
of this window in <code>*real-other-window*</code> (but <strong>only</strong> if this
last-used-window isn&rsquo;t itself urxvt - otherwise we&rsquo;ll sometime end up
&lsquo;over-writing&rsquo; the actual last-used-window we want to keep). It then
switches back to window that was active when <code>rxvt-quake</code> was invoked,
and then it calls the <code>urxvt</code> function which either launches a new
urxvt (if none currently exists), or raises the currently running
urxvt window. Storing the value in <code>*real-other-window*</code> gives us a
way to remember what the &lsquo;real&rsquo; last-used window should be.</p>
<p>If <code>rxvt-quake</code> is invoked while the Quake urxvt drop-down is the
currently active window, then first <code>(other-window)</code> is invoked,
switching us back to the window that was active before we called the
drop-down terminal, then we switch to our stored window (which was the
&lsquo;real&rsquo; last-used window before Quake was invoked), and then we switch
back with <code>(other-window)</code> to the window that was active when Quake
was first invoked.</p>
<p>This way the window configuration that existed before we summoned our
Quake drop-down terminal is restored, and <code>pull-hidden-other</code>
effectively ignores the Quake drop-drop.</p>
<p>Bind this command to the traditional F12 with:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">define-key</span> <span class="vg">*top-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;F12&#34;</span><span class="p">)</span> <span class="s">&#34;rxvt-quake&#34;</span><span class="p">)</span>
</code></pre></div><p>And you&rsquo;re got a &lsquo;traditional&rsquo; Quake drop-down terminal in StumpWM.</p>
<p>It is a &lsquo;full-length&rsquo; drop-down terminal, however. It might be nice to
have it be a fraction of the screen like a more traditional Quake drop-down.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/stumpwm" term="stumpwm" label="stumpwm" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Dockerised Firefox on GuixSD]]></title>
            <link href="https://babbagefiles.xyz/dockerised_firefox_guix/"/>
            <id>https://babbagefiles.xyz/dockerised_firefox_guix/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2018-09-15T20:18:00-06:00</published>
            <updated>2018-09-15T20:29:22-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>So GuixSD doesn&rsquo;t currently package Firefox (<a href="https://lists.gnu.org/archive/html/guix-devel/2018-05/msg00021.html">though hopefully that
is changing</a>), but only IceCat (which is now EOL). On freenode#guix,
pkill9 suggested that Firefox (and Chromium etc.) could be installed
on Guix via the <a href="https://nixos.org/nix/download.html">Nix</a> installer (install as per instructions on their
site and then <code>nix-env -i firefox</code>) with the following trick, create a
file <code>~/.local/bin/firefox</code> with the following content:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="c1"># Wrapper to run the Firefox built and packaged by Nix</span>
<span class="nv">MESA_LIB</span><span class="o">=</span><span class="k">$(</span>dirname <span class="k">$(</span>realpath /run/current-system/profile/lib/libGL.so<span class="k">))</span> <span class="c1">#To get webgl working</span>
<span class="nb">export</span> <span class="nv">LD_LIBRARY_PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$MESA_LIB</span><span class="si">${</span><span class="nv">LD_LIBRARY_PATH</span><span class="p">:+:</span><span class="si">}</span><span class="nv">$LD_LIBRARY_PATH</span><span class="s2">&#34;</span>
<span class="c1">#export FONTCONFIG_PATH=&#34;$(guix build fontconfig)/etc/fonts${FONTCONFIG_PATH:+:}$FONTCONFIG_PATH&#34;</span>
<span class="nb">export</span> <span class="nv">FONTCONFIG_PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>guix build fontconfig<span class="k">)</span><span class="s2">/etc/fonts&#34;</span>
<span class="nb">exec</span> -a <span class="s2">&#34;</span><span class="nv">$0</span><span class="s2">&#34;</span> <span class="s2">&#34;/nix/var/nix/profiles/per-user/</span><span class="nv">$USER</span><span class="s2">/profile/bin/firefox&#34;</span> <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span>
</code></pre></div><p>And then add <code>~/.local/bin</code> to your <code>$PATH</code> in <code>~/.profile</code>.</p>
<p>Unfortunately I haven&rsquo;t been able to get that to work, though I could
be doing something daft.</p>
<p>I figured out the following (elaborate) alternative workaround for
Firefox (no luck for Chromium):</p>
<ul>
<li>
<ol>
<li>Install Docker via Nix. Launch with <code>sudo dockerd</code>.</li>
</ol>
</li>
<li>
<ol start="2">
<li>Create a Dockerfile for Firefox</li>
</ol>
</li>
</ul>
<div class="highlight"><pre class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="c"># firefox in a docker container</span><span class="err">
</span><span class="err"></span><span class="c"># the following line will start firefox in the container, thus</span><span class="err">
</span><span class="err"></span><span class="c"># video and sound will be played on the host machine</span><span class="err">
</span><span class="err"></span><span class="k">FROM</span><span class="s"> alpine:edge</span><span class="err">
</span><span class="err"></span><span class="k">MAINTAINER</span><span class="s"> slade@jnanam.net</span><span class="err">
</span><span class="err">
</span><span class="err"></span><span class="c"># add testing repo + install packages + add user and group</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> <span class="nb">echo</span> <span class="s2">&#34;http://dl-cdn.alpinelinux.org/alpine/edge/testing/&#34;</span> &gt;&gt; /etc/apk/repositories <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> apk add --no-cache <span class="se">\
</span><span class="se"></span>       xz <span class="se">\
</span><span class="se"></span>       dbus-x11 <span class="se">\
</span><span class="se"></span>       ttf-dejavu <span class="se">\ </span>         <span class="c1"># a bunch of fonts; you may not want all of these</span><span class="err">
</span><span class="err"></span>       ttf-freefont <span class="se">\
</span><span class="se"></span>       ttf-ubuntu-font-family <span class="se">\
</span><span class="se"></span>       font-noto <span class="se">\
</span><span class="se"></span>       font-noto-extra <span class="se">\
</span><span class="se"></span>       font-noto-emoji <span class="se">\
</span><span class="se"></span>       font-noto-oriya <span class="se">\
</span><span class="se"></span>       font-noto-tamil <span class="se">\
</span><span class="se"></span>       font-noto-avestan <span class="se">\
</span><span class="se"></span>       font-noto-gothic <span class="se">\
</span><span class="se"></span>       font-noto-myanmar <span class="se">\
</span><span class="se"></span>       font-noto-telugu <span class="se">\
</span><span class="se"></span>       font-noto-deseret <span class="se">\
</span><span class="se"></span>       font-noto-bengali <span class="se">\
</span><span class="se"></span>       font-noto-kannada <span class="se">\
</span><span class="se"></span>       font-noto-ethiopic <span class="se">\
</span><span class="se"></span>       font-noto-armenian <span class="se">\
</span><span class="se"></span>       font-noto-tibetan <span class="se">\
</span><span class="se"></span>       font-noto-sinhala <span class="se">\
</span><span class="se"></span>       font-noto-gurmukhi <span class="se">\
</span><span class="se"></span>       font-noto-malayalam <span class="se">\
</span><span class="se"></span>       font-noto-gujarati <span class="se">\
</span><span class="se"></span>       font-noto-devanagari <span class="se">\
</span><span class="se"></span>       font-noto-thai <span class="se">\
</span><span class="se"></span>       font-noto-adlam <span class="se">\
</span><span class="se"></span>       font-noto-nko <span class="se">\
</span><span class="se"></span>       font-noto-lisu <span class="se">\
</span><span class="se"></span>       font-noto-carian <span class="se">\
</span><span class="se"></span>       font-noto-buhid <span class="se">\
</span><span class="se"></span>       font-noto-osage <span class="se">\
</span><span class="se"></span>       font-noto-hebrew <span class="se">\
</span><span class="se"></span>       font-noto-arabic <span class="se">\
</span><span class="se"></span>       font-noto-chakma <span class="se">\
</span><span class="se"></span>       font-noto-gothic <span class="se">\
</span><span class="se"></span>       font-noto-khmer <span class="se">\
</span><span class="se"></span>       font-noto-cypriot <span class="se">\
</span><span class="se"></span>       font-noto-kayahli <span class="se">\
</span><span class="se"></span>       font-noto-mandaic <span class="se">\
</span><span class="se"></span>       font-noto-olchiki <span class="se">\
</span><span class="se"></span>       font-noto-thaana <span class="se">\
</span><span class="se"></span>       font-noto-georgian <span class="se">\
</span><span class="se"></span>       font-noto-shavian <span class="se">\
</span><span class="se"></span>       font-noto-cherokee <span class="se">\
</span><span class="se"></span>       font-noto-oldturkic <span class="se">\
</span><span class="se"></span>       font-noto-osmanya <span class="se">\
</span><span class="se"></span>       font-noto-glagolitic <span class="se">\
</span><span class="se"></span>       font-noto-tifinagh <span class="se">\
</span><span class="se"></span>       font-noto-adlamunjoined <span class="se">\
</span><span class="se"></span>       font-noto-nko <span class="se">\
</span><span class="se"></span>       font-noto-lao <span class="se">\
</span><span class="se"></span>       arc-theme <span class="se">\
</span><span class="se"></span>       hunspell <span class="se">\
</span><span class="se"></span>       hunspell-en <span class="se">\
</span><span class="se"></span>       firefox <span class="se">\
</span><span class="se"></span>       libcanberra-gtk2 <span class="se">\
</span><span class="se"></span>       pulseaudio <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> rm -fr /var/cache/apk/* <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> adduser -D -u <span class="m">1000</span> -g <span class="m">1000</span> user<span class="err">
</span><span class="err">
</span><span class="err"></span><span class="c"># add user&#39;s work</span><span class="err">
</span><span class="err"></span><span class="k">WORKDIR</span><span class="s"> /home/user</span><span class="err">
</span><span class="err">
</span><span class="err"></span><span class="c"># switch to user</span><span class="err">
</span><span class="err"></span><span class="k">USER</span><span class="s"> user</span><span class="err">
</span><span class="err">
</span><span class="err"></span><span class="k">ENTRYPOINT</span> <span class="p">[</span><span class="s2">&#34;/usr/bin/firefox&#34;</span><span class="p">,</span> <span class="s2">&#34;--no-remote&#34;</span><span class="p">]</span><span class="err">
</span></code></pre></div><p>Save the above out to a file <code>Dockerfile</code> in some directory. <code>cd</code> to
that directory. Then create a Docker container with <code>docker build -t someprefix/firefox .</code> (with <code>someprefix</code> being whatever you like).</p>
<ul>
<li>
<ol start="3">
<li>Create in <code>~/.local/bin/firefox</code>:</li>
</ol>
</li>
</ul>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="cp">#!/bin/sh
</span><span class="cp"></span><span class="c1"># Wrapper to run the Chromium built and packaged by Nix</span>
xhost +local:docker@<span class="p">;</span> <span class="se">\
</span><span class="se"></span>docker run --rm -it -e <span class="nv">DISPLAY</span><span class="o">=</span><span class="nv">$DISPLAY</span> -v /tmp/.X11-unix/:/tmp/.X11-unix <span class="se">\
</span><span class="se"></span>-v /dev/snd:/dev/snd <span class="se">\
</span><span class="se"></span>-v /run/user/<span class="nv">$USER_UID</span>/pulse:/run/pulse:ro <span class="se">\
</span><span class="se"></span>-v /home/<span class="nv">$USER</span>/.mozilla:/home/user/.mozilla <span class="se">\
</span><span class="se"></span>-v /home/<span class="nv">$USER</span>/.cache/mozilla:/home/user/.cache/mozilla <span class="se">\
</span><span class="se"></span>-v /tmp/Downloads:/tmp/Downloads <span class="se">\
</span><span class="se"></span>-v /home/<span class="nv">$USER</span>/.gtkrc-2.0:/home/user/.gtkrc-2.0 <span class="se">\
</span><span class="se"></span>-v /home/<span class="nv">$USER</span>/.config/gtk-3.0/:/home/user/.config/gtk-3.0 <span class="se">\
</span><span class="se"></span>-v /home/<span class="nv">$USER</span>/.nix-profile/share/fonts/truetype:/usr/share/fonts/truetype-nix <span class="se">\ </span><span class="c1"># for nix fonts, if you have them here</span>
-v /home/<span class="nv">$USER</span>/.nix-profile/share/fonts/ubuntu:/usr/share/fonts/ubuntu-nix <span class="se">\ </span>    <span class="c1"># ditto</span>
-v /home/<span class="nv">$USER</span>/.nix-profile/share/fonts/noto:/usr/share/fonts/noto-nix <span class="se">\ </span>        <span class="c1"># ditto</span>
-v /home/<span class="nv">$USER</span>/.guix-profile/share/fonts/truetype:/user/share/fonts/truetype-guix <span class="se">\ </span><span class="c1"># for guix fonts, if you have them here</span>
-v /home/<span class="nv">$USER</span>/.guix-profile/share/fonts/opentype:/usr/share/fonts/opentype-guix <span class="se">\ </span><span class="c1"># ditto</span>
 --shm-size 2g  --privileged someprefix/firefox  <span class="c1"># substitute your prefix here for &#34;someprefix&#34;</span>
</code></pre></div><p>Where again <code>someprefix</code> is whatever you chose before. <code>chmod +x ~/.local/bin/firefox</code> and then you can run Firefox with
<code>firefox</code>. (You&rsquo;ll have to make sure <code>dockerd</code> is running.)</p>
<p>Perhaps this could be useful elsewhere. It has the advantage of being
a nice <a href="https://alpinelinux.org/">Alpine Linux</a> <a href="https://www.musl-libc.org/">musl-libc</a>/<a href="https://www.libressl.org/">libressl</a> build of Firefox running inside
your GuixSD (or whatever you&rsquo;re using).</p>
<p>What doesn&rsquo;t work:</p>
<ul>
<li>Spellchecking - I don&rsquo;t quite know why, hunspell should be
available. Just disable spellchecking for now (otherwise everything
will be underlined in red).</li>
<li><a href="https://wire.com">Wire</a>&lsquo;s web portal.</li>
<li>Saving or uploading from anywhere but <code>/tmp/Downloads</code> (but this is
a feature really).</li>
<li>If you want CJK fonts, you&rsquo;ll have to install them in Nix or Guix
(Alpine doesn&rsquo;t seem to have any); this is indicated in the Firefox
launch script above.</li>
</ul>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/guix" term="guix" label="guix" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/docker" term="docker" label="docker" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/firefox" term="firefox" label="firefox" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Grab word etymologies in Emacs]]></title>
            <link href="https://babbagefiles.xyz/emacs_etymologies/"/>
            <id>https://babbagefiles.xyz/emacs_etymologies/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2018-08-26T21:49:00-06:00</published>
            <updated>2018-08-26T21:50:27-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>On Xah Lee&rsquo;s <a href="http://ergoemacs.org/emacs/blog.html">blog</a> I noticed an entry on linking to word etymologies
from Emacs (2018-08-16: &ldquo;emacs, create link of word etymology&rdquo;). What
his function does is create a html link to the <a href="http://www.etymonline.com">Etymonline.com</a> page on
the currently selected word.</p>
<p>But I thought: it would be great to have a quick way of pulling up
etymological information within Emacs itself. So, with a little help
from <a href="http://links.twibright.com">Links</a> (i.e. you&rsquo;ll need to have Links installed), here is a first
attempt at such a function:</p>
<div class="highlight"><pre class="chroma"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">slade/etymonbuf</span> <span class="p">()</span>
  <span class="s">&#34;Look up word etymology notes from http://www.etymonline.com. Requires [[http://links.twibright.com/][links] to be installed.&#34;</span>
  <span class="p">(</span><span class="nb">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">(</span><span class="nv">$p1</span> <span class="nv">$p2</span> <span class="nv">$input</span> <span class="nv">$result</span><span class="p">)</span>    <span class="c1">;; lifted from Xah Lee&#39;s 2018-08-16: &#34;emacs, create link of word etymology&#34;</span>
    <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nv">use-region-p</span><span class="p">)</span>
	<span class="p">(</span><span class="nb">progn</span> <span class="p">(</span><span class="nb">setq</span> <span class="nv">$p1</span> <span class="p">(</span><span class="nf">region-beginning</span><span class="p">))</span>
	       <span class="p">(</span><span class="nb">setq</span> <span class="nv">$p2</span> <span class="p">(</span><span class="nf">region-end</span><span class="p">)))</span>
      <span class="p">(</span><span class="nb">progn</span> <span class="p">(</span><span class="nb">setq</span> <span class="nv">$p1</span> <span class="p">(</span><span class="nf">line-beginning-position</span><span class="p">))</span>
	     <span class="p">(</span><span class="nb">setq</span> <span class="nv">$p2</span> <span class="p">(</span><span class="nf">line-end-position</span><span class="p">))))</span>
    <span class="p">(</span><span class="nb">setq</span> <span class="nv">$input</span> <span class="p">(</span><span class="nf">buffer-substring-no-properties</span> <span class="nv">$p1</span> <span class="nv">$p2</span><span class="p">))</span> <span class="c1">;;</span>
    <span class="p">(</span><span class="nv">generate-new-buffer</span> <span class="nv">$input</span><span class="p">)</span>  <span class="c1">; generate new buffer titled with entry word</span>
    <span class="p">(</span><span class="nv">switch-to-buffer</span> <span class="p">(</span><span class="nv">last-buffer</span><span class="p">))</span> <span class="c1">; switch to that new buffer</span>
    <span class="p">(</span><span class="nf">insert</span> <span class="p">(</span><span class="nv">shell-command-to-string</span> <span class="p">(</span><span class="nf">concat</span> <span class="s">&#34;links -dump http://www.etymonline.com/word/&#34;</span> <span class="nv">$input</span><span class="p">)))</span> <span class="c1">; dump text via links from etymonline</span>
    <span class="p">(</span><span class="nf">goto-char</span> <span class="p">(</span><span class="nf">point-min</span><span class="p">))</span> <span class="c1">; go to beginning</span>
    <span class="p">(</span><span class="nb">if</span> <span class="p">(</span><span class="nf">re-search-forward</span> <span class="s">&#34;Error 404 (Not Found)&#34;</span> <span class="no">nil</span> <span class="no">t</span><span class="p">)</span>   <span class="c1">; for non-word or words with no etymonline entry</span>
	<span class="p">(</span><span class="nv">slade/word-not-found</span><span class="p">)</span>   <span class="c1">; if 404, kill buffer and display minibuffer message</span>
      <span class="p">(</span><span class="nv">slade/trim-etymonbuf</span><span class="p">))))</span>  <span class="c1">; otherwise trim off unneeded text from header and footer</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">slade/trim-etymonbuf</span> <span class="p">()</span>
  <span class="s">&#34;Trim off unneeded text from top and bottom.&#34;</span>
  <span class="p">(</span><span class="nf">goto-char</span> <span class="p">(</span><span class="nf">point-min</span><span class="p">))</span>		<span class="c1">; goto beginning of buffer</span>
  <span class="p">(</span><span class="nf">search-forward</span> <span class="s">&#34;[\s\s]&#34;</span><span class="p">)</span> 		<span class="c1">; the text output of links always delivers text with a &#34;[  ]&#34; right before the text we want</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">begdel</span> <span class="p">(</span><span class="nf">point</span><span class="p">)))</span>
    <span class="p">(</span><span class="nf">delete-region</span> <span class="mi">1</span> <span class="nv">begdel</span><span class="p">))</span>		<span class="c1">; delete from beginning of buffer until &#34;[  ]&#34;</span>
  <span class="p">(</span><span class="nf">goto-char</span> <span class="p">(</span><span class="nf">point-max</span><span class="p">))</span>  		<span class="c1">; goto end of buffer</span>
  <span class="p">(</span><span class="nv">search-backward-regexp</span> <span class="s">&#34;Share[[:space:]]*Share on FacebookShare&#34;</span><span class="p">)</span>   <span class="c1">; the unneeded text at the bottom starts with a &#34;Share&#34; section</span>
  <span class="p">(</span><span class="nb">let</span> <span class="p">((</span><span class="nv">enddel</span> <span class="p">(</span><span class="nf">point</span><span class="p">)))</span>
    <span class="p">(</span><span class="nf">delete-region</span> <span class="p">(</span><span class="nf">point-max</span><span class="p">)</span> <span class="nv">enddel</span><span class="p">))</span>	<span class="c1">; delete from end of buffer until Share section</span>
  <span class="p">(</span><span class="nf">goto-char</span> <span class="p">(</span><span class="nf">point-min</span><span class="p">)))</span>		<span class="c1">; move point back to beginning</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">slade/word-not-found</span> <span class="p">()</span>
  <span class="s">&#34;Delete buffer and display minibuffer message.&#34;</span>
  <span class="p">(</span><span class="nf">kill-buffer</span><span class="p">)</span>
  <span class="p">(</span><span class="nf">message</span> <span class="s">&#34;Sorry, word not found.&#34;</span><span class="p">))</span>
</code></pre></div><p>After <code>eval</code>'ing the above, one day, if you have an idle fancy to know
the etymology of a word, you can select it and call <code>slade/etymonbuf</code>
and have a quick peek at what Etymonline has to say.  Here&rsquo;s what you
get if you try this on &ldquo;lisp&rdquo;:</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">                                lisp (n.)

&#34;act or habit of lisping,&#34; 1620s, from lisp (v.).

lisp (v.)

sometimes lipse, late 14c. alteration of wlisp, from late Old English
awlyspian &#34;to lisp, to pronounce &#39;s&#39; and &#39;z&#39; imperfectly,&#34; from wlisp
(adj.) &#34;lisping,&#34; which is probably imitative (compare Middle Dutch, Old
High German lispen, Danish læspe, Swedish läspa). General sense &#34;speak
imperfectly or childishly&#34; is from 17c. Transitive sense from 1610s.
Related: Lisped; lisping. Suggestive of effeminacy from 14c.
</code></pre></div><p>Probably not the lisp you were looking for, but interesting none the
less.</p>
<p>Of course it would be nice to retain italics and not to have to depend
on an external application, so there&rsquo;s more that can be done.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/elisp" term="elisp" label="elisp" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/etymology" term="etymology" label="etymology" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Managing emacsclient windows in StumpWM]]></title>
            <link href="https://babbagefiles.xyz/emacs_windows_stumpwm/"/>
            <id>https://babbagefiles.xyz/emacs_windows_stumpwm/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2018-08-18T21:36:00-06:00</published>
            <updated>2018-09-18T19:01:51-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>I&rsquo;m still working on getting my GuixSD machine configured, including
working on getting familiar with <a href="https://stumpwm.github.io/">StumpWM</a> – a windows manager written
in Common Lisp – which is the desktop paradigm I&rsquo;ve decided upon for
this Lisp-centric machine.</p>
<p>I&rsquo;m somewhat habituated to (my) <a href="https://awesomewm.org/">AwesomeWM</a> keybindings, which involve
the Super key in combination with various other keys, including say
<code>s-1</code> for tag/workspace 1, <code>s-3</code> for tag/workspace 3, &amp;c., and
<code>s-E</code> (i.e. hold Super and Shift and press <code>e</code>) to launch an
emacsclient (see below on the Emacs client/daemon
configuration). StumpWM could be configured in a somewhat similar
fashion (though it doesn&rsquo;t seem to quite use tag/workspaces in the
same fashion), but the &lsquo;tradition&rsquo; seems to be to use a prefix, which
is by default <code>C-t</code> (that is, hold Control and press <code>t</code>), which
is then released and followed with another key or key combination. I
don&rsquo;t really like using Control for windows management since it tends
to conflict with bindings in Emacs and elsewhere, so I&rsquo;m testing out
<code>s-F</code> (hold Super, press <code>f</code>) as a prefix (though whether I&rsquo;ll
stick with prefixed bindings or go back to single action bindings, I&rsquo;m
not yet certain).</p>
<p>From browsing other stumpwm configs, I came across a useful bit of
configuration:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nb">defun</span> <span class="nv">run-or-raise-prefer-group</span> <span class="p">(</span><span class="nv">cmd</span> <span class="nv">win-cls</span><span class="p">)</span>
  <span class="s">&#34;If there are windows in the same class, cycle in those. Otherwise call
</span><span class="s">run-or-raise with group search t.&#34;</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">windows</span> <span class="p">(</span><span class="nv">group-windows</span> <span class="p">(</span><span class="nv">current-group</span><span class="p">))))</span>
    <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">member</span> <span class="nv">win-cls</span> <span class="p">(</span><span class="nf">mapcar</span> <span class="nf">#&#39;</span><span class="nv">window-class</span> <span class="nv">windows</span><span class="p">)</span> <span class="ss">:test</span> <span class="nf">#&#39;string-equal</span><span class="p">)</span>
	<span class="p">(</span><span class="nv">run-or-raise</span> <span class="nv">cmd</span> <span class="o">`</span><span class="p">(</span><span class="ss">:class</span> <span class="o">,</span><span class="nv">win-cls</span><span class="p">)</span> <span class="no">nil</span> <span class="no">T</span><span class="p">)</span>
	<span class="p">(</span><span class="nv">run-or-raise</span> <span class="nv">cmd</span> <span class="o">`</span><span class="p">(</span><span class="ss">:class</span> <span class="o">,</span><span class="nv">win-cls</span><span class="p">)</span> <span class="no">T</span> <span class="no">T</span><span class="p">))))</span>
</code></pre></div><p>This function can then be used with specific applications, e.g.:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">defcommand</span> <span class="nv">run-or-raise-icecat</span> <span class="p">()</span> <span class="p">()</span>
	    <span class="p">(</span><span class="nv">run-or-raise-prefer-group</span> <span class="s">&#34;icecat&#34;</span> <span class="s">&#34;Icecat&#34;</span><span class="p">))</span>
</code></pre></div><p>The above function leverages <code>run-or-raise-prefer-group</code> to either
launch Icecat, if it is not already running, or else focus the Icecat
window, and successive calls will cycle through multiple Icecat
windows/windows if more than one Icecat window exists. This is extremely
useful as it&rsquo;s much less cognitively-tasking than figuring out which
window number Icecat currently is associated with.</p>
<p>This can then be assigned a binding(s) like:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;W&#34;</span><span class="p">)</span> <span class="s">&#34;run-or-raise-icecat&#34;</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;C-W&#34;</span><span class="p">)</span> <span class="s">&#34;run-or-raise-icecat&#34;</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;s-W&#34;</span><span class="p">)</span> <span class="s">&#34;run-or-raise-icecat&#34;</span><span class="p">)</span>
</code></pre></div><p>This means that one first presses the prefix (<code>s-f</code> for me) followed
by either <code>W</code> or <code>C-W</code> or <code>s-W</code> (that is, <code>Shift+w</code>,
<code>Control-Shift+w</code> or <code>Super-Shift+w</code>) to either launch Icecat or
focus/cycle through existing Icecat windows/windows.</p>
<p>Now, the way I typically use Emacs is to invoke it as a daemon
(i.e. <code>emacs --daemon</code>) and then connect Emacsclients to this daemon
(i.e. <code>emacsclient -c</code> for the &lsquo;windowed&rsquo; gtk-application,
<code>emacsclient -t</code> in the terminal). However, if we define a parallel
function for Emacs, a particular edge-case arises:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">defcommand</span> <span class="nv">run-or-raise-emacsclient</span> <span class="p">()</span> <span class="p">()</span>
		<span class="p">(</span><span class="nv">run-or-raise-prefer-group</span> <span class="s">&#34;emacs&#34;</span> <span class="s">&#34;Emacs&#34;</span><span class="p">))</span>
</code></pre></div><p>This works rather like the Icecat one as long as at least one
Emacsclient window exists (so if there are multiple window,
successive calls of <code>run-or-raise-emacsclient</code> will cycle through
them). <strong>However</strong>, if no emacsclient is currently open, it will launch an
undaemon&rsquo;ed Emacs (requiring loading the entire <code>init.el</code>), even
if/though an Emacs daemon is currently running and thus could be
attached to.</p>
<p>At least one solution to this is the following function (with
relevant keybindings):</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">defcommand</span> <span class="nv">decide-on-emacsclient</span> <span class="p">()</span> <span class="p">()</span>
 <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">equal</span> <span class="p">(</span><span class="nv">run-shell-command</span> <span class="s">&#34;pgrep \&#34;emacsclient\&#34;&#34;</span> <span class="no">t</span><span class="p">)</span> <span class="s">&#34;&#34;</span><span class="p">)</span>
     <span class="p">(</span><span class="nv">run-shell-command</span> <span class="s">&#34;emacsclient -c&#34;</span><span class="p">)</span>
     <span class="p">(</span><span class="nv">run-or-raise-emacsclient</span><span class="p">)))</span>

<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;s-e&#34;</span><span class="p">)</span> <span class="s">&#34;decide-on-emacsclient&#34;</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;C-e&#34;</span><span class="p">)</span> <span class="s">&#34;decide-on-emacsclient&#34;</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;e&#34;</span><span class="p">)</span> <span class="s">&#34;decide-on-emacsclient&#34;</span><span class="p">)</span>
</code></pre></div><p>The above function executes the shell-command <code>pgrep &quot;emacsclient&quot;</code>
and evaluates whether the output of that shell-command is equal to the
empty string (which will be the case only when no emacsclient is
running). Where at least one emacsclient is running, it executes the
<code>run-or-raise-emacsclient</code> function defined earlier,
focussing/cycling through running emacsclients. Where no emacsclient
is currently running, it executes instead <code>emacsclient -c</code>, opening
a windowed emacsclient.  And the bindings let me press the prefix and
then either <code>Super+e</code>, <code>Control+e</code> or simply <code>e</code> to execute this
new function.</p>
<p>I also have the following definitions:</p>
<div class="highlight"><pre class="chroma"><code class="language-lisp" data-lang="lisp"><span class="p">(</span><span class="nv">defcommand</span> <span class="nv">emacsclient-launch</span> <span class="p">()</span> <span class="p">()</span>
  <span class="p">(</span><span class="nv">run-shell-command</span> <span class="s">&#34;emacsclient -c&#34;</span><span class="p">))</span>

<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;s-E&#34;</span><span class="p">)</span> <span class="s">&#34;emacsclient-launch&#34;</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;C-E&#34;</span><span class="p">)</span> <span class="s">&#34;emacsclient-launch&#34;</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="vg">*root-map*</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">&#34;E&#34;</span><span class="p">)</span> <span class="s">&#34;emacsclient-launch&#34;</span><span class="p">)</span>
</code></pre></div><p>So that if I want to launch a new emacsclient window instead of switching to an
existing one, I can do so using one of series of keybindings parallel
to the previous set, but with a capital <code>E</code> rather than a lowercase
one.</p>
<p>This is working well for me, and is a nice example of the power of
using Lisp-based &lsquo;desktop environment&rsquo;.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/stumpwm" term="stumpwm" label="stumpwm" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Guix: You are in a maze of lispy little passages, (map equal? ′(′all ′alike) ′(′all ′alike))]]></title>
            <link href="https://babbagefiles.xyz/guix_maze_of_lispy_little_passages/"/>
            <id>https://babbagefiles.xyz/guix_maze_of_lispy_little_passages/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2018-08-04T21:47:00-05:00</published>
            <updated>2025-02-24T04:41:50-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>So I finally made a serious go of running <a href="https://www.gnu.org/software/guix/">GuixSD</a>, a GNU Linux distro
which is largely built on <a href="https://en.wikipedia.org/wiki/GNU_Guile">GNU Guile Scheme</a> (a dialect of Lisp) on one
of my machines (one I had actually put together with GuixSD in mind:
an X200 Thinkpad, which I <a href="https://libreboot.org/">Libreboot</a>&lsquo;ed and put a Atheros Wi-Fi card
in), and, to increase both the quantity and variety of Lisps involved,
am trying to use with <a href="https://stumpwm.github.io/">StumpWM</a> (which is written in Common Lisp).</p>
<p>It&rsquo;s a fascinating distro, modelled on <a href="https://nixos.org/">Nix</a>, but implemented in
Guile. It&rsquo;s not been exactly easy to get running (<a href="https://www.gnu.org/software/guix/blog/2017/back-from-fosdem-2017/">one of the videos on
GuixSD from Fosdem 2017</a> included the line &ldquo;[GuixSD] is like Gentoo for
grown ups&rdquo;), in part because its architecture is rather different from what
I&rsquo;ve experienced with other Linux distros, which use different package
managers perhaps and sometimes even different libc&rsquo;s, but generally
follow a similar design philosophy. Rather than spreading out
configuration across lots of different pieces, GuixSD seems to largely
try to concentrate it in specific configuration files which are
transactional in that they can be rolled back (and thus the system can
be rolled back to known working states).</p>
<p>It is a GNU-blessed distro, and does take the FSF&rsquo;s hard line (and to
my eyes sometimes weird line) approach to software. So no proprietary
software is included in the Guix repos, including firmware (and it
runs on the linux-libre kernel). That by itself is fine, but it means
the state of affairs for Guix-packaged browsers is pretty poor. No
Chromium, no Firefox. IceCat 52 is essentially what&rsquo;s currently
available (if IceCat were up to the latest Firefox ESR 60, it might be
easier) in terms of browsers which might be considered secure.</p>
<p>This led me to try to use the Nix installer by itself<sup><a href="#org-target--0b">*</a></sup><span class="org-target" id="org-target--0t"></span> to try to
install Firefox and Chromium. Sadly, I can&rsquo;t get Nix&rsquo;s Chromium to
work at all on GuixSD, and while Firefox works fine, I can&rsquo;t get it to
see my locally installed fonts (or other fonts I&rsquo;ve installed via
Nix).</p>
<p>Hopefully at some point <a href="http://next-browser.com/">Next Browser</a> will be packaged for Guix, to
bring in another major component written in (Common) Lisp. And when
(if?) IceCat 60 comes out, that will alleviate the pain somewhat. (I
was a long-time <a href="http://conkeror.org/">Conkeror</a> user, and I briefly tried it again in GuixSD,
but I&rsquo;m not certain of its security and uBlock Origin no long works
with it, which I believe is why I stopped using it in the first
place).</p>
<p>Other interesting Lispy pieces include <a href="https://www.gnu.org/software/mcron/">mcron</a>, a cron which accepts (as
well as Vixie cron style, I think) Guile config files. The <a href="https://www.gnu.org/software/guix/manual/en/html_node/Scheduled-Job-Execution.html">examples in
Guix manual</a> I couldn&rsquo;t really get to work. But via the <code>help-guix</code>
listserv I found that one can put simple guile scripts in
<code>~/.config/cron/job.guile</code>. Working out how to do a &lsquo;run this every N
minutes&rsquo; was not immediately obvious, but I figured out how to do it,
e.g.:</p>
<pre><code class="language-guile" data-lang="guile">; execute run_me every 5 minutes
(job '(next-minute (range 0 60 5)) &quot;run_me&quot;)
; run execute_me every 2 hours
(job '(next-hour (range 0 24 2)) &quot;execute_me&quot;)
</code></pre><p>One of the other great things about GuixSD is that its init manager,
<a href="https://www.gnu.org/software/shepherd/">GNU Shepherd</a>, is also written in Guile Scheme. I&rsquo;ve only had a chance
to play with it a little bit, but it seems very nice and it&rsquo;s good to
find other innovative init managers (I would mention here also <a href="http://smarden.org/runit/">runit</a>
and <a href="https://skarnet.org/software/s6/">s6</a>) which take very different approaches to <a href="https://www.freedesktop.org/wiki/Software/systemd/">systemd</a> (another
innovative init, or perhaps init+, but one that creates more problems
than it solves in the end, in my experience).</p>
<p>On the Guix package manager itself: I learned the hard way that
searching for packages in Guix is really only comfortable within
Emacs: so do <code>guix package -i guix-emacs</code> and then do everything else
from guix-emacs within Emacs ( <code>M-x guix-search-by-name</code> to search
package names by regex; and <code>M-x guix-search-by-regex</code> to search
names+descriptions by regex). The results returned by <code>guix package -s ....</code> in a terminal are not very browseable (though I tried valiantly
for some time). But if you&rsquo;re interested in Guix, you&rsquo;ll likely
interested in Emacs anyway.</p>
<p>What I&rsquo;m trying to build on this machine is something with lots of
Lisp. Of course the kernel is still a kernel written in C, as are lots
of the other pieces like the terminal &amp;c., but much of the user-facing
things: the package manager, the windows manager, the init, the job
scheduler (=cron), and (most importantly perhaps) the &lsquo;text editor&rsquo;
(read: document composer, email interface, irc interface, twitter
interface, blog post interface, code editor &hellip;) are all largely
written in and interacted with using some form of Lisp (Guile Scheme,
Common Lisp, Emacs Lisp).</p>
<p>Guix is a bit like Emacs, I think. It&rsquo;s an incredibly powerful tool
with lots of interesting possibilities, but when you start using it
you&rsquo;re presented with an empty screen with little indication of what
you can do. I&rsquo;ll be sticking with it, I think. Now I&rsquo;ve got to get to
grips with StumpWM and figure out how to configure <a href="https://github.com/jaagr/polybar">polybar</a>&hellip;</p>
<p>[And if you&rsquo;re curious about why Guix is pronounced like &ldquo;geeks&rdquo;, have
a look at <a href="https://slade.jnanam.net/post/scheming-french-geeks-with-guile/">this post over on my linguistics blog</a>.]</p>
<p><a href="#org-target--0t">*</a><span class="org-target" id="org-target--0b"></span> As well as being the package managers of both of their
respective distros, both the Nix and Guix package managers can be used
<strong>on top of</strong> other distros. Nix doesn&rsquo;t have quite the same hard line
approach to software licences as Guix.</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/lisp" term="lisp" label="lisp" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/guix" term="guix" label="guix" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/linux" term="linux" label="linux" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/stumpwm" term="stumpwm" label="stumpwm" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[New blog using Emacs, Org mode & Hugo]]></title>
            <link href="https://babbagefiles.xyz/new-blog/"/>
            <id>https://babbagefiles.xyz/new-blog/</id>
            
                    <author>
                        <name>Benjamin Slade</name>
                    </author>
            <published>2018-06-07T20:26:00-06:00</published>
            <updated>2018-06-07T23:00:25-06:00</updated>
            
            
            <content type="html"><![CDATA[<p>I&rsquo;ve been wanting to have an Emacs-powered blog for some
time. Finally, thanks largely to the yeoman&rsquo;s work put in by <a href="https://scripter.co">Kaushal
Modi</a> on <a href="https://ox-hugo.scripter.co/"><code>ox-hugo</code></a>, an exporter from native Org mode format to
Hugo-ready markdown files, as well his theme/configuration for Hugo,
<a href="https://gitlab.com/kaushalmodi/hugo-theme-refined"><code>hugo-refined</code></a> (which the theme used here is largely based on), I
finally have an ideal Emacs-centric blogging environment, though I&rsquo;m
sure I&rsquo;ll continue to tweak things a bit.</p>
<p><a href="https://gohugo.io/">Hugo</a> itself is a static website generator, which interprets <a href="https://en.wikipedia.org/wiki/Markdown">markdown</a>
files. It&rsquo;s written in <a href="https://golang.org/">Go</a>, a language developed at Google, which is
notable for including <a href="https://en.wikipedia.org/wiki/Ken_Thompson">Ken Thompson</a> and <a href="https://en.wikipedia.org/wiki/Rob_Pike">Rob Pike</a> among its creators.</p>
<p>This is more or less a continuation of my old blog, of <a href="https://babbagefiles.blogspot.com/">a similar name</a>,
and will largely address Emacs/elisp and other lispy things, and
Linux/UNIX/*nix and free/open source software and operating systems,
but, like my old blog, probably also occasionally <a href="https://babbagefiles.blogspot.com/search/label/minecraft">games</a> and <a href="https://babbagefiles.blogspot.com/2010/10/new-babbage-analytical-engine.html">pre-20th
century technology</a>, as well as whatever else catches my interest
(other than natural language things, which I&rsquo;ll keep on my
<a href="https://slade.jnanam.net/post/">professional site</a>).</p>
]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/categories/emacs" term="emacs" label="emacs" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/org" term="org" label="org" />
                             
                                <category scheme="https://babbagefiles.xyz/categories/hugo" term="hugo" label="hugo" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://babbagefiles.xyz/tags/go" term="go" label="go" />
                            
                        
                    
                
            
        </entry>
    
</feed>
