<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title><![CDATA[Joar von Arndt - streams]]></title>
<description><![CDATA[Joar von Arndt - streams]]></description>
<link>https://joarvarndt.se//tag-streams.html</link>
<atom:link href="https://joarvarndt.se/streams-rss.xml" rel="self" type="application/rss+xml" />
<lastBuildDate>Fri, 16 Oct 2026 05:35:16 +0800</lastBuildDate>
<item>
  <title><![CDATA[Emacs does not need LSP]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<nav id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#in-buffer-completion">In-buffer completion</a></li>
<li><a href="#jump-to-definition-and-references">Jump to definition and references</a></li>
<li><a href="#syntax-checking">Syntax checking</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</div>
</nav>
<hr>

<p class="dcap">
Language-server-protocol (<span class="small-caps">lsp</span>) is standard to communicate information about a
current programming project to an editor integration, powered by a &ldquo;language
server&rdquo; for the specific programming language. Emacs has two major
implementations of this editor functionality: <code>eglot</code> (built-in) and <code>lsp-mode</code>.
Getting this set up is usually one of the first things that new users will want
to do, but in my experience one can get by for a surprisingly long time using
Emacs without needing to reach for <code>eglot</code> or <code>lsp-mode</code>. That is because many of
the functions that <span class="small-caps">lsp</span>-servers provide can be provided by Emacs through other
means.
</p>

<p>
As someone who quite often moves between different programming languages, I find
the need to set up each <span class="small-caps">lsp</span>-server to a bit clunky. Despite being presented as a
simple one-stop solution, you often need to research exactly what server to use,
how to set it up, and to tailor it to your machine in some ways. For me, this
only makes sense in a few languages that I use often, and even then it might not
be the first thing I take care of.
</p>
<div id="outline-container-in-buffer-completion" class="outline-2">
<h2 id="in-buffer-completion">In-buffer completion</h2>
<div class="outline-text-2" id="text-in-buffer-completion">
<p>
The most obvious functionality is auto-complete (<span class="small-caps">aka</span> in-buffer completion). In
Emacs this is provided on multiple levels, which is sometimes confusing for new
users. There are many ways to <i>show</i> in-buffer completions, but they are all
powered by the same back-end functionality: <code>completion-at-point-functions</code>
(<span class="small-caps">capf</span>). This is a list of functions that run in order, only returning a value if
there is something to complete.
</p>

<p>
Some major modes (most notable perhaps <code>lisp-interaction-mode</code>) come with their
own buffer-local values of <span class="small-caps">capf</span><sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> that provide buffer-specific completions.
<code>completion-at-point</code> will then move on to the global value if the buffer-local
value of <span class="small-caps">capf</span> includes the value <code>t</code>. It is therefore this global value that we
are often interested in modifying.
</p>

<p>
For this we can use the <code>cape</code>-package — standing for &ldquo;Completion At Point
Functions&rdquo;. Specifically, we can make liberal use of the <code>cape-dabbrev</code>-function.
This <span class="small-caps">capf</span> makes use of Emacs&rsquo; built-in dabbrev-functionality. Dabbrev will scan
all the words in the buffers of your choice and give you completions for words
that you have already used. This means that, while working on your project, you
have quick and easy access to all the keywords and function names in your
currently opened buffers.
</p>

<p>
This also works as a form of autocomplete for writing prose, and so we can
combine these two using <code>cape-wrap-super</code>:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">cape-language</span> (<span class="org-type">&amp;optional</span> interactive)
    (<span class="org-keyword">interactive</span> (list t))
    (<span class="org-keyword">if</span> interactive
        (cape-interactive #'cape-language)
      (cape-wrap-super #'cape-dabbrev #'cape-dict)))
</code></pre>
</div>

<p>
This will combine the output <code>cape-dabbrev</code> and <code>cape-dict</code><sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup> into one <span class="small-caps">capf</span> that
can run at the same time (and therefore not block each other). For a simpler
contribution we also use the <code>cape-keyword</code> <span class="small-caps">capf</span> that comes with a number of
pre-configured programming language<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup> keywords. This is a bit of a pre-<span class="small-caps">lsp</span>
solution, where each editor provided support for each programming language. To
set up <code>cape</code>, simple add something like this to your init file:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(add-hook 'completion-at-point-functions #'cape-history)
(add-hook 'completion-at-point-functions #'cape-keyword)
(add-hook 'completion-at-point-functions #'cape-file)
(add-hook 'completion-at-point-functions #'cape-language)
(add-hook 'completion-at-point-functions #'cape-abbrev)
</code></pre>
</div>

<p>
This is the order in which <code>cape</code> will try your <span class="small-caps">capf</span>s, with the first matching
blocking the rest. As mentioned earlier, locally bound <span class="small-caps">capf</span>s will supersede
these global values, and so <code>python-completion-at-point</code> will run before any of
these in <code>python-mode</code> buffers.
</p>

<p>
Another improvement we can do is to change the value of
<code>cape-dabbrev-buffer-function</code>. The value of this variable is the function that
returns the buffers to scan for dabbrev results. By default this is
<code>cape-same-mode-buffers</code>, meaning that only buffers in the same mode (org-mode,
python-mode, <i>et cetera</i>) will be used for results. This is a good default, but I
would rather have always have <i>some</i> relevant completion candidate than nothing.
So personally I set this value to <code>cape-text-buffers</code>, which will return all
buffers in text-mode or prog-mode buffers. This means extra niceties like
referring to practically anything you are working on when writing a commit
message in git.
</p>
</div>
</div>
<div id="outline-container-jump-to-definition-and-references" class="outline-2">
<h2 id="jump-to-definition-and-references">Jump to definition and references</h2>
<div class="outline-text-2" id="text-jump-to-definition-and-references">
<p>
A second major feature of <span class="small-caps">lsp</span>-servers is &ldquo;jump to definition&rdquo; that allows you to
go to the code that creates a function (its <i>definition</i>) to inspect its inner
workings. There are a few different ways to do this.
</p>

<p>
The first is the package named <a href="https://github.com/jacktasia/dumb-jump"><code>dumb-jump</code></a> that uses <code>ag</code>, <code>rg</code>, or simply <code>grep</code> to
find definition-matching strings. This works similarly to <code>cape-keyword</code> in that
it uses a pre-configured set of language-syntax elements to match
function-defining cases.<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup> I however do not personally use this.
</p>

<p>
Instead I use another of <a href="https://github.com/minad">Minad</a>&rsquo;s wonderful additions to the Emacs&rsquo; ecosystem;
<a href="https://github.com/minad/consult"><code>consult</code></a>. I must confess that I slept on consult for far too long. I had it in
my config for a long time, but the number of (largely stand-alone) functions
made it a bit overwhelming to learn.<sup><a id="fnr.5" class="footref" href="#fn.5" role="doc-backlink">5</a></sup> What Consult offers is a suite of
improved versions of preëxisting Emacs utilities (and a few new ones building
upon said features), largely based on the <code>completing-read</code>.
</p>

<p>
Emacs has a built-in feature called <code>imenu</code> that scans the buffer&rsquo;s structure for
top-level features (Org-mode and markdown headers, or in this case function
definitions) and allows you to quickly move to them. Consult of course expands
on this by instantly showing you the place you will move to, but it also offers
an extension of this feature: <code>consult-imenu-multi</code> (as opposed to the regular
<code>consult-imenu</code>). This will not only scan the current buffer, but also all other
same-mode buffers. Better yet, it integrates with Emacs&rsquo; <code>project.el</code>, and so only
checks buffers that belong to the same project (<i>exempli gratia</i> git repository).
As I usually have the relevant buffers open in a project I am working on, this
works very well as an alternative to <code>dumb-jump</code>, and the preview and subsequent
confirmation mean that I am not worried about jumping to the wrong place —
although of course at the cost of speed.
</p>

<p>
Then we of course have the venerable <code>xref</code> built into Emacs. This works a bit
differently depending on the programming language backend, and plugs into an
<span class="small-caps">lsp</span>-server automatically if it is available. Use <code>xref-find-references</code><sup><a id="fnr.6" class="footref" href="#fn.6" role="doc-backlink">6</a></sup>
Otherwise we can use <code>etags-regen</code> to automatically create and refresh the <code>TAGS</code>
file that keeps track of the project structure:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">use-package</span> etags-regen
  <span class="org-builtin">:config</span>
  (<span class="org-keyword">setq</span> etags-regen-ignores
        '(<span class="org-string">"*.pyc"</span> <span class="org-string">".git"</span> <span class="org-string">".venv"</span> <span class="org-string">"venv"</span> <span class="org-string">"node_modules"</span>))
  (etags-regen-mode 1))
</code></pre>
</div>

<p>
Consult can also be used to improve upon this as well by replacing the <span class="small-caps">ui</span>
interaction component:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">setq</span> xref-show-xrefs-function #'consult-xref
      xref-show-definitions-function #'consult-xref)
</code></pre>
</div>

<p>
The reason why I usually prefer using <code>imenu</code> is because it requires no trip to
inspect the state of any files on the system — everything is already loaded and
available, and perhaps 90% of the time it includes the things that I need (since
they are things I am working on).
</p>
</div>
</div>
<div id="outline-container-syntax-checking" class="outline-2">
<h2 id="syntax-checking">Syntax checking</h2>
<div class="outline-text-2" id="text-syntax-checking">
<p>
Here we can use the built-in <code>flymake</code> or the standalone <code>flycheck</code>. Both of these
make use of external syntax-checking tools for each language, and so do require
some more setup on the host machine than the above solutions. In fact, Flycheck
can even act as its own <a href="https://emacsredux.com/blog/2026/07/29/flycheck-38/"><span class="small-caps">lsp</span>-server</a> just for syntax checking. But my experience
has still been that this is easier to deal with than <span class="small-caps">lsp</span>-servers that need to be
configured, started, reconnected, that crash, or have some other issue that need
to be dealt with; A simple <span class="small-caps">unix</span>-style shell command that prints text (and that
is then processed by Flymake or Flycheck) rarely needs much setup or maintenance
in my experience. This is a matter that I really just want out of my mind.
</p>
</div>
</div>
<div id="outline-container-conclusion" class="outline-2">
<h2 id="conclusion">Conclusion</h2>
<div class="outline-text-2" id="text-conclusion">
<p>
There are only so many things that can be done merely through these static
solutions. For example, neither of our in-buffer completion or
jump-to-definition solution will complete external libraries; only buffers that
have already been opened. There is no hover information.
</p>

<p>
I am not ideologically opposed to the use of language servers, and I will
sometimes intentionally start one if I expect to be working on a larger code
base for a longer amount of time. But tools like the ones above are nevertheless
incredibly useful because they work in all buffers and contexts.
</p>

<p>
One of the strengths of Emacs is how it acts as a sort of force-multiplier for
any task interacting with text, and how it builds upon improvements in one area
throughout the entire editor. Having some of the most critical <span class="small-caps">lsp</span>-like
abilities available anywhere you want without any extra effort is incredibly
powerful. This is especially noteworthy for things like <code>cape-dabbrev</code> since it
has made me so spoiled for autocompletion for everything I write.
</p>

<p>
I am sure that there are lots of different solutions and tools that I have
missed that would work to replace even more <span class="small-caps">lsp</span>-based ones, and probably
solutions that work better and/or are more elegant than the ones I use — the
answer may even be to just use <span class="small-caps">lsp</span>. But honestly I only think about the matter
of language server when I am talking with others. In day to day life I rarely
feel the need for them at all. ❦
</p>
</div>
</div>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
For <code>lisp-interaction-mode</code> the value is <code>(elisp-completion-at-point
t)</code>.
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
The results of <code>cape-dict</code> are decided by the value of the
<code>cape-dict-file</code> variable.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Here is the list of programming languages supported:
</p>

<p class="footpara">
C++, C, Caml, Crystal, C#, D, Elixir, Erlang, <span class="small-caps">f90</span>, Go, Java,
Javascript, Kotlin, Lua, Nim. Objective C, Perl, <span class="small-caps">php</span>, Purescript,
Python, Ruby, Rust, Scala, Scheme, Swift, Julia, Thrift, sh.
</p></div></div>

<div class="footdef"><sup><a id="fn.4" class="footnum" href="#fnr.4" role="doc-backlink">4</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
This supposedly misidentifies things sometimes; as expected, it is &ldquo;dumb&rdquo;
after all.
</p></div></div>

<div class="footdef"><sup><a id="fn.5" class="footnum" href="#fnr.5" role="doc-backlink">5</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
If you are using the built-in <code>switch-to-buffer</code> command (by default bound to
<code>C-x b</code>) I highly recommend switching it to <code>consult-buffer</code> instead. Instant
&ldquo;previews&rdquo; is super useful, and I often just spam <code>C-n</code> (N being right next to B)
to cycle through my open buffers instead of starting to write the exact name
that I want.
</p></div></div>

<div class="footdef"><sup><a id="fn.6" class="footnum" href="#fnr.6" role="doc-backlink">6</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
By default bound to <code>M-?</code>. To run <code>xref-go-back</code> press <code>M-,</code>.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[emacs]]></category>
  <category><![CDATA[technology]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/emacs-lsp.html</link>
  <guid>https://joarvarndt.se/emacs-lsp.html</guid>
  <pubDate>Sun, 06 Sep 2026 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Being Against LLMs is Against the Spirit of FLOSS]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<div class="intro">
<p>
Earlier this year <a href="https://joarvarndt.se/free-vibes">I wrote</a> <i>Vibe-coding Brings the Power of Emacs to
Everything</i> in a more positive manner surrounding this topic. This is
the negative version. I am not a radical in either direction
surrounding this debate. But the internet does not reward subtlety or
nuance, as I also <a href="https://joarvarndt.se/crypto">pointed out</a> in <i>Three Thoughts on Crypto and <span class="small-caps">nft</span>s</i>.
</p>

</div>

<p class="dcap">
The debate surrounding <span class="small-caps">llm</span>-generated code is more intense than
ever. There are some people who have recently tried to turn this
debate into action as an attempt to stem the tide of <span class="small-caps">llm</span> contributions
that are flooding maintainers. Most notably <a href="https://drewdevault.com/blog/Forking-vim/">Vim &ldquo;Classic&rdquo;</a> and my own
adopted public git forge of Codeberg. This is a shame, since it flies
against the spirit of Free (Libre) and Open Source Software (<span class="small-caps">floss</span>).
</p>

<p>
Linus Torvalds has effectively been vibe-coding the Linux kernel for
decades (his models being the world&rsquo;s best kernel programmers and his
agent harness email) and that has clearly turned out fine, but I still
write most my code the old-fashioned way (at least in a raw <span class="small-caps">loc</span>-way)
because I think that the hardest parts about programming are not
solved by <span class="small-caps">llm</span>s. I also do not like to refer to <span class="small-caps">llm</span>s as &ldquo;<span class="small-caps">ai</span>&rdquo;.<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>
</p>

<p>
You are of course <i>free</i> to not accept contributions made by <span class="small-caps">llm</span>s (or
any contribution for any reason) to your own project, and you are also
not forced to host someone else&rsquo;s vibe-coded projects on your
infrastructure; the beauty of <span class="small-caps">floss</span> is that they are then also free to
fork your project.<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup> But rejecting code not for technical reasons (of
which there are often many for vibecoded code) but for ideological
reasons is unfortunate.
</p>

<p>
At the same time there is of course a genuine problem for maintainers
happening that I do not know how to solve. One possible solution may
be to only accept contributions that you yourself <i>believe</i> to be
written by a human, while nominally accepting <span class="small-caps">llm</span>s to be used in any
capacity. This means that <span class="small-caps">llm</span>s can be used when they are useful, but
that code must remain readable and succinct enough for humans to
process and develop.
</p>

<p>
<span class="small-caps">llm</span>s are fundamentally a tool. Trying to ban <span class="small-caps">llm</span>s is giving them too
much credit; it is contributing to the framing made by companies like
Anthropic and Open<span class="small-caps">ai</span> that <span class="small-caps">llm</span>s are a new class of software that is so
powerful it must be banned, and entirely unlike all the other tools
that make software development easier. Imagine banning the use of
compilers and only accepting contributions written in Assembly as a
way to reduce the number of contributions. It works, but is it in any
way reasonable?
</p>

<p>
Such a policy — that contributing to a project because of the
circumstances of the contribution&rsquo;s creation, and not due to its
quality — is directly contradictory to the spirit of free
software. Being able to read, spread, and easily customize a program
is just as useful as being able to merely run it. And the long-term
strengths of free software is that it can adapt to everyone&rsquo;s
(collective) needs instead of each person building their own custom
software.<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>
</p>

<p>
Arguments that <span class="small-caps">llm</span>s fundamentally a part of a system of political
oppression fall flat against the case of open-weight models that can
be run at home on (high-end) consumer hardware. Are you going to be
against online search in general because most people use Google as
their provider?
</p>

<p>
The Russian-born French philosopher Alexandre Kojève pointed out that
&mdash; when studying the history of Hegel&rsquo;s <i>geist</i> &mdash; it is uninteresting
to study the existence of the nihilist, because the path of nihilism
is one of (intellectual) suicide. This is what is happening to these
projects ideologically opposed to <span class="small-caps">llm</span>s; projects like <i>Vim
Classic</i>. They are intellectually oriented not towards improving the
program either through bug-fixes or new features, but merely towards
making sure that every line of code was written by hand (wilfully
ignoring other automated tools such as <span class="small-caps">lsp</span>, the aforementioned
compilers, macros, automated config tools, even editors
themselves!). There are features that could be in the next release of
my beloved Emacs <i>right now</i> because they have been blocked because of
the initial use of an <span class="small-caps">llm</span>.<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup>
</p>

<p>
If you actually care about the direct impacts and externalities of
<span class="small-caps">llm</span>s, this is not the path forward. It shutting your eyes and plugging
your ears to the reality that <span class="small-caps">llm</span>s are widely used to develop
software, akin to what the Qing empire did during the heyday of
European colonialism. Instead the solution might be to solve the
underlying causes of overconsumption, fossil fuel use, and increasing
inequality. ❦
</p>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
&ldquo;<span class="small-caps">ai</span>&rdquo; is a very broad term, that today mostly refers to the
specific technology of Large language models. Other forms of
algorithms (both deterministic and not) perform acts of &ldquo;intelligence&rdquo;
— even very simple algorithms such as a single <code>if</code>-statement. At the
same time it is not clear to me that <span class="small-caps">llm</span>s or humans have some sort of
magical &ldquo;true&rdquo; intelligence that other systems do not.
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
And naturally you do not have to subsidise strangers&rsquo; bad ideas.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
This is coming from a die-hard Emacs user, where the biggest
strength comes from easily building your own tools and extending the
program for yourself.
</p></div></div>

<div class="footdef"><sup><a id="fn.4" class="footnum" href="#fnr.4" role="doc-backlink">4</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
In this case for fairly valid concerns about licensing issues,
another (albeit related) problem, by the <span class="small-caps">fsf</span>. How this problem is to
be solved I will leave to the lawyers.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[technology]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/free-vibes-2.html</link>
  <guid>https://joarvarndt.se/free-vibes-2.html</guid>
  <pubDate>Sat, 15 Aug 2026 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Dawn in 大安]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<hr>


<figure>
<img src="LibertySquare.jpg" alt="LibertySquare.jpg">

<figcaption><span class="figure-number">Figure 1: </span>My view of Liberty Square (自由廣場), as well as the National Concert Hall (right) and National Theater (left) from atop the steps of Chiang Kai-Shek Memorial Hall.</figcaption>
</figure>

<hr>

<p class="dcap">
Writing this, I have arrived at the island of Formosa, the adopted
home of the Republic of China (中華民國) as well as the Taiwanese
people. I intent to stay here during the fall term, as I complete part
of my studies abroad.
</p>

<p>
This journey (as planned) will be the longest time I have been away
from home, amounting to a rough 2% of my then-lived lifetime. I have
always been a part of a very internationalized family, with Oslo and
Berlin holding especially fond places in my heart. But thinking of
being away from Stockholm, I am nonetheless filled with a sense of
yearning in my heart:
</p>

<blockquote>
<p>
Of cities I know in the world
</p>

<p>
Thou are the city that has it all
</p>

<p>
Through the Malar&rsquo;s love to the sea
</p>

<p>
A mixture of salty and sweet.
</p>
</blockquote>

<p>
Last year, while I was <a href="https://joarvarndt.se/chinese_phenomenology.html">visiting the <span class="small-caps">prc</span></a>, I was tasked to photograph
everything I ate while I was away. This was a wonderful idea, and one
that I wish to adapt to my longer stay away. For this aim I have
created a sub-blog, generated with similar tooling as that of the main
blog. It can be found at <a href="https://joarvarndt.se/food/">https://joarvarndt.se/food/</a>, with the landing
page showing one meal for every (Gregorian) calendar day. This is
automatically generated by a small Emacs lisp <a href="https://codeberg.org/Joar/food/src/branch/main/food.el">program</a> building on my
use of my own fork of <a href="https://codeberg.org/Joar/org-static-blog">org-static-blog</a>. This means that each entry is
itself a full post, that I could theoretically fill with entire essays
if I wished it. Head on over and check out if anything looks tasty,
right now there are
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(message <span class="org-string">"%s posts (last updated %s)"</span>
         (length (directory-files
                  <span class="org-string">"~/Documents/blog/food/posts/"</span>
                  nil
                  directory-files-no-dot-files-regexp))
         (format-time-string <span class="org-string">"%B %d, %Y"</span>))
</code></pre>
</div>

<pre class="example">
66 posts (last updated October 16, 2026)
</pre>

<p>
for you to look through! If you want to subscribe to these (short)
posts, I have created a dedicated <a href="https://joarvarndt.se/food/rss.xml"><span class="small-caps">rss</span> feed</a> for that.
</p>

<p>
It is developed as a subproject to the main blog using git, and so the
two are heavily interwoven with each other. The <span class="small-caps">css</span> stylesheet of the
&ldquo;sub-blog&rdquo; even <code>@import</code>-s the main blog stylesheet, only changing the
few parts that need custom tweaking. That way the small, incremental
improvements and opinionated changes I make to the main blog&rsquo;s design
will automatically carry over into this project. 
</p>

<p>
I have some expectations written down privately that I plan to go
through once I return, an inspiration from a friend who recently
returned from a time in the orient. Hopefully these will be
interesting to cover once I finish my time here. My initial impression
is that it is quite similar to the mainland<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> but still distinctly
different. How to describe these differences I do not yet know.
</p>

<p>
One wonderful surprise is how every receipt is a lottery ticket as
part of a scheme to incentivise people to demand them, and thus make
it harder for businesses to avoid taxes. It is interesting to see how
one can use the otherwise destructive tendencies exploited by gambling
for a noble cause.
</p>

<p>
So far I have been very warmly welcomed, and I look forward to a great
time familiarising myself with this foreign country and its people. ❦
</p>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Even (or especially) in the small things. For example the 100元
人民币 and 100圓 新臺幣 bills are both red. They also have a tendency
for announcements that I complained about in the post on the <span class="small-caps">prc</span>.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[website]]></category>
  <category><![CDATA[life]]></category>
  <category><![CDATA[china]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/daan-dawn.html</link>
  <guid>https://joarvarndt.se/daan-dawn.html</guid>
  <pubDate>Wed, 12 Aug 2026 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Automatically Discover Mail Encryption Keys with Emacs]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<hr>

<p class="dcap">
I have found a peculiar delight in encryption, and particularly in
using <span class="small-caps">pgp</span>-based asymmetric cryptography. This is especially useful for
securing email communications, but doing so requires remembering to
run <code>M-x mml-secure-sign</code> or <code>mml-secure-encrypt</code> in Mail Utilities For
Emacs (Mu4e) before sending the message. For this reason I have been
wanting to write a quick method that automatically adds signing
capabilities and encryption only when the recipient has a public
key. Thankfully, I did not have to do so because of Nicolas
Cavigneaux&rsquo;s <a href="https://www.bounga.org/emacs/2026/06/30/sign-always-encrypt-when-possible-in-mu4e/">recent post</a> <i>Sign Always, Encrypt When Possible</i> where he
showcases his functions for doing exactly that. It prompted me to tweak
it slightly, and to add the functionality to automatically discover
public keys that I have yet to import.
</p>

<p>
Cavigneaux&rsquo;s post sets up an elegant arrangement: email should always
be signed with your <span class="small-caps">pgp</span> key, and you should &ldquo;upgrade&rdquo; from there to
encryption only if your correspondent can handle encrypted mail and
has shared their public key with you. This is done by using Emacs&rsquo;
built-in Easy Privacy Guard (<span class="small-caps">epg</span>) tooling to compare the owners of the
imported public keys on the machine with the list of recipients in an
email.<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> If you have the public key for <i>all</i> recipients, the message
will be automatically encrypted — otherwise it will merely be signed
to prove that you are the author.
</p>

<p>
This was exactly what I had wanted, and I quickly incorporated this
functionality into my own configuration. Testing it out, I felt a bit
uneasy sending encrypted mail automatically. Because it is not the
default, I felt that fully automated encryption made me unsure whether
the mail has been encrypted or not — even when I know I have the
recipient&rsquo;s public key. I thus made the slight modification of
prompting for both encryption and signing:
</p>

<div class="org-src-container">
<pre class="src src-elisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">kudu/message-sign-encrypt-if-all-keys-available</span> ()
    <span class="org-doc">"Add MML tag to encrypt message when there is a key for each recipient,
sign it otherwise."</span>
    (<span class="org-keyword">if</span> (bounga/message-all-epg-keys-available-p)
        (<span class="org-keyword">if</span> (y-or-n-p <span class="org-string">"Encrypt? "</span>)
            (mml-secure-message-sign-encrypt)
          (<span class="org-keyword">when</span> (y-or-n-p <span class="org-string">"Sign? "</span>)
            (mml-secure-message-sign)))
      (<span class="org-keyword">when</span> (y-or-n-p <span class="org-string">"Sign? "</span>)
        (mml-secure-message-sign))))
</code></pre>
</div>

<p>
In practice I will probably always be pressing <code>Y</code> when prompted, but
this adds a level of certainty that the code is running correctly. It
reminds me of an advertising gimmick for housewives in the 1950s: when
cake mix first became available, it was not appreciated because it was
perceived as being &ldquo;too easy&rdquo; — it did not feel like baking a cake
yourself. And so the recipe was tweaked to simply require an extra egg
to be added. In practice this was a trivial change, but it meant that
you felt more responsible for the finished work.
</p>

<p>
But there is an additional step I want to automate that Cavigneaux
alluded to, but did not implement. He writes:
</p>

<blockquote>
<p>
[T]he policy is only as good as my keyring. Encryption depends on
having imported the right public keys; nothing here fetches them for
me.
</p>
</blockquote>

<p>
I have recently been having more correspondence with people using the
Swiss-based email provider <i>Protonmail</i>. Proton provides support for the
Web Key Directory (<span class="small-caps">wkd</span>) method of providing keys, where the <span class="small-caps">dns</span>
settings of a domain point to a file containing a public key for a
specific user. This allows the email itself to &ldquo;provide&rdquo; its own
public key for encryption. The public key of any <code>@protonmail.com</code> or
<code>@pm.me</code> address can be quickly imported in a second using <code>gpg
--locate-external-keys email@pm.me</code>. Proton will similarly use <span class="small-caps">wkd</span> to
get <i>your</i> public key, and so you will receive encrypted mail as well.
This is still easy to set up for any other email provider, as long as
you can edit the domain settings.<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>
</p>

<p>
Sadly, <span class="small-caps">epg</span> does not come with any options for Gnu<span class="small-caps">pg</span>&rsquo;s <code>--locate-keys</code> or
<code>--locate-external-keys</code> flags. This might be a good addition. In the
meantime, we can write a quick and dirty function to do the job for us
in the background when opening any new email:
</p>

<div class="org-src-container">
<pre class="src src-elisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">kudu/message-locate-keys</span> ()
  <span class="org-doc">"Tries to find the public keys of '</span><span class="org-doc"><span class="org-constant">bounga/message-recipients</span></span><span class="org-doc">' via WKD through --locate-keys."</span>
  (<span class="org-keyword">dolist</span> (recipient (string-to-list (bounga/message-recipients)))
    (<span class="org-keyword">let</span> ((recipient-email (cadr recipient))
          (proc (make-process
                 <span class="org-builtin">:name</span> <span class="org-string">"gpg-locate-keys"</span>
                 <span class="org-builtin">:command</span> (list epg-gpg-program <span class="org-string">"--no-tty"</span> <span class="org-string">"--locate-keys"</span> (cadr recipient))
                 <span class="org-builtin">:connection-type</span> 'pipe
                 <span class="org-builtin">:filter</span> (<span class="org-keyword">lambda</span> (proc string)
                           (process-put proc 'output
                                        (concat (<span class="org-keyword">or</span> (process-get proc 'output) <span class="org-string">""</span>) string)))
                 <span class="org-builtin">:sentinel</span> (<span class="org-keyword">lambda</span> (proc event)
                             (<span class="org-keyword">when</span> (eq (process-status proc) 'exit)
                               (<span class="org-keyword">let</span> ((output (process-get proc 'output))
                                     (email (process-get proc 'email)))
                                 (<span class="org-keyword">cond</span> ((<span class="org-keyword">and</span> output (string-match-p <span class="org-string">"imported: [1-9]"</span> output))
                                        (message <span class="org-string">"Public key imported for %s"</span> email))
                                       ((<span class="org-keyword">and</span> output (string-match-p <span class="org-string">"pub"</span> output))
                                        (message <span class="org-string">"Public key available for %s"</span> email))
                                       (t (message <span class="org-string">"No public key found for %s"</span> email))
                                   )))))))
      (process-put proc 'email recipient-email))))
</code></pre>
</div>

<p>
We use <code>--locate-keys</code> because the stricter <code>--locate-external-keys</code> will
check the domain even if a key already exists in the local
keyring. <code>--locate-keys</code> will instead instantly find local keys and
finish early, while only missing keys will be checked. This is done
asynchronously through <code>make-process</code> so that it does not cause the <span class="small-caps">ui</span>
to freeze as we wait for network requests to finish. <span class="small-caps">gpg</span> seems to
check very quickly, so it is not a large performance hit regardless.
</p>

<p>
We want to have imported any new keys before sending our email because
Cavigneaux&rsquo;s functions are run on <code>'message-send-hook</code>. We therefore run
<code>kudu/message-locate-keys</code> much earlier:
</p>

<div class="org-src-container">
<pre class="src src-elisp"><code>(add-hook 'mu4e-view-rendered-hook 'kudu/message-locate-keys)
</code></pre>
</div>

<p>
This adds any correspondents&rsquo; public keys on any opened email, and so
does not have to go through your entire contacts list. A downside is
that it does not check for a public key when you are the person
<i>sending</i> an unsolicited email, but it will when you get their reply.
</p>

<p>
This is not the world&rsquo;s most elegant function, and if someone more
familiar with <span class="small-caps">epg</span> has any comments regarding ways to improve it I will
happily edit this post for improvements. Just send me a (<span class="small-caps">pgp</span>
encrypted!) email. Run <code>echo 'am9hcnhwYWJsb0B2b25hcm5kdC5zZQ' | base64
--decode | xargs gpg --locate-external-keys</code> to import my key. If you
have <span class="small-caps">wkd</span> set up, I will automatically fetch your public key. If not,
this is a great opportunity to fix that!
</p>

<p>
Gnu<span class="small-caps">pg</span>&rsquo;s <code>--locate-keys</code> can also be made to automatically check public
keyservers, such as the popular <a href="https://keys.openpgp.org">keys.opengpg.org</a> one. To do this, add
this line to <code>~/.gnupg/gpg.conf</code>:
</p>

<div class="org-src-container">
<pre class="src src-conf"><code>auto-key-locate clear,local,wkd,keyserver
</code></pre>
</div>

<p>
And then this to <code>~/.gnupg/dirmngr.conf</code>:<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>
</p>

<div class="org-src-container">
<pre class="src src-conf"><code>keyserver hkps://keys.openpgp.org
</code></pre>
</div>

<p>
I will also note how wonderful Emacs and <span class="small-caps">epg</span> make working with
encrypted files, messages, and/or text more generally. I must admit
that I used a plain-text <code>.authinfo</code> for an embarrassingly long time to
store some of my secrets, thinking it would be bothersome to use the
encrypted <code>.authinfo.gpg</code> for <code>auth-sources</code>. If you are like me, do not
worry — the peace of mind from being able to write down personal
information and simply leave it scattered on your filesystem is well
worth just having to type your password. ❦
</p>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
From Cavigneaux&rsquo;s post, the function to get all message recipients. Returns a list formatted like <code>(("First recipient" "first@domain.tld") ("Second recpient" "second@domain.tld") ...)</code>:
</p>

<div class="org-src-container">
<pre class="src src-elisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">bounga/message-recipients</span> ()
  <span class="org-doc">"Return a list of all recipients in the message, looking at TO, CC and BCC.
Each recipient is in the format of `</span><span class="org-doc"><span class="org-constant">mail-extract-address-components</span></span><span class="org-doc">'."</span>
  (mapcan (<span class="org-keyword">lambda</span> (header)
            (<span class="org-keyword">let</span> ((header-value (message-fetch-field header)))
              (<span class="org-keyword">and</span>
               header-value
               (mail-extract-address-components header-value t))))
          '(<span class="org-string">"To"</span> <span class="org-string">"Cc"</span> <span class="org-string">"Bcc"</span>)))
</code></pre>
</div></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
So no <code>@gmail.com</code>, <code>@outlook.com</code>, or <code>@yahoo.com</code> addresses. You can
still use these to service your mail; <span class="small-caps">wkd</span> does not interact with any
email-related <span class="small-caps">dns</span> records.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
I also needed to add &ldquo;<code>disable-ipv6</code>&rdquo; to force connections over <span class="small-caps">ipv4</span>
because I could not connect over <span class="small-caps">ipv6</span>. I imagine that this is a rare
issue however, but it is worth mentioning in case <span class="small-caps">gpg</span> inexplicably
freezes.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[emacs]]></category>
  <category><![CDATA[technology]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/mu4e-wkd.html</link>
  <guid>https://joarvarndt.se/mu4e-wkd.html</guid>
  <pubDate>Fri, 03 Jul 2026 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[The Kramer-Peterman Duality: Fragments on Eccentricity]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle">Fragments on Eccentricity</h2>
<hr>

<p class="dcap">
The concept of <span class="small-caps">fomo</span> has always alluded me. Part of the reason is a
persistent need for eccentricity, for growing against the grain. That
is not to say that I am persistently rebellious, but that there is a
natural aversion to doing things &ldquo;the way everyone else does it&rdquo;.
</p>

<p>
The word <i>eccentricity</i> has for me always evoked a relation to its
meaning within astrodynamics<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> — the amount by which a given orbit
deviates from a &ldquo;perfect&rdquo; circle to an ellipse. For a spacecraft
raising or lowering one&rsquo;s orbit requires entering into an elliptical
orbit before one can <i>circularise</i>, meaning that an eccentric orbit
often means that an object is either shooting away from a body or
crashing down toward it, only to gently miss. By comparison a circular
orbit is merely the stable, unchanging floating above another
world. The eccentric is instead constantly changing speed and
direction, moving towards and away by large amounts while maintaining
some sort of liminal existence.
</p>

<div class="dinkus">
⁂
</div>

<p>
<i>Seinfeld</i> (1989-1998) is one of the most influential sitcoms of all
time, rivalled only by its contemporary <i>Friends</i>. It covers the
dramatized life of the titular Jerry Seinfeld, a comedian who is
played by himself, in New York City. Jerry&rsquo;s life, like in most
sitcoms, is closely intertwined with a quirky cast of misfits that he
calls his friends: Elain Benes — a cultured literature major who sees
herself as the only &ldquo;normal&rdquo; member of the group, George Constanza —
an insecure but highly determined and neurotic man whose bold attempts
at improvement often end in failure, and Cosmo Kramer — the whimsical
and perpetually unemployed neighbour whose bold actions (contrary to
Costanza&rsquo;s) often succeed, but that are then undermined by Kramer&rsquo;s
own poor judgement.
</p>

<p>
In the final episode of the sixth season Elaine, down on her luck,
meets Jacopo &ldquo;J.&rdquo; Peterman, the author of the <i>Peterman&rsquo;s</i> fashion
catalogue, where accessories or articles of clothing are marketed with
stories of adventure, mystique, and international intrigue. Elaine
starts to work on the catalogue, and Peterman becomes a side character
in the world of <i>Seinfeld</i>. He is well-travelled, wealthy, and charming,
seemingly living proof of the life described in the narratives of the
catalogue.
</p>

<p>
In the episode <i>The Van Buren Boys</i> Elaine is tasked with ghostwriting
Peterman&rsquo;s biography, a task that she expects to be fairly easy
because of Peterman&rsquo;s history of mischief. She anticipates that it
will merely be a question of documenting and detailing the his many
stories, but is caught off-guard when he instead wants her to write
about his ordinary everyday life instead of exotic travels. Elaine
quickly realises that Peterman&rsquo;s normal life is (as for most people)
dreadfully dull. Instead Peterman buys the rights to the stories of
Kramer&rsquo;s life — who has a genuinely bizarre and <i>eccentric</i> lifestyle —
for a one-time fee of 500 <span class="small-caps">usd</span>.
</p>

<p>
This duality exists within all of us, although in differing
degrees. The two aspects of <i>Kramer</i> and <i>Peterman</i> are two sides of the
same coin, the striving for action and authenticity (Peterman) as well
as the genuine everyday experimentation and adventurousness itself
(Kramer). They are both however true eccentrics.
</p>

<p>
Peterman is fundamentally a fake — a poser — who tries to appear
differently from how he &ldquo;truly&rdquo; is. Regardless of if he does it
through fictitious takes in the catalogue, buying <span class="small-caps">jfk</span>&rsquo;s golf clubs or
antique cake, or venturing on impromptu journeys to Burma<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup> it is
always in the attempt to appear as something other than his &ldquo;true&rdquo;
self. In this regard he truly is a <i>performative</i> man, in the sense that
his life is the performance of the role that he has chosen for
himself.
</p>

<blockquote>
<p>
You only know me as you see me, not as I actually am.
</p>

<p>
⸺ Immanuel Kant
</p>
</blockquote>

<p>
Kramer instead represents the reverse; an <i>ideal</i> form of the eccentric
attempting to anchor themselves in the real. Instead of attempting to
stand out Kramer tries to act as society wishes that he would act. His
&ldquo;job&rdquo; at Brant-Leland Investments, something that appears to us as
absurd, is absurd precisely in its dichotomy with the &ldquo;ordinary&rdquo;
working life of the audience.
</p>

<p>
Thus Kramer and Peterman are equally troubled beings wrestling with
the social expectations of <i>the eccentric</i> as being both the troublesome
madman and the hopeless romantic of life. What they have in common is
their unwavering confidence in themselves,<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup> in that whatever project
they assume it will only be seen through by their own will and hope.
</p>

<blockquote>
<p>
I’m out there, Jerry. And I’m loving every minute of it!
</p>

<p>
⸺ Cosmo Kramer
</p>
</blockquote>

<p>
What is one to choose then? Am I a Kramer of a Peterman? A narcissist
would never confess to being a narcissist<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup> and in a similar vein a
true <i>Kramer</i> would never want to be seen as a <i>Kramer</i>, and a <i>Peterman</i>
would never want to be seen as a <i>Peterman</i>. Both are, despite their
similar surface-level behaviour, attempting to imitate the life of the
other. This is of course most clearly seen in Peterman buying Kramer&rsquo;s
stories, but an example of the reverse is Kramer&rsquo;s attempt to market
his perfume <i>The Beach</i> to an executive at Calvin Klein<sup><a id="fnr.5" class="footref" href="#fn.5" role="doc-backlink">5</a></sup> — he wishes to
advance to story <i>teller</i> and not merely the mystic who lives for his
own sake.
</p>

<p>
Choosing the reverse from what wishes to be is no better, as it plays
into the performative play forced upon the Kramer/Peterman. Instead we
must accept two facts: (i) that Kramer and Peterman are not two
separate state of being but instead one holistic idea — <i>id est</i> the
replacement of &ldquo;Kramer/Peterman&rdquo; with Kramer+Peterman<sup><a id="fnr.6" class="footref" href="#fn.6" role="doc-backlink">6</a></sup> — as well as
(ii) that this is inherently an unstable phenomenon. By this
instability is meant not that Kramer+Peterman will inevitably
collapse, but instead merely that it is in <i>movement</i> relative to both
itself and the world.
</p>

<p>
The consequences of these two statements are tremendous for the life
of the eccentric. It means that the goal of the eccentric is
constantly moving and that a Kramer+Peterman must constantly move with
it. There is no possibility to &ldquo;rest on one&rsquo;s laurels&rdquo; and simply live
with the reputation of once having been eccentric. It is a constant
spirit that one must involve oneself with, even at the expense of
temporary comfort. But that the Kramer+Peterman dichotomy is moving
relative to <i>itself</i> is also meaningful, as it means that simply
&ldquo;picking&rdquo; either Kramer or Peterman is a fruitless task (for reason we
have already established). One is by necessity both elements of
Kramer+Peterman, and each element takes turns (although not
necessarily equal turns) in being the dominant factor in the pair. The
other is still present and merely dormant — ready to take over when
the opportunity arrives.
</p>

<p>
What then decides the degree to which either <i>Kramer</i> or <i>Peterman</i> is
dominant? It is most assuredly the degree of self-awareness that is
possessed in the moment.<sup><a id="fnr.7" class="footref" href="#fn.7" role="doc-backlink">7</a></sup> The <i>Kramer</i> is dominant when the lack of
self-awareness of appears, when the eccentric is simply acting out of
instinct for what they deem required. <i>Peterman</i> is the opposite, being
an insecurity over one&rsquo;s lack of eccentricity and whimsy, and thus
corresponding to the conscious effort of standing out. The two must
not always be in a strict hierarchical order, but can fluidly exist in
different ratios between one another; they may even be of equal share.
</p>

<p>
We can therefore conclude that the Kramer-Peterman dichotomy is not
really a dichotomy, but a <i>duality</i> where neither side can truly exist
without the other. The hipster or eccentric, filled with <i>whimsy</i> is
therefore always insecure in some regard, but brave enough to see
their vision through; the master in Hegel&rsquo;s <a href="https://en.wikipedia.org/wiki/Lord%E2%80%93bondsman_dialectic"><i>master &amp; slave
dialectic</i></a>. ❦
</p>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
The movements of objects in space. Orbital mechanics.
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Myanmar to you or me, but always <i>Burma</i> to Peterman. Itself an
attempt at eccentricity.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Some of this of course coming from the absurdity necessary in
making a network sit-com, but like all fiction grounded in truth.
</p></div></div>

<div class="footdef"><sup><a id="fn.4" class="footnum" href="#fnr.4" role="doc-backlink">4</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Since that would recognize the existence of a negative flaw in
themselves.
</p></div></div>

<div class="footdef"><sup><a id="fn.5" class="footnum" href="#fnr.5" role="doc-backlink">5</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
The idea is then later stolen and re-branded as <i>Ocean</i> by Calvin
Klein.
</p></div></div>

<div class="footdef"><sup><a id="fn.6" class="footnum" href="#fnr.6" role="doc-backlink">6</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Similar to the ancient Chinese concept of 陰陽 (yīn yáng).
</p></div></div>

<div class="footdef"><sup><a id="fn.7" class="footnum" href="#fnr.7" role="doc-backlink">7</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Keep in mind that a striving for eccentricism must always be
present, and that this is defined in terms of social relation with the
world around you.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[culture]]></category>
  <category><![CDATA[life]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/eccentricity.html</link>
  <guid>https://joarvarndt.se/eccentricity.html</guid>
  <pubDate>Sun, 21 Jun 2026 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Vibe-coding Brings the Power of Emacs to Everything]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<hr>

<p class="dcap">
One of the first use-cases I found for <span class="small-caps">llm</span>s back when <i>Chat<span class="small-caps">gpt</span></i> first
released was automating the creation of citations, or rather the
transformations of citations structured in one way into <code>.bib</code>-files
that can be used to create a wide variety of uniform citations in
\(\LaTeX\) documents. <span class="small-caps">llm</span>s are <i>fantastic</i> for this sort of work, where
some sort of messily structured data needs to be transformed into some
other form that is then useful. As <span class="small-caps">llm</span>s become cheaper and cheaper it
becomes easier and easier to make data become useful. The benefits of
this is obvious to the point of it being the main strength of what is
perhaps the world&rsquo;s oldest continuously developed software project;
<span class="small-caps">gnu</span> Emacs.
</p>

<p>
Much of software engineering is <a href="https://joarvarndt.se/cl-workflows">piping</a> that transforms data from one
form to another, where we can then process it, ingest it, or present
it in some interesting and beneficial way. This is true regardless of
the underlying nature of that data. In Emacs there are primarily two
forms of this data: <a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)">lisp</a> code and <a href="https://joarvarndt.se/text">text</a>. Ardent lisp wizards will
object on the basis that one of the core strengths of lisp is that it
does not discriminate between lisp as a program qua list of
instructions and lisp as a data structure due to its simple syntax.<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>
The classic <span class="small-caps">unix</span> environment also made use of this compatibility made
possible by a common language of text.
</p>

<p>
Emacs is a lisp interpreter that comes with a text editor and tools to
evaluate elisp code written in said editor. This simple basis allows
Emacs to very quickly and easily be extended. While other programs
(and even text editors specifically) may offer theoretically similar
capabilities<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup> through scripting languages and <span class="small-caps">api</span>s they do not offer
the truly <i>free</i> experience that only a few Emacs-like programs build
their experience upon. Most code written in Emacs is not packaged or
distributed anywhere, but is made up of small and opinionated changes
and functions that are likely not maintained in any way. This means
that each Emacs user&rsquo;s computing experience is personally tailored to
his or hers own preferences.
</p>

<p>
The second of the four freedoms (freedom #1) of free software is the
&ldquo;the freedom to study how the program works, and change it to make it
do what you wish&rdquo;. In practice this only means access to the source
code under a free-software license. But Emacs takes this much further;
instead of a merely &ldquo;negative&rdquo; freedom (freedom <i>from</i> proprietary
restrictions) it adopts a positive approach, where the user is
directly given the tools and documentation<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup> to change each and every
part of the Emacs source code.
</p>

<p>
Emacs, <span class="small-caps">unix</span>, and modern <span class="small-caps">llm</span>s all make use of the unique strengths of
text. Emacs however goes much further in this regard than the standard
<span class="small-caps">unix</span> system, and in many regards can be seen as an extension and
intensification of it. <a href="https://christiantietze.de/posts/2026/02/complete-feedback-loop-in-emacs/">Tietze</a> pointed out recently how the textual
representations of almost all data in Emacs is &ldquo;completes computing&rdquo;
through the universality of text and primarily the <i>text buffer</i>.
</p>

<p>
If the costs of creating software goes to zero due to continuing
advancements in <span class="small-caps">llm</span>s it would bring this quality that Emacs has to all
software. The restrictions of proprietary software has always been an
invention by monopolistic software companies wishing to add a shackle
on what is really just a bunch of abstract logical statements. That
this has been maintained is impressive, but it can not do so under the
onslaught of code produced by <span class="small-caps">llm</span>s.
</p>

<p>
Trivially creating <a href="https://joarvarndt.se/xournal-emacs">quick and simple programs</a> that serve the user is
Emacs&rsquo; greatest strength, and it is something that will be accessible
to everyone, no matter their experience in software creation. One will
be able to make small little applications that serve yourself, and
because of its low cost will naturally freely share them with friends,
colleagues, and family members. It is, to repeat a often-used
<a href="https://joarvarndt.se/email">sentiment</a> of mine, a revolution in the field of software development —
a dramatic return to the older state of affairs, albeit now aided by
the lessons of the time in between. In this case it is a return to the
times before &ldquo;free software&rdquo;, when specifying that a given software
was free to use, share, and modify was not necessary but expected and
normal.
</p>

<p>
To reiterate, vibe coding and <span class="small-caps">llm</span>s have two great strengths:
</p>

<ol class="org-ol">
<li>Easily creating, recreating, and modifying small programs that do
not need to be maintained and are tailored for the user&rsquo;s needs.</li>
<li>Formatting roughly structured data into ways that fit the user&rsquo;s
needs, or writing small scripts that do so.</li>
</ol>

<p>
The consequence of these two strengths is a renaissance of free
software development where the user become free to construct their
computing environment however they see fit. &ldquo;Emacs is a great
operating system, if only it came with a decent text editor&rdquo; goes the
famous quip; Emacs is of course not a operating system in the strict
sense,<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup> but it does allow for the almost complete reshaping of
one&rsquo;s interactions with a computer — being able to replace most other
use-facing applications. <span class="small-caps">llm</span>s extend this freedom to outside the frame
of Emacs and into almost every part of the software stack. ❦
</p>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Simple in the sense of this quote by Leonardo da Vinci:
</p>

<blockquote>
<p>
A poet knows he has achieved perfection not when there is nothing left
to add, but when there is nothing left to take away.
</p>
</blockquote>

<p class="footpara">
This simplicity does not mean that it is impossible to construct
elaborate or complex programs in lisp — in fact it is one of the most
<i>expressive</i> programming languages. Rather it refers to the basic axioms
inferred from the nature of prefix notation and the structure of the
abstract syntax tree itself.
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
What matters in theory is in fact not very interesting. Many
languages and processes are Turing-complete, and could thus be used to
create any other program, but what actually matters is the ease and
manner of creating such a program.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Emacs has extensive manuals, but most of all it is its nature as
the &ldquo;self-documenting editor&rdquo; that gives it this quality.
</p></div></div>

<div class="footdef"><sup><a id="fn.4" class="footnum" href="#fnr.4" role="doc-backlink">4</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
In that it does not facilitate the interaction between software
and hardware.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[emacs]]></category>
  <category><![CDATA[technology]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/free-vibes.html</link>
  <guid>https://joarvarndt.se/free-vibes.html</guid>
  <pubDate>Sun, 01 Mar 2026 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Pluribus & Alienation]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<div class="intro">
<p>
This post contains spoilers for Apple <span class="small-caps">tv</span>&rsquo;s <i>Pluribus</i>, a really great
show that you should ideally watch without having read anything about
it.
</p>

</div>

<p class="dcap">
<i>Pluribus</i> — the newest show by Vince Gilligan (of <i>Breaking Bad</i> fame) —
is a show that is refreshingly new in its ideas. After finishing it I
discussed its themes with some of my friends and acquaintances, almost
all of whom had quickly connected the show with artificial
intelligence (<span class="small-caps">ai</span>). But for me, continuing on an <a href="https://joarvarndt.se/exp">earlier thought</a> that
<span class="small-caps">ai</span> is not extraordinary, it was much more obvious that <i>Pluribus</i> is
about &ldquo;the internet&rdquo; more-so than <span class="small-caps">ai</span>.
</p>

<p>
The obvious reason that one might connect the &ldquo;joining&rdquo; in episode one
with <span class="small-caps">ai</span> is that it leads very quickly<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> to what in practice becomes a
sort of post-scarcity economy, at least for those 13 individuals who
remain. But importantly this takeover is not a result of rapid gains
in intelligence, but instead in <i>communication</i>. That each individual
appears much smarter is a consequence of at all times having 7.1
billion people, some of whom are experts on the relevant topic, as
instantly available sources. The large systems of coöperation and
organisation that are displayed throughout the show<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup> could not be
done by mere intelligence alone, but instead it requires fast and
seamless communication and delegation of needs and tasks respectively.
</p>

<p>
The technology that humanity has created for this task is commonly
referred to as &ldquo;the internet&rdquo;, although it is not a single cohesive
thing. The increasing connectivity of the internet means that more and
more things can be utilised by this collective whole — every computer
can in theory connect to every other computer. And because each
individual today usually possesses a device capable of connecting to
the internet (at least in the developed world) we can in turn
communicate our wishes and opinions to (theoretically) every other
internet-connected person in the world.
</p>

<p>
Throughout the first season (but in the following cases in the second
episode) <i>they</i><sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup> will often cite the opinions of individual members of
the collective, such as the bottling-plant worker who bottled a
specific bottle of water, or of medical doctors throughout the
world. That this is the case makes me think that the thoughts of the
individual (as in activity in the mind) are still present, it is
simply the case that the information transfer is so enormous that a
chain of reasoning can be diverted and spread across the minds of all
the people of mankind. What is individuality if all your thoughts and
memories can be moved to <a href="https://en.wikipedia.org/wiki/Ship_of_Theseus">another body</a>?
</p>

<p>
While the degree to which this happens in <i>Pluribus</i> is of course
impossible as of this time it is still occurring at a smaller scale
today. When we share our opinions, and critically read those of
others, we slowly construct a homogeneous state where all opinions and
thoughts are the same and that serves as some kind of collective
hive-mind. The widespread idea of &ldquo;filter bubbles&rdquo; is many ways false,
we are much more filtered through our physical surroundings than our digital ones.<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup>
</p>

<p>
<i>They</i> continually insist on the greatness of the joined
experience, and on how each member of mankind saw it as a wonderful
thing — just as we ourselves insist on how wonderful it is to be a
part of <a href="https://etymology.substack.com/p/stay-on-your-phone">digital society</a>.
</p>

<p>
And in fact it is hard to argue with the benefits. As Koumba points out,
</p>

<blockquote>
<p>
As we speak no-one is being robbed or murdered. No one is in
prison. The colour of one&rsquo;s skin, by all accounts, now
meaningless. All zoos are empty. All dogs are off their chains. Peace
on Earth!
</p>
</blockquote>

<p>
This is in all regards an improvement in the state of things. It is in
fact the idealised state of, dare I say it, most of mankind. What is
the point in undoing the joining, as is the goal of Carol and
Manousos, when continuing in the same trajectory inevitably leads to
the same outcome? But Carol&rsquo;s response to Koumba — &ldquo;What has been
lost?&rdquo; — is equally valid, for it is clear that there is something in
the human experience that is missing, both for the individuals and for
the collective.
</p>

<p>
Hannah Arendt, in her book <i>The Human Condition</i> divides human activity into three different forms:
</p>

<ol class="org-ol">
<li>Labour</li>
<li>Work</li>
<li>Action</li>
</ol>

<p>
These three activities all have their own corresponding sphere — the
private, the social, and the political respectively. What the joining
destroys is both the social and political realms, and with them the
two higher forms of human activity, work and political action. The
Earth is left with only 14 distinct individuals, only one of which is
really relevant for any economic activity. Thus there is no
requirement for social interaction for any sort of production; when
<i>they</i> leave Albuquerque Carol is perfectly able to go about her life,
but she is left entirely alone — not even able to shift her life into
the digital as the world did in 2020.
</p>

<p>
One of the most impactful scenes for me was strangely the almost
silent scene when Zosia removes a body from a crashed car, helping
place it in a truck before continuing on. It struck me in its
simplicity — there is no genuine activity left, only cleaning up the
entropy of the universe.
</p>

<p>
Performing work — in order to create <i>a work</i> — becomes useless once
this final position has been reached; there will always be someone who
is better and irregardless the extreme division of labour means that
all work becomes eliminated, leaving the individual as a mere unit of
<i>labour power</i>. This leads to Marx&rsquo; (and that Arendt adopts) description
of the worker as alienated from their labour, incapable of seeing the
whole production of an object through and only relevant in the
production of a small part.
</p>

<p>
When the social and political disappears we are left alone. Alienated
no just from or labour but also from each other. The individuals in
<i>Pluribus</i> are separated from the rest of humanity, who all have one
single shared experience that they can not communicate with Carol and
the others. The only way to understand <i>them</i> is to join them, to become
a part of this information flow and learn. But in doing so one
naturally loses one&rsquo;s identity and individuality, because the act of
understanding the other necessarily brings you closer to them<sup><a id="fnr.5" class="footref" href="#fn.5" role="doc-backlink">5</a></sup> —
just as how the internet makes you closer to some form of a global
digital culture.
</p>

<p>
Is there a solution to all this? That is the question that is posed by
<i>Pluribus</i>; What do we do when there is nothing more to be done as
individuals? Does our individuality have any innate meaning and worth
giving up social connections for, or should we trade it for the
material rewards that come with social organisation? The answer may
simply be that individuality was always meaningless — as <i>they</i> argue —
and vastly eclipsed by the satisfaction of universal understanding. ❦
</p>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Akin to a hypothesised fast <span class="small-caps">ai</span>-takeover.
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Most notably perhaps in the restocking of the <i>Sprouts</i> in episode
3, but also in impressive things such as the transportation and
preparation of Zosia in episode 2.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
&ldquo;They&rdquo; are in this case the collective whole of mankind, except
for the 13 (later 12) who still possessed their individuality.
</p></div></div>

<div class="footdef"><sup><a id="fn.4" class="footnum" href="#fnr.4" role="doc-backlink">4</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Our families most likely have similar political views, incomes,
languages, favourite films, favourite sports teams <i>et cetera</i> than most
of the people you interact with online. Being able to be mad at a
Telugu nationalist on a micro-blogging service is sign of more
interconnectedness, not less.
</p></div></div>

<div class="footdef"><sup><a id="fn.5" class="footnum" href="#fnr.5" role="doc-backlink">5</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
The common saying that you are &ldquo;the average of the five people you
spend the most time with&rdquo; reflects this thinking, but it is also true
in the reverse. Spending time with someone makes them more like <i>you</i>,
and in this sense interacting with the internet makes its contents a
little bit more like you.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[culture]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/pluribus.html</link>
  <guid>https://joarvarndt.se/pluribus.html</guid>
  <pubDate>Wed, 18 Feb 2026 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Handwritten notes in Emacs using Xournal++]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<hr>

<p class="dcap">
My girlfriend recently bought a <a href="https://frame.work/se/en/laptop12">Framework 12</a> laptop<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> with a
correspoding stylus and, influenced by my ravings about the power of
<span class="small-caps">gnu</span> Emacs, wants to take handwritten digital notes on it for her
engineering and mathematics classes and then organise them in
<a href="https://orgmode.org/">org-mode</a>. I see an earlier version of myself clearly in her, and of
course want to help her attain the vision she has for her own Emacs
system. At the same time I am a strong believer in that you should not
try and <i>learn</i> Emacs, but that it should be molded to your own personal
desires. For that reason I try to act in merely an advisory capacity,
while helping whenever she asks.
</p>

<p>
At first we discovered an old package named <a href="https://gitlab.com/vherrmann/org-xournalpp">org-xournalpp</a> that seems
to fit perfectly. It creates xournal++-specific links that opens the
corresponding xournal file whenever clicked, and they are rendered
like inline images in the org-mode buffer. We had some trouble getting
Emacs to load the file after installing with the new built-in <code>:vc</code>
keyword for <code>use-package</code> (it worked fine on my machine, but not hers)
but after installing it the inline previews would not be
displayed. Worse yet, running the included <code>org-xournalpp-mode</code> (that
tries to render the images) would cause Emacs to freeze.
</p>

<p>
But how hard can it be to implement your own version of this
functionality? Org-mode already has the ability to display inline
images (as I am <a href="https://joarvarndt.se/notes">well familiar with</a>) and xournal++ can export to
both <code>.png</code> and <code>.svg</code> formats. For this we wrote a small function that
uses <code>xournalpp</code> to export a given file to png format and then inserts a
link to it in the buffer.
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">create-png-xournal-file</span> (<span class="org-type">&amp;optional</span> only-export)
  <span class="org-doc">"Creates a png from a xournal file and inserts it into the buffer."</span>
    (<span class="org-keyword">interactive</span> <span class="org-string">"P"</span>)
    (<span class="org-keyword">let</span> ((file-name (expand-file-name
                      (file-name-sans-extension
                       (read-file-name <span class="org-string">".xopp file to convert: "</span>)))))
      (shell-command (format <span class="org-string">"xournalpp --create-img=%s.png %s.xopp"</span>
                             file-name file-name))
      (<span class="org-keyword">unless</span> only-export
       (kill-new (concat <span class="org-string">"[["</span> file-name <span class="org-string">".png"</span> <span class="org-string">"]]"</span>))
       (yank)
       (org-display-inline-images))
      (org-redisplay-inline-images)))

  (<span class="org-keyword">bind-key</span> <span class="org-string">"C-&#246;"</span> 'create-png-xournal-file)
</code></pre>
</div>

<p>
If <code>org-startup-with-inline-images</code> is <code>t</code> this is then immediately
rendered as an image in the buffer. If called with a prefix argument
(usually <code>C-u</code>) this skips the insertion part and calls
<code>org-redisplay-inline-images</code> to get an updated version.
</p>

<p>
The experience of imagining functionality that you wish was possible
and in an afternoon creating a program (even one as small as a single
11-loc function) that fulfills your requirements is something that you
rarely get outside of Emacs. Even in other free software, few programs
open themselves so fully to introspection and modification. Something
as simple as the <code>*scratch*</code>-buffer, with its use as a quasi-persistent
<span class="small-caps">repl</span>, invites creative solutions and problem solving — not to mention
the self-documenting capabilities of <code>C-h</code> and docstrings.
</p>

<p>
While I am not one of those users insistent on reducing the number of
packages installed<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>, there is a certain perspective that you should
be emphasised; You can easily make do with home-grown functionality
that builds on packages you already have installed, or the rich
functionality that already comes with Emacs. ❦
</p>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
For the short period she&rsquo;s had it now it seems like a great
product, but I still prefer the philosophy of <a href="https://mntre.com/index.html"><span class="small-caps">mnt</span> Research</a>. It is
however good enough that I do not have any issues with linking to it.
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
My current count as of writing this is 134. 
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[technology]]></category>
  <category><![CDATA[emacs]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/xournal-emacs.html</link>
  <guid>https://joarvarndt.se/xournal-emacs.html</guid>
  <pubDate>Mon, 15 Dec 2025 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Decentralized Personal Computing]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<hr>

<p class="dcap">
I have a few different computers. Those machines have different
capabilities, form factors, portability, hardware, and software — and
yet I still want to do roughly the same work on them. While most of
the work I do is with text — reading, writing, editing, programming —
Sometimes I want to work on one specific machine because I might need
a dedicated graphics card or I need the machine to fit in my
pocket. The solution to this is to have work sync automatically
between my machines.
</p>

<p>
This is not a unique problem to have — some common solutions include
Microsoft Windows&rsquo; <i>Active directory</i> or doing all your work through
cloud-based offerings like Google&rsquo;s <i>Drive</i> or Microsoft&rsquo;s
<i>Sharepoint</i>. There are however a few issues with these solutions. They
are almost by definition insecure since they entail sending my data to
a third party&rsquo;s computer<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>. Services like <a href="https://proton.me/drive">Proton drive</a> or <a href="https://koofr.eu/">Koofr</a> at
least do the bare minimum of encrypting your files so that only you
can access them, but they still keep you reliant on someone else&rsquo;s
infrastructure, and you have to be able to connect to their services
in order to access <i>your</i> data.
</p>

<p>
A better workflow would be (to be able) to work entirely offline and
then sync those changes semi-periodically when I am online again. I do
not want to have to think about if a machine is connected to the
internet or what work I have done on it earlier (or not done). I
simply want to pick up (or sit down at) a computer and keep going with
whatever I was doing before — all without relying on a centralized
host. This of course requires all machines to have their own copy of
the data, and for those machines to sync changes to that data between
themselves.
</p>

<p>
This syncing functionality can be offered through a lot of different
services or systems. I personally use <a href="https://syncthing.net">Syncthing</a> for this, but you
could just as well use <a href="https://en.wikipedia.org/wiki/Rsync">rsync</a> paired with <a href="https://en.wikipedia.org/wiki/Cron">cron</a> jobs to sync your files
securely over <code>ssh</code>. Syncthing is cross platform (so I can use it on
android, <span class="small-caps">gnu</span>/Linux, and theoretically Microsoft Windows) and doesn&rsquo;t
rely on port forwarding (and so it doesn&rsquo;t matter what network I am
connected to).
</p>

<p>
If you want to use <code>rsync</code> and sync to your phone I would probably use
something like <a href="https://tailscale.com">tailscale</a> paired with <a href="https://termux.dev/en/">termux</a> to run your cron
jobs. That way you get a single ip address to hardcode into your <code>rsync</code>
scripts and you can move across networks and use your phone&rsquo;s cellular
connection. I actually use tailscale myself (even though it is not
free software) to speed up discovery between devices.
</p>

<p>
Tailscale also allows me to <code>ssh</code> quickly between machines to transfer
files that aren&rsquo;t in my general work-related folders. This is usually
stuff that <i>allows</i> me to perform my work (like dotfiles or other
configuration). It pairs wonderfully with <span class="small-caps">gnu</span> Emacs&rsquo; <a href="https://www.gnu.org/software/tramp/"><span class="small-caps">tramp</span></a> since
machines ssh-ing over tailscale don&rsquo;t need any further authentication
(they are authenticated through the tailnet) and it also means I don&rsquo;t
have to expose port 22 to the open internet.
</p>

<p>
I have five different machines that each keep a complete copy of all
of my critical files:
</p>

<ol class="org-ol">
<li><i>Parana</i>.
This is my server. It does not hold any centralised role in my
network, but is configured to serve content to the open web and
maintain high uptime. Runs Debian stable.</li>
<li><i>Nile</i>.
My desktop computer. Like Parana this is located at my home. This
is a more high-powered device meant for user-facing
interaction. Runs Arch linux.</li>
<li><i>Yukon</i>.
A larger, higher performance laptop that acts as a portable
workstation when I am away from home for longer periods of
time. Runs Debian testing.</li>
<li><i>Danube</i>.
A small, durable, and low-power<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup> laptop that I can easily toss
into my bag. This is probably one of my most used devices because
it has a nice keyboard and long battery life. Runs <span class="small-caps">gnu</span> Guix.</li>
<li><i>Yangtze</i>.
My phone. In this context it mostly acts as a music player and a
way for me to access my grocery list. Runs lineage<span class="small-caps">os</span>.</li>
</ol>

<p>
One of the biggest advantages to this approach is that not only is the
general state of the files kept in sync, but I have five different
machines located in different places that all have copies of my
data. This means I can maintain the integrity of my data even if I
lose my phone (1/5), my laptop is stolen (1/5), or my home burns down
when I am away (usually 3/5). I can also wipe my computers — something
I do semi-regularly — without worrying about losing anything of
importance on them. My desktop and server both have high uptime, so
there is always some node on the network that is mirroring
changes. Critically I am still able to automatically sync files
between my phone and laptop when my home network is offline<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>.
</p>

<p>
It is however inevitable that conflicts occur when syncing files
between machines that are not always online. Thankfully there is a
tried-and-tested program that fits with this offline workflow on a
distributed number of machines; <a href="https://git-scm.com/">git</a>.
</p>

<p>
Git is very useful not just as a collaborative development tool, but
also for singular users. Syncthing has an integrated way of dealing
with this by retaining copies of files and renaming them. This is
good, but it is still an annoying hassle to deal with. Using git is a
lot nicer (partly because I can use <a href="https://magit.vc">magit</a> to resolve problems) but git
also allows me to see the history of the edits that I have made. Most
of my git repositories are not kept on any external server like
<a href="https://github.com">github&rsquo;s</a> or <a href="https://codeberg.org">codeberg&rsquo;s</a>, but instead only exist in this pseudo-<span class="small-caps">lan</span>/<span class="small-caps">vpn</span>
that my devices all interact through.
</p>

<p>
All my machines (yes, even my phone) also posses its own copy of my
configuration files for <span class="small-caps">gnu</span> Emacs, and so the way that I interact with
my computers is always tailor-made for my preferences and
workflows. Because of Emacs I work almost exclusively with files in
plaintext (usually formatted using <a href="https://karl-voit.at/2021/11/27/orgdown/">orgdown</a>). Working with plaintext of
course makes using git very straightforward, but I still use git for
non-plaintext tasks.
</p>

<p>
Git is harder to use in non-plaintext contexts, but in my experience
most binary files do not change often enough to experience many
conflicts. That might differ for you however, and you might
require some other custom solution.
</p>

<p>
The end result of this system is that each machine becomes an
old-school terminal that interacts with a broader computing system,
although one that does not require a centralised server or even a
network connection to operate! While all my machines possess complete
records of the data and are fully empowered to make arbitrary changes
(I am the only user after all) no single computer encompasses the
whole of the computing environment. I can always discard one element
of the network for another<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup>.
</p>

<p>
This approach not only maintains your own sovereignty over your data,
it doesn&rsquo;t cost anything and also allows you to make use of your
preëxisting storage for a continuos backup solution. It doesn&rsquo;t stop
you from accidentally wiping a given file from all of your machines of
course, so adding some sort of air-gapped backup is also necessary.
</p>

<p>
The modern day computer is increasingly less personal. Smartphones
today act more like network terminals than real computing platforms
(even though they are many times more powerful than old
supercomputers). Building a decentralized system in this fashion is
one way of maintaining a computer that is still intimately personal
while still offering the advantages of network storage and automatic
backups. It allows you to make your own choice about what software to
use and how to use that software, and is therefore free in the true
essence of free software — and of the enlightenment idea of freedom it
is based on. ❦
</p>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Also known as &ldquo;the cloud&rdquo;. Sending your data to another person
requires you trusting them to never get cracked or do anything
malicious with your data themselves.
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
When I say low-power I mean it, it only has 4Gb of <span class="small-caps">ram</span> and 128Gb
of storage. I could probably get a machine of similar performance
second hand for free.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
My home internet connection is very unreliable, and suffers from
intermittent spots of downtime.
</p></div></div>

<div class="footdef"><sup><a id="fn.4" class="footnum" href="#fnr.4" role="doc-backlink">4</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Moving between machines that are all connected to the network is
almost seamless and instantaneous, but even setting up a new computer
(after purchasing it or wiping the disk) takes only perhaps an
hour. After that using the machine is once again as simple as using
any other perfectly tailor-made computing device that I own.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[technology]]></category>
  <category><![CDATA[emacs]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/decentralized-machines.html</link>
  <guid>https://joarvarndt.se/decentralized-machines.html</guid>
  <pubDate>Fri, 14 Nov 2025 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Don't Prioritise What to Learn]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<p class="dcap">
I have recently read not just one, but two different blog posts
arguing for roughly the same thing. To summarise them; You have a
finite amount of time to learn new things, and so you should not only
focus that time toward learning things that actually matter, but you
should also only try and learn things that are <i>easy to learn</i>. I
fundamentally disagree with both of these two things, but think they
are prevalent in &ldquo;productivity&rdquo; sphere, especially in those targeting
people at the intersection between policy and technology, where I
happen to find myself right now.
</p>
<div id="outline-container-what-not-to-learn" class="outline-2">
<h2 id="what-not-to-learn">What Not to Learn</h2>
<div class="outline-text-2" id="text-what-not-to-learn">
<p>
The first post I&rsquo;d like to critique is <i><a href="https://dylanfitzgerald.net/blog/decide-what-not-to-learn/">Decide what not to learn</a></i> by
Dylan Fitzgerald, a self-titled &ldquo;voracious learner/metalearner&rdquo;. He
opens with an unsourced quote that goes &ldquo;You can learn anything, but
you can’t learn everything&rdquo;. This is of course undeniable, but I
struggle to see what relevance it actually has. In return I&rsquo;d like to
offer my own quote, but Mahatma Gandhi:
</p>

<blockquote>
<p>
Live as if you were to die tomorrow. Learn as if you were to live forever.
</p>
</blockquote>

<p>
Learning <i>everything</i> is impossible, but it is only in striving toward
that unattainable goal that one can come close to it. Learning more is
in-and-of-itself a worthy goal. He then goes on to talk about how
&ldquo;career-adjacent&rdquo; learning can be valuable, but also &ldquo;bottomless and
neverending&rdquo;. Once again, a trivially true statement phrased as a
problem without any real motivation. His claim of this being
especially true for technology workers is even more absurd:
</p>

<blockquote>
<p>
there’s every day a new language, framework, process, or entire field
of study promising to make the life of you and those around you
somewhere from better to utterly transformed
</p>
</blockquote>

<p>
The strange thing about this claim is that for anyone who has done any
serious (or even amateur) programming will realise that the more
languages<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> one learns, the easier it is to learn another one. Ideas
don&rsquo;t just come up out of nowhere, requiring us to build entirely new
mental system to accommodate them. Instead they are based on other
preëxisting things that we can use to &ldquo;anchor&rdquo; these new ideas to. The
more one learns, the easier learning becomes. Getting a
&ldquo;Wikipedia-depth summary of each topic&rdquo; is not a problem, as that
knowledge allows one to create novel new pathways to new knowledge.
</p>

<p>
Fitzgerald proposes this as his solution to this &ldquo;problem&rdquo;:
</p>

<blockquote>
<p>
If you, like myself, struggle with this, a tactic: write down all the
things you want to learn, are in the middle of learning, haven’t quite
had time for, know you really would benefit from…you get the
idea. Pick the top one. The rest of the list–especially #2 and #3!–is
the stuff you absolutely must avoid at all costs. That’s not your
“next” list. That’s your “<span class="small-caps">danger</span>” list.
</p>
</blockquote>

<p>
This is anathema to me. Fitzgerald&rsquo;s ideas may be rooted in some sort
of <a href="https://joarvarndt.se/ellul.html">technical</a> thinking — where the mind is some sort of robot; studying
continuously before moving on to the next thing — but this is not how
learning works (at least not in my experience). Learning is instead a
fluid, moving process, where one moves from topic to topic as their
interest waxes and wanes. We create the aforementioned pathways by
learning new things that support each other. Knowledge is like a brick
wall; much stronger if standing in a lattice of other bricks than if
placed on top of each other one after another.
</p>
</div>
</div>
<div id="outline-container-what-to-learn" class="outline-2">
<h2 id="what-to-learn">What to Learn</h2>
<div class="outline-text-2" id="text-what-to-learn">
<p>
The second post is Neel Nanda&rsquo;s <i><a href="https://www.neelnanda.io/blog/34-learning">Post 34: Learning how to learn</a></i>. I
agree much more with this post that the former one, but still think it
falls into the same sort of thinking about learning as
Fitzgerland&rsquo;s. This is perhaps surprising, as the two are really
diametrically opposed in their advice. Nanda&rsquo;s post mostly focuses on
<i>how to learn</i> through teaching, introspection, and spaced repetition,
but it also does have a section on what he calls the <i>80/20 Rule</i>, a
personal spin on the <a href="https://en.wikipedia.org/wiki/Pareto_principle">Pareto principle</a><sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>. In it he says that you
should find out how to get &ldquo;80% of the value from 20% of the effort&rdquo;,
and then do that. He expands on this idea in a post titled <i><a href="https://www.neelnanda.io/blog/mini-blog-post-22-the-8020-rule">The world
is full of wasted motion</a></i>.
</p>

<p>
In summary, one should try and <i>reduce</i> (in both the literal sense and
in the sense of cooking) ideas and try and obtain a &ldquo;big picture&rdquo;
view, to see what really matters. This reminds me a lot of advice that
one should &ldquo;go back to first principles&rdquo;, axiomatic ideas that all
other assumptions are built upon. Learning these is most important,
and so learning the axiomatic basics and &ldquo;key ideas&rdquo; (the <i>reduced</i>
knowledge) in a given field is what is most valuable. I agree with all
of this in principle, but in practice I think it falls apart.
</p>

<p>
Stepping back and looking at the &ldquo;big picture&rdquo; is a common technique
for trying to distance yourself from one&rsquo;s obsession with whatever is
in front of them, and to re-prioritise toward what is really
important. For this purpose it is very useful; but as a general
attitude for learning I find it almost as harmful as Fitzgerald&rsquo;s
suggestion.
</p>

<p>
The main obstacle to long-term learning, in my experience, is almost
never time or &ldquo;wasting&rdquo; it on learning the wrong thing. Instead, it is
ambition and interest. This may very well be a natural instinct
guarding against what Nanda warns about — I usually have no interest
in learning about something that is not useful<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>.
</p>

<p>
Just as it is problematic to try and <i>avoid</i> merely getting
&ldquo;Wikipedia-depth [knowledge] of each topic&rdquo;, it is equally problematic
to aim for it. Learning should be an intuitive process that we are
prompted to naturally, and trying to &ldquo;choose&rdquo; what to learn
strategically will almost always be a mistake. What (and when
something) will be useful is almost impossible to predict in my
experience. One will always encounter newfound situations where one&rsquo;s
knowledge of an obscure topic will become relevant.
</p>

<p>
That learning should be fluid and intuitive does not however mean the
rejection of structured, deliberate practice. Techniques like <a href="https://en.wikipedia.org/wiki/Spaced_repetition">spaced
repetition</a> can significantly speed up long-term memorization and do
not need to be discarded. But <i>what</i> you choose to learn through such
things does not have to be equally rigid. Perhaps this is what modern
schooling gets wrong? It sees that intensive, rigid learning works,
but wrongfully applies that to the curriculum. Just because the
metaphors (fluid vs structured) are antipodes does not need to mean
the same in practice.
</p>

<p>
There are of course certain situations that in microcosm appear worth
focusing on. If you are a material sciences researcher, it might be
more important to read up on crystal structures than to study the work
of Shakespeare. But this is a consequence of our society&rsquo;s technical
view; a material science researcher should <i>only</i> be focusing on
material science — everything else is unnecessary. But in reality
humans are multifaceted individuals who not only may be interested in
multiple things, but they also interact with other people interested
completely different things. Having a broad base of knowledge, or an
unnecessarily deep knowledge of something niche, allows us to see new
perspectives, but they also deepen human connection and makes us able
to empathize with others. ❦
</p>
</div>
</div>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Or &ldquo;frameworks&rdquo;, ugh. 
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Both the Pareto principle and Nanda&rsquo;s rule are observations of
power laws, and are therefore roughly the same. The difference is
merely in their application, and so there is some merit to using the
same name. Nanda uses the word &ldquo;Pareto principle&rdquo; in <i>wasted motion</i>,
but the original principle is about land ownership, not labour.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
I remember having to perform derivations &ldquo;by hand&rdquo; for different
functions, or combinations of functions, in high school. This was of
course interesting, and interesting to have been taught, but it is not
something that I retain for very long once I learned to the much
simpler rules for different function categories. Similarly I have no
interest in memorising digits of \(\pi\) or \(e\) because I do not see the
practical application in it.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[life]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/learning-priorities.html</link>
  <guid>https://joarvarndt.se/learning-priorities.html</guid>
  <pubDate>Tue, 19 Aug 2025 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Common lisp and Github workflows]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<nav id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#dynamically-updating-github-profile">Dynamically updating Github profile</a></li>
<li><a href="#creating-the-readme">Creating the <span class="small-caps">readme</span></a></li>
<li><a href="#using-github-workflows">Using Github workflows</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</div>
</nav>
<div id="outline-container-dynamically-updating-github-profile" class="outline-2">
<h2 id="dynamically-updating-github-profile">Dynamically updating Github profile</h2>
<div class="outline-text-2" id="text-dynamically-updating-github-profile">

<figure>
<img src="./github-profile-banner.png" alt="github-profile-banner.png">

</figure>

<p class="dcap">
Github has a functionality where the <code>README</code> file in a repository with
the same name as your username will be displayed on your
profile. Since I am a fan of the <a href="https://indieweb.org/POSSE">indieweb&rsquo;s</a> <span class="small-caps">posse</span><sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> practice, I&rsquo;d
like to use that space to advertise the writings on this website.
</p>

<p>
Other than merely linking to it as a handle I can write a short
program that dynamically updates the profile to include what my most
recent posts are. This also gives me an opportunity to write another
program in <a href="https://lisp-lang.org/">lisp</a>, something that always brings me joy.
</p>
</div>
</div>
<div id="outline-container-creating-the-readme" class="outline-2">
<h2 id="creating-the-readme">Creating the <span class="small-caps">readme</span></h2>
<div class="outline-text-2" id="text-creating-the-readme">
<p>
First we must create the actual <code>README</code> file that will be
displayed. For this we create a short program in common lisp. The
result should be a file named <code>README.org</code><sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup> with a short biographical
text followed by the X most recent blog posts (I chose 3 arbitrarily).
</p>

<p>
First we fetch the <code>drakma</code> and <code>xmls</code> packages from quicklisp, the only
two dependencies. We also create global variables that hold the link
to the <span class="small-caps">rss</span> feed being fetched, the permanent contents of the <code>README</code>,
and the number of posts to display.
</p>

<div class="org-src-container">
<pre class="src src-lisp"><code>(ql:quickload '(<span class="org-builtin">:drakma</span> <span class="org-builtin">:xmls</span>))

(<span class="org-keyword">defvar</span> <span class="org-variable-name">*rss-url*</span> <span class="org-string">"https://joarvarndt.se/rss.xml"</span>)

(<span class="org-keyword">defvar</span> <span class="org-variable-name">*template*</span> <span class="org-string">"I am a university student currently studying political science
at the Swedish Defence University. I am interested in Lisp-languages, GNU Emacs,
Free software, political economy, and the philosophy of life in an
increasingly technical world.

Here are some recent blog posts of mine:
"</span>)

(<span class="org-keyword">defvar</span> <span class="org-variable-name">*article-count*</span> 3)
</code></pre>
</div>

<p>
Then we write a function that will find the titles of the three (or
any other number) most recent entries in the <span class="small-caps">url</span> of the <span class="small-caps">rss</span> feed
supplied. Each entry is formatted as a associated list (alist) with
the first element being the title of the post, and the second being
the link to it.
</p>

<div class="org-src-container">
<pre class="src src-lisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">fetch-recent-articles</span> (url <span class="org-type">&amp;optional</span> (count 3))
  <span class="org-doc">"Fetch the COUNT (default 3) most recent RSS feed entries from URL and return an
alist
mapping title to link for each article."</span>
  (<span class="org-keyword">let*</span> ((response (drakma:http-request url))
         (xml-string (octets-to-string response))
         doc channel items)
    (setf doc (xmls:parse xml-string))
    (setf channel (first (xmls:xmlrep-find-child-tags <span class="org-string">"channel"</span> doc)))
    (setf items (xmls:xmlrep-find-child-tags <span class="org-string">"item"</span> channel))
    (<span class="org-keyword">loop</span> for item in (subseq items 0 (min count (length items)))
          for title-node = (xmls:xmlrep-find-child-tag <span class="org-string">"title"</span> item)
          for link-node  = (xmls:xmlrep-find-child-tag <span class="org-string">"link"</span> item)
          for title = (xmls:xmlrep-string-child title-node)
          for link  = (xmls:xmlrep-string-child link-node)
          collect (cons title link))))
</code></pre>
</div>

<p>
The titles are then formatted to a bullet list of org-mode titles and
inserted after the contents of the template. The contents are then
written to the <code>README.org</code> file, creating it if it doesn&rsquo;t exist.
</p>

<div class="org-src-container">
<pre class="src src-lisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">format-org-links</span> (articles)
  <span class="org-doc">"Expects an alist of article titles and their links, and outputs
a list of strings formatted as org-mode links."</span>
  (mapcar (<span class="org-keyword">lambda</span> (pair)
            (<span class="org-keyword">let</span> ((title (car pair))
                  (link (cdr pair)))
              (format t <span class="org-string">"- [[~a][~a]]"</span> link title)
              (format nil <span class="org-string">"- [[~a][~a]]"</span> link title)))
          articles))

(<span class="org-keyword">defun</span> <span class="org-function-name">fill-in-template</span> (template links)
  <span class="org-doc">"Adds the list of LINKS to the end of TEMPLATE.
LINKS should be a list of org-mode link strings.
Returns the final string."</span>
  (format nil <span class="org-string">"~a~%~{~a~^~%~}"</span> template links))

(<span class="org-keyword">defun</span> <span class="org-function-name">write-to-file</span> (text)
  (<span class="org-keyword">with-open-file</span> (str <span class="org-string">"./README.org"</span>
                       <span class="org-builtin">:direction</span> <span class="org-builtin">:output</span>
                       <span class="org-builtin">:if-exists</span> <span class="org-builtin">:supersede</span>
                       <span class="org-builtin">:if-does-not-exist</span> <span class="org-builtin">:create</span>)
    (format str text)))
</code></pre>
</div>

<p>
Finally, this is all carried out in a <code>main</code> function, with the fetched
results being printed beforehand.
</p>

<div class="org-src-container">
<pre class="src src-lisp"><code>(<span class="org-keyword">defun</span> <span class="org-function-name">main</span> ()
  (<span class="org-keyword">let</span> ((recent-articles (fetch-recent-articles
                          *rss-url*
                          *article-count*)))
    (format t <span class="org-string">"Recent article list: ~% ~A ~%"</span> recent-articles)
    (write-to-file (fill-in-template *template*
                                     (format-org-links recent-articles)))))

(main)
</code></pre>
</div>
</div>
</div>
<div id="outline-container-using-github-workflows" class="outline-2">
<h2 id="using-github-workflows">Using Github workflows</h2>
<div class="outline-text-2" id="text-using-github-workflows">
<p>
This exercise also served to teach me to use Github&rsquo;s &ldquo;workflow&rdquo;
feature, a form of <a href="https://en.wikipedia.org/wiki/Continuous_deployment">continous deployment</a>. This way I also don&rsquo;t have to
maintain any hardware to constantly monitor the status of the blog
posts.
</p>

<p>
First we give github write permissions to the repo and tell it to run
the workflow twice a day and whenever I push to main (this was useful
for troubleshooting).
</p>

<div class="org-src-container">
<pre class="src src-yaml"><code><span class="org-variable-name">name</span>: Update Github Blog list

<span class="org-constant">on</span>:
  <span class="org-variable-name">push</span>:
    <span class="org-variable-name">branches</span>: [main]
  <span class="org-variable-name">pull_request</span>:
    <span class="org-variable-name">branches</span>: [main]
  <span class="org-variable-name">schedule</span>:
    - <span class="org-variable-name">cron</span>: <span class="org-string">"0 */12 * * *"</span>

<span class="org-variable-name">permissions</span>:
  <span class="org-variable-name">contents</span>: write
</code></pre>
</div>

<p>
Since this will always be a fresh machine we need to make sure that
<span class="small-caps">sbcl</span> (Steel Bank Common Lisp) as well as the necessary dependencies
are installed. This will also install <a href="https://www.quicklisp.org/beta/">Quicklisp</a>, a package manager for
common lisp libraries. Quicklisp usually requires you to confirm that
it should add itself to the <span class="small-caps">sbcl</span> init file, but since this is an
automated workflow we have to wrap <code>(ql:add-to-init-file)</code> in the
<code>(ql-util:without-promting)</code> macro.
</p>

<p>
Installing <span class="small-caps">sbcl</span> accounts for the vast majority of the runtime, with
installing <code>drakma</code> and <code>xmls</code> as the two smaller steps. Actually running
<code>main.lisp</code> is almost instant.
</p>

<div class="org-src-container">
<pre class="src src-yaml"><code><span class="org-variable-name">jobs</span>:
  <span class="org-variable-name">run-app</span>:
    <span class="org-variable-name">runs-on</span>: ubuntu-latest
    <span class="org-variable-name">steps</span>:
      - <span class="org-variable-name">name</span>: Checkout repository
        <span class="org-variable-name">uses</span>: actions/checkout@v4

      - <span class="org-variable-name">name</span>: Install sbcl
        <span class="org-variable-name">run</span>: sudo apt-get update &amp;&amp; sudo apt-get install -y sbcl

      - <span class="org-variable-name">name</span>: Install Quicklisp
        <span class="org-variable-name">run</span>: |
          <span class="org-string">curl -O https://beta.quicklisp.org/quicklisp.lisp</span>
<span class="org-string">          sbcl --noinform --non-interactive \
               --load quicklisp.lisp \
               --eval '(quicklisp-quickstart:install)' \
               --eval '(ql-util:without-prompting (ql:add-to-init-file))' \
               --quit
               
</span>      - <span class="org-variable-name">name</span>: Install Dependencies
        <span class="org-variable-name">run</span>: |
          <span class="org-string">sbcl --noinform --non-interactive \</span>
<span class="org-string">               --eval '(ql:quickload "drakma")' \
               --eval '(ql:quickload "xmls")' \
               --quit</span>
</code></pre>
</div>

<p>
It then runs the common lisp application we wrote earlier and
configures git. If <code>git diff</code> doesn&rsquo;t detect any changes to the <code>README</code>
it will finish successfully, otherwise the workflow will itself update
the repository and push the changes to Github, displaying the changes
publicly.
</p>

<div class="org-src-container">
<pre class="src src-yaml"><code>      - <span class="org-variable-name">name</span>: Update README
        <span class="org-variable-name">run</span>: |
          <span class="org-string">sbcl --load ~/.sbclrc --script main.lisp</span>
<span class="org-string">          git config --local user.name "github-actions"
          git config --local user.email "github-actions@github.com"
</span>
      - <span class="org-variable-name">name</span>: Check for changes
        <span class="org-variable-name">id</span>: check_changes
        <span class="org-variable-name">run</span>: |
          <span class="org-string">git diff --quiet || echo "changed=true" &gt;&gt; $GITHUB_OUTPUT</span>
<span class="org-string">          
</span>      - <span class="org-variable-name">name</span>: Commit changes
        <span class="org-variable-name">if</span>: steps.check_changes.outputs.changed == <span class="org-string">'true'</span>
        <span class="org-variable-name">run</span>: |
          <span class="org-string">git add README.org</span>
<span class="org-string">          git commit -m "Update README with latest blogs"
          git push
</span>        <span class="org-variable-name">env</span>:
          <span class="org-variable-name">GITHUB_TOKEN</span>: ${{ secrets.GITHUB_TOKEN }}
</code></pre>
</div>
</div>
</div>
<div id="outline-container-conclusion" class="outline-2">
<h2 id="conclusion">Conclusion</h2>
<div class="outline-text-2" id="text-conclusion">
<p>
I&rsquo;ve tried to make the code portable, so if you want to do the same
thing all you have to do is put this code in your own repository,
change the values of the global variables in <code>main.lisp</code> and it should
work. ❦
</p>
</div>
</div>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Publish (on your) Own Site, Syndicate Elsewhere 
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
I of course prefer to use <a href="https://orgmode.org/">org mode</a> over markdown here, just to subtly
showcase my love for it publicly.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[technology]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/cl-workflows.html</link>
  <guid>https://joarvarndt.se/cl-workflows.html</guid>
  <pubDate>Sun, 03 Aug 2025 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[学中文]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<nav id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#why-chinese">Why Chinese?</a></li>
<li><a href="#how-to-study">How to Study</a></li>
<li><a href="#how-i-studied">How I Studied</a></li>
<li><a href="#thoughts-on-the-language">Thoughts on the Language</a></li>
<li><a href="#the-future">The Future</a></li>
</ul>
</div>
</nav>
<div class="intro">
<p>
This is part of a series of posts written during or shortly after my
visits to the mainland of the People&rsquo;s Republic of China in the summer
of 2025.
</p>

</div>
<div id="outline-container-why-chinese" class="outline-2">
<h2 id="why-chinese">Why Chinese?</h2>
<div class="outline-text-2" id="text-why-chinese">
<p class="dcap">
There are a multitude of reasons why I wanted to study Chinese. Being
the language of the &ldquo;workshop of the world&rdquo; naturally offers many
business opportunities, but I am more interested in the burgeoning
fields of Integrated circuit (<span class="small-caps">ic</span>), <span class="small-caps">r&amp;d</span>, and software
development. <a href="https://planet.emacslife.com">Planet Emacslife</a> allows me to keep tabs on the goings-on
in the Emacssphere, but I also want to be able to read Emacs China; so
I figured it might be a good idea to be able to read Chinese.
</p>

<p>
Right now — after 1-2 months of quite intensive study — I am roughly
at an <span class="small-caps">hsk</span> 2-3 level in writing and reading, however my listening and
speaking skills are far worse. I can read slightly over 70% of average
Chinese text.
</p>
</div>
</div>
<div id="outline-container-how-to-study" class="outline-2">
<h2 id="how-to-study">How to Study</h2>
<div class="outline-text-2" id="text-how-to-study">
<p>
I&rsquo;ve been interested in learning Chinese for a few years now, but the
greatest mental barrier was understanding how to go about actually
<i>learning</i>. If I were to try and learn Russian I would naturally begin
with quickly learning to read Cyrillic, but how should you learn
Chinese? Should one try and master speaking Chinese and reading
romanisations first, before memorising thousands of characters, or the
other way around?
</p>

<p>
Really, neither is possible without the others. In general I have
focused more on the written language than the spoken one. This is
because Chinese is intimately connected with the written form, more so
than the other Germanic languages I know<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>. Not only do many words
sound very similar (only differentiated by <i>tone</i>) but there is a very
large number of homonyms — words that sound exactly the same. When
only romanised text is available, especially when written without
tonal markers, it is often impossible to tell what is written.
</p>

<p>
This means that knowing written characters, and their corresponding
pronunciation, is critical to understanding spoken Chinese. Many times
there is a lot of split-second guesswork involved, with some
characters having many possible meanings beyond just having to
remember what character is being said.
</p>
</div>
</div>
<div id="outline-container-how-i-studied" class="outline-2">
<h2 id="how-i-studied">How I Studied</h2>
<div class="outline-text-2" id="text-how-i-studied">
<p>
I decided to jump in the deep end immediately by <a href="https://joarvarndt.se/chinese_phenomenolgy.html">travelling to China</a>
with practically no knowledge beforehand. In hindsight this was
perhaps not the best idea, I spend much of the time learning the very
basics, but it was certainly fun. Formally I was enrolled in a
&ldquo;Chinese for beginners&rdquo; online course at <a href="https://www.uu.se/utbildning/kurs?query=5KN666">Uppsala University</a> but I did
most of my studies outside of the confines of the course. It was
however very useful as a way to motivate myself to actually study when
abroad.
</p>

<p>
Most of my study was done through <a href="https://www.hanlyapp.com/">Hanly</a>, a Chinese-learning app that
pedagogically introduces new characters by showing how they are
composed of other ones. To give an example: Early on in my studies I
looked up how to write numbers in Chinese, and was pleasantly
surprised at the numbers 1-3 (一, 二, 三) but equally afraid of the
character for 0 (零) with its 13 strokes. But when I eventually
learned it through Hanly it was in fact quite an easy character to
learn, being composed of the character for rain (雨) and for order (令).
</p>

<p>
Hanly uses flashcards, an established technique for memorisation,
paired with preprepared mnemonics and <span class="small-caps">ai</span>-generated artwork. These
range from quite sensible (three <i>people</i> 人 are a <i>crowd</i> 众) to absurd
but surprisingly easy to learn (A racecar driver using their <i>tongue</i> 舌
to check if a road is <i>suitable</i> 适). Hanly also teaches <i>words</i> —
combinations of characters — that both increase understanding and help
ground characters in actual use. These and the characters themselves
are taught in the same way, each with their own flashcard.
</p>

<p>
I try and learn 20-30 new cards (either characters or words) each day,
and aim to do a <i>minimum</i> of 100 repetitions of old cards daily. While
in the <span class="small-caps">prc</span> I would often do 200+ repetitions and sometimes even 300,
especially on long subway rides. I now know roughly 600 characters and
300 words by heart, and can therefore read slightly over 75% of
&ldquo;average text&rdquo; according to Hanly. Had I learned just the 600 most
common characters I would have understood 80% of average text, but
learning those 600 characters would have been a lot more difficult.
</p>

<p>
After learning new words I tried to quickly get them into my usable
vocabulary by employing them in daily life. This of course varied
quite a lot depending on what the words was — 戈 is quite difficult to
use in everyday life for example — but I think it helped anchor a lot
of words in my mind. Having a mental &ldquo;mission&rdquo; for each day makes
practical application a lot easier, even when the words are abstract.
</p>

<p>
I am still struggling quite a bit with tones, both in speaking and
listening, but that will hopefully improve with practise.
</p>
</div>
</div>
<div id="outline-container-thoughts-on-the-language" class="outline-2">
<h2 id="thoughts-on-the-language">Thoughts on the Language</h2>
<div class="outline-text-2" id="text-thoughts-on-the-language">
<p>
Chinese is quite fascinating as a language. It feels both familiar and
alien, with a comparatively simple grammar and Subject-verb-object
word order but also reliance on guesswork and rote memorisation.
</p>

<p>
My mental model of the language looks something like this:
</p>


<figure>
<img src="./ChineseVenn.svg" alt="ChineseVenn.svg" class="org-svg">

</figure>

<p>
Characters are initially grouped by pronunciation<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup> while not having
anything to do with each other, and then have their meaning narrowed
down by combining with other words. To emphasise the character as its
own word the sound is often repeated<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>, but it can also be combined
with another word with a similar meaning to create a new
synthesis. This sometimes works the same way as compound words do in
German, Swedish, or Norwegian where the two words are completely
different but combines into one word (such as 学院, college, literally
&ldquo;learning institution&rdquo;) but also with words carrying very similar
meanings (like with 打开, literally &ldquo;hit on&rdquo;, used similarly as &ldquo;slå
på&rdquo; in Swedish). If a character does not have any overlap with
adjacent characters in a sentence, it is its own word.
</p>

<p>
This seems like the long-term challenge with Chinese to me. Navigating
this three-dimensional space of guessing the meanings of words based
on their pronunciation, tone, and context rather than the quite simple
one-dimensional space in, for example, English (what does <i>that word</i>
mean?).
</p>

<p>
In general I sense a surprising similarity to <i>toki pona</i> in
Chinese. This is something of a horseshoe I guess; the easiest and
hardest<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup> languages to learn being similar. Both have very abstract
building blocks that represent something conceptually rather than
being fixed — with Chinese just having a few more.
</p>
</div>
</div>
<div id="outline-container-the-future" class="outline-2">
<h2 id="the-future">The Future</h2>
<div class="outline-text-2" id="text-the-future">
<p>
Hopefully I will be able to continue improving, although likely not at
the same rate as I was before. I am starting to feel that I should
immerse myself more in Chinese media and start reading texts in
Chinese, but the texts that I would really like to read are far too
advanced.
</p>

<p>
I would of course like to improve my speaking and listening skills,
but I think for the near-term I will continue focusing on reading
until I get to a level where I can get some practical use out of it. ❦
</p>
</div>
</div>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Swedish, Norwegian, English, and German. 
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
There exists a larger grouping of pronunciation <i>without</i> tone,
although it is not pictured in the diagram for clarity&rsquo;s sake.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
If the word is a noun you can also add 儿 or 子 for the same
effect; emphasising that what you&rsquo;re saying is a noun.
</p></div></div>

<div class="footdef"><sup><a id="fn.4" class="footnum" href="#fnr.4" role="doc-backlink">4</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Chinese is probably not actually the hardest language to learn for
a Germanic speaker — that might instead be some southern African click
(or Khoisan) language — but seen from an Everyman’s perspective it is.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[china]]></category>
  <category><![CDATA[culture]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/xuezhongwen.html</link>
  <guid>https://joarvarndt.se/xuezhongwen.html</guid>
  <pubDate>Sat, 26 Jul 2025 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Web browsing in Emacs]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<nav id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#why-would-you-want-web-browsing-in-a-text-editor">Why would you want web browsing in a text editor?</a></li>
<li><a href="#the-emacs-web-wowser-eww">The Emacs Web Wowser (eww)</a></li>
<li><a href="#w3m">w3m</a></li>
<li><a href="#non-text-based">Non-text based</a>
<ul>
<li><a href="#xwidgets">Xwidgets</a></li>
<li><a href="#eaf"><span class="small-caps">eaf</span></a></li>
</ul>
</li>
</ul>
</div>
</nav>
<div id="outline-container-why-would-you-want-web-browsing-in-a-text-editor" class="outline-2">
<h2 id="why-would-you-want-web-browsing-in-a-text-editor">Why would you want web browsing in a text editor?</h2>
<div class="outline-text-2" id="text-why-would-you-want-web-browsing-in-a-text-editor">
<p class="dcap">
Emacs has a tendency to try and absorb all user-facing interactions I
have with a computer. Some people seem able to separate &ldquo;Emacs stuff&rdquo;
— like programming or using org-mode for note taking — but Emacs&rsquo;
extendability makes me want to use it for everything. For a long time
I have only really needed four different tools to do any kind of work
at a computer:
</p>

<ol class="org-ol">
<li><span class="small-caps">gnu</span> Emacs (with my personal config).</li>
<li>Tailscale to connect to my other machines.</li>
<li>Syncthing so that I have local access to my files<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>.</li>
<li>A web browser (usually Firefox).</li>
</ol>

<p>
Quickly getting these four things up and running is usually my first
priority when setting up a new machine, and they require different
amounts of work. Critically Firefox requires a <span class="small-caps">gui</span> environment in
order to run, but it&rsquo;s really quite unnecessary to get a desktop
environment or tiling window manager up and running — and dealing with
their conflicting keybinds — when all I&rsquo;m going to be doing is working
in Emacs perhaps 80% of the time, finding information or reading docs
in Firefox 15% of the time, and using a stray terminal the remaining
5% (something I can easily replace with one of Emacs&rsquo; many terminal
and shell solutions). This is why I like using <code>exwm</code><sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>. Rather than
using Emacs&rsquo; window manager capabilities to manage all your X windows
it allows you to stay continually within Emacs and just treat any
stray non-Emacs <span class="small-caps">gui</span> programs within the same system. The solution then
seems to try and subsume web browsing into my preëxisting
workflow. There are many solutions to this, but I prefer ones that
work with <i>text</i> as the primary medium as that is what Emacs is best
oriented to work with.
</p>
</div>
</div>
<div id="outline-container-the-emacs-web-wowser-eww" class="outline-2">
<h2 id="the-emacs-web-wowser-eww">The Emacs Web Wowser (eww)</h2>
<div class="outline-text-2" id="text-the-emacs-web-wowser-eww">
<p>
<code>eww</code> is probably the most well-known of Emacs&rsquo; web browsing solutions,
chiefly because it comes included with it. It is also the one I have
the most experience with. I first came across <code>eww</code> as a consequence of
reading about all of the crazy features included in Emacs by default
like Tetris and Gnus. This isn&rsquo;t very surprising; web browsers are
some of the largest ubiquitous user-facing applications, so the fact
that Emacs ships with one as a tangential feature is very
surprising. However, these visions of browsing the web are often
quickly crushed when one uses <code>eww</code> for the first time. It does not load
any <span class="small-caps">css</span>, so websites do not look like one is accustomed to, and a lot
of javascript functionality is non-functional.
</p>

<p>
I expect this experience — or at least something like it — to be
somewhat universal amongst those who have even tried using <code>eww</code>. The
modern web is built around these technologies, and going without them
might seem almost impossible. But there is a place for <code>eww</code> in a modern
workflow. The strength of Emacs is its power for <i>working with text</i>,
and most media I interact with in a web browser <i>is</i> text. Emacs then is
really the perfect environment for this. Reading and rewriting the
content of web pages side-by-side using the same key-chords and
user-interface allows for minimal context friction. But there are
serious downsides, primarily ugly formatted headers and <i>images</i>. Most
of the time <code>eww</code> can figure out the main readable content using the
<code>eww-readable</code> function, bound to <code>R</code> by default. It&rsquo;s meant to strip out
long &ldquo;navigation menus and the like&rdquo;. In my experience it is either
hit or miss. For some web pages like <a href="https://en.wikipedia.org/wiki/French_Revolution">wikipedia</a> it seems to work well
(but only for some pages), but for others like <a href="https://github.com">github</a> it just strips
out the entire page. Thankfully it is bound to a convenient key and
its easy to switch between the two views quickly. Images are probably
one of the first things that stops people from using <code>eww</code>, with large
ones causing the site to jump around as they cover the whole screen
and then disappear. I solved this using <a href="https://github.com/jdtsmith/ultra-scroll">ultra-scroll</a>, which provides
scrolling smoother than you could ever imagine in Emacs. It also
allows you to scroll intuitively over images, rendering them as you
would expect in a traditional browser like Firefox. With a slow
network connection <code>eww</code> can have some problems with blocking, but this
can be remedied by setting <code>eww-retrieve-command</code>:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">setq</span> eww-retrieve-command
      '(<span class="org-string">"chromium"</span> <span class="org-string">"--headless"</span> <span class="org-string">"--dump-dom"</span>))
</code></pre>
</div>

<p>
<code>eww</code> is written by <a href="https://lars.ingebrigtsen.no">Lars Ingebrigtsen</a> of <code>gnus</code> fame. It uses the <code>shr</code>
(simple <span class="small-caps">html</span> Renderer) program written in elisp to render <span class="small-caps">html</span>. He
originally wrote <code>shr</code> to read <code>elfeed</code> entries in Emacs and <code>eww</code> is merely
the addition of browsing (or wowsing) capability built on top of
it. <code>shr</code>&rsquo;s elisp nature is why it can sometimes struggle with
complicated site layouts, but in my experience it is usually fast
enough, especially with native compilation.
</p>

<p>
Because you are in Emacs, and Emacs knows how to handle a large
variety of programming languages, you can even use <code>eww</code> to render code
snippets using your own theme&rsquo;s syntax highlighting. This is bot
shipped with Emacs by default, but can be supplied with the
<code>shr-tag-pre-highlight</code> package:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><code>(<span class="org-keyword">use-package</span> shr-tag-pre-highlight
  <span class="org-builtin">:defer</span> t
  <span class="org-builtin">:ensure</span> t
  <span class="org-builtin">:after</span> shr
  <span class="org-builtin">:commands</span> (shr-tag-pre-highlight)
  <span class="org-builtin">:init</span>
  (add-to-list 'shr-external-rendering-functions '(pre . shr-tag-pre-highlight)))
</code></pre>
</div>

<p>
Evaluate the above value and then navigate to this page to see it in
action. This configuration should also stop loading the package until
it is used in the rendering process and thus not impact startup time.
This shows how easy it is to configure the rendering of <code>shr</code> (and by
extension <code>eww</code>) to work in just the way you want it to, just as is the
case for Emacs.
</p>

<p>
Generally <code>eww</code> can be surprisingly powerful, but most of its power
(like Emacs itself) comes from its tight integration with other
tooling and extensibility with elisp, rather than any amazing workflow
it has built for you in advance. There is very little to learn, but a
lot to master.
</p>
</div>
</div>
<div id="outline-container-w3m" class="outline-2">
<h2 id="w3m">w3m</h2>
<div class="outline-text-2" id="text-w3m">
<p>
<code>w3m</code> can some sense be seen as the &ldquo;hardcore&rdquo; version of text-based web
browsing in Emacs. <code>w3m</code> relies on the eponymous <span class="small-caps">tui</span> program to render
web pages in a way similar to <i>Lynx</i>, allegedly the world&rsquo;s oldest
continually-maintained web browser. <code>w3m</code> even comes with lynx bindings
by default, although you can also change them to the included
&ldquo;info-like&rdquo; ones. It allows for tabs, bookmarks, and other features
one would expect in a modern browser. It also relies on interpreting
the raw <span class="small-caps">html</span> to format the page, rather than rendering <span class="small-caps">css</span>. Like <code>eww</code>
it also does not support javascript. Like I alluded to earlier, this
is both a blessing and a curse, and reading just straight text can be
very nice, both from a therapeutic minimalist way and a way to
minimize context-switching. It is also nice and async by default,
since it uses an external binary to fetch websites, not that it takes
very long to load to begin with since it&rsquo;s not running any heavy
javascript. One nice difference between <code>w3m</code> and <code>eww</code> is also that the
arrow keys navigate to the next link in the page by default, and the
text in the buffer will resize itself whenever the buffer is
resized<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>, something that is very handy if you&rsquo;re using a package
like <a href="https://github.com/roman/golden-ratio.el">golden-ratio</a> that resizes windows depending on focus.
</p>
</div>
</div>
<div id="outline-container-non-text-based" class="outline-2">
<h2 id="non-text-based">Non-text based</h2>
<div class="outline-text-2" id="text-non-text-based">
<p>
While I prefer text-based browsers that fit more seamlessly into the
rest of Emacs, there are other solutions for more traditional browser
experiences.
</p>
</div>
<div id="outline-container-xwidgets" class="outline-3">
<h3 id="xwidgets">Xwidgets</h3>
<div class="outline-text-3" id="text-xwidgets">
<p>
Since version 25 Emacs has had the ability to be compiled with
xwidgets support, and to render complete web pages through webkit. To
do this merely add the <code>--with-xwidgets</code> flag to Emacs when compiling,
and then run <code>M-x xwidget-webkit-browse-url</code> to surf the web! Sadly this
doesn&rsquo;t work very well at the moment, as you&rsquo;ll quickly realize if you
were to do the above. There is apparently a bug in the xwidget library
<code>webkitgtk</code> that causes Emacs to abort. Worse yet its status is <code>wontfix</code>
since <span class="small-caps">gnu</span> Emacs seems to be the only program affected by it thanks to
its old and strange ways of doing things. Upon crashing Emacs links to
a <i>22 year old</i> <a href="https://gitlab.gnome.org/GNOME/gtk/-/issues/221">bug
report</a> that had its last message sent five years ago, so I&rsquo;m not
exactly holding out hope (but maybe a good project to work on for
someone?). It seems to work with version 2.40 of <code>webkitgtk</code> so try
and use that if you&rsquo;re having issues. Sadly I have not gotten it to
work but I keep seeing people mentioning xwidgets as an option so it
might work on some machines.
</p>
</div>
</div>
<div id="outline-container-eaf" class="outline-3">
<h3 id="eaf"><span class="small-caps">eaf</span></h3>
<div class="outline-text-3" id="text-eaf">
<p>
Getting a non-text browser in the text-based environment of Emacs
requires working around Emacs&rsquo; systems. <span class="small-caps">eaf</span> (Emacs Application
Framework) does this through creating an exterior python system that
then hooks into Emacs to run a number of programs, chief among them a
webkit-based browser.
</p>

<p>
The largest problem I have with the <span class="small-caps">eaf</span>&rsquo;s browser is not really with
the browser itself, although it is a bit unstable, but instead with
the <span class="small-caps">eaf</span> itself. Being part of this large group of packages that feel
very external to Emacs they are troublesome to install and maintain as
part of a workflow. Installing <span class="small-caps">eaf</span> (something you&rsquo;re probably only
doing for the browser) asks you individually if you&rsquo;d like to install
things like a git client, system monitor, file browser, terminal,
multiple demos, and <a href="https://github.com/orgs/emacs-eaf/repositories">numerous other things</a>. Some of these things <i>are</i>
impressive and useful, but I&rsquo;d rather explicitly say what I&rsquo;d <i>like</i> to
install instead of having a Y/N on each program. Imagine having to do
that for every <span class="small-caps">elpa</span> package when first launching Emacs! <span class="small-caps">eaf</span> is of
course nowhere near that level, but it&rsquo;s still a baffling design
decision. It also makes it way less portable, since I can&rsquo;t describe
what <span class="small-caps">eaf</span> programs I want to install (although I can for which ones I
want to <i>use</i>) in a declarative way. It also has a few issues with
javascript that feel like deal-breakers for a graphical browser, I&rsquo;d
rather go all-the-way with a text-based browser like <code>eww</code> or use a
fully functional graphical browser like firefox. ❦
</p>
</div>
</div>
</div>
<div id="footnotes">
<h2 class="footnotes">Footnotes: </h2>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
I can use tailscale to edit the files remotely using <code>TRAMP</code>, but
that is usually pretty slow since my home network is very
unreliable. Much easier to just sync &ldquo;asynchronously&rdquo; when the network
feels like it.
</p></div></div>

<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
<code>exwm</code> has other issues, namely around multi-monitor setups. They
make sense and are probably sacrifices I would have made had I created
<code>exwm</code>, but I don&rsquo;t really like working with frames instead of
windows. It becomes another layer of friction, even if I&rsquo;m still
working within Emacs.
</p></div></div>

<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
To try and replicate this behaviour in <code>eww</code> you have to run
<code>(eww-reload t)</code>, by default bound to <code>C-u g</code>, to refresh the buffer and
reformat it. You can probably run it in a hook whenever you resize
windows, but I don&rsquo;t care to do that.
</p></div></div>


</div>
</div>
]]></description>
  <category><![CDATA[emacs]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/eww.html</link>
  <guid>https://joarvarndt.se/eww.html</guid>
  <pubDate>Mon, 12 May 2025 00:00:00 +0800</pubDate>
</item>
<item>
  <title><![CDATA[Functional Life Without Side-Effects]]></title>
  <description><![CDATA[
 <h2 class="post-subtitle"></h2>
<nav id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#functions">Functions</a></li>
<li><a href="#side-effects">Side Effects</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</div>
</nav>
<hr>

<p class="dcap">
Programming languages (and their users) are often grouped into two
main intellectual schools of thought: Object-oriented programming
(<span class="small-caps">oop</span>) and functional programming. These two schools both claim to
offer ways to manage the increasing complexity that comes with the
development of larger and larger programs. Functional programming
states that this complexity can be managed by following these
principles:
</p>

<ol class="org-ol">
<li>Functions are first-class citizens.</li>
<li>Functions are deterministic.</li>
<li>Functions should not have side effects.</li>
<li>Data is immutable.</li>
</ol>

<p>
Not all of these are directly applicable to non-programming concepts,
but twisting them somewhat gives us an interesting perspective on how
do deal with complexity and chaos in our lives.
</p>
<div id="outline-container-functions" class="outline-2">
<h2 id="functions">Functions</h2>
<div class="outline-text-2" id="text-functions">
<p>
In order to apply <i>functional</i> programming to our activities, we must
first endeavour to understand what a <i>function</i> is in this new
context. Strictly speaking a function transforms each element in the
set X into exactly one element in set Y. This is where the second
point listed earlier comes from, since each element in set X must
always output the same element (so no randomness).
</p>

<p>
A function must therefore <i>produce</i> something; it is not a activity
merely done for the sake of doing. Things like doomscrolling or verbal
communication are not functions, since they do not produce a tangible
output — even if they may impact you in some way.
</p>

<p>
In what activities do we produce things then? One obvious example is
cooking. Ingredients are combined or separated and should hopefully
come together to create the dish that you aimed for. This will not be
entirely deterministic since ingredients may be of a differing
quality, your tools may be different from time to time, and your own
skills as a chef means that you will inevitably make mistakes. In
reality — as opposed to the realm of mathematics and computer science
— the world will never be truly deterministic. But this does not mean
that we should surrender completely, instead we must aim toward
cooking a dish as we intended each and every time.
</p>
</div>
</div>
<div id="outline-container-side-effects" class="outline-2">
<h2 id="side-effects">Side Effects</h2>
<div class="outline-text-2" id="text-side-effects">
<p>
Another example where it is perhaps not imminently obvious that the
activity is functional is cleaning. It also highlights the aspect of
functional programming that is perhaps most famous, and is also
present in the act of cooking: side effects. Side effects are when a
given function not only transforms the input X into output Y, but also
modifies a third value. This impacts the global <i>state</i> and may mean
that the function, or any other function, behaves differently when run
in the future. When I vacuum my apartment I have to move furniture to
reach all of the nooks and crannies where dust settles. This means
that the function whose purpose is to rid the floor of dust causes the
side effect of moving my furniture. These side effects have to be
managed and dealt with quickly, and critically be seen as part of the
task itself.
</p>

<p>
It is easy to dismiss many side effects as something that can be dealt
with later, but this dramatically increases the mental effort
involved. Not only are you more familiar with the various items whose
position and status must be dealt with, but you are also already
performing the task, and so there is no &ldquo;startup&rdquo; cost involved with
dealing with it straight away. Staring at the dishes that need to be
done after a meal is an experience I expect most people to be familiar
with, but it can often times be avoided when they are seen as
inexirably linked to the task itself.
</p>
</div>
</div>
<div id="outline-container-conclusion" class="outline-2">
<h2 id="conclusion">Conclusion</h2>
<div class="outline-text-2" id="text-conclusion">
<p>
Life may never reach the pure determinism prized by functional
programming, but adopting its mindset allows us to manage the
increasing complexity of everyday life. By thinking of our actions as
functions we can try and deal with side-effects immidiately instead of
waiting for them to pile up over time, minimizing unwanted distortions
in our personal “global state”.
</p>
</div>
</div>

]]></description>
  <category><![CDATA[technology]]></category>
  <category><![CDATA[life]]></category>
  <category><![CDATA[streams]]></category>
  <link>https://joarvarndt.se/functional-living.html</link>
  <guid>https://joarvarndt.se/functional-living.html</guid>
  <pubDate>Thu, 13 Mar 2025 00:00:00 +0800</pubDate>
</item>
</channel>
</rss>
