<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>akos.ma</title><link>https://akos.ma/</link><description>Recent content on akos.ma</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Fri, 07 Aug 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://akos.ma/index.xml" rel="self" type="application/rss+xml"/><item><title>Playing With Admonitions</title><link>https://akos.ma/blog/playing-with-admonitions/</link><pubDate>Fri, 07 Aug 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/playing-with-admonitions/</guid><description><![CDATA[ <p>I&rsquo;ve added the possibility of generating <a href="https://docs.asciidoctor.org/asciidoc/latest/blocks/admonitions/">Asciidoctor-style admonitions</a> to this website, not only in the final HTML generated for this website, but also in the PDF and EPUB outputs, and you might have seen them in action in previous posts.</p>]]></description><content:encoded><![CDATA[ <p>I&rsquo;ve added the possibility of generating <a href="https://docs.asciidoctor.org/asciidoc/latest/blocks/admonitions/">Asciidoctor-style admonitions</a> to this website, not only in the final HTML generated for this website, but also in the PDF and EPUB outputs, and you might have seen them in action in previous posts.</p>
<p>In Asciidoctor and <a href="https://antora.org/">Antora</a> I liked to use such highlighted blocks, to showcase some particular information, so I wanted to have them on this website too. Although, as you might imagine, Markdown does not provide this capability <em>per se</em>.</p>
<p>The Markdown code I use looks like this, showing its corresponding output below.</p>
<h2 id="notes">Notes</h2>
<p>Great for extra information.</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>::: note
</span></span><span style="display:flex;"><span>This is a note.
</span></span><span style="display:flex;"><span>:::
</span></span></code></pre></div><p>NOTE: This is a note.</p>
<h2 id="tips">Tips</h2>
<p>In green, for suggestions or shortcuts.</p>
<pre tabindex="0"><code>::: tip
This is a tip.
:::
</code></pre><p>TIP: This is a tip.</p>
<h2 id="important">Important</h2>
<p>Emphasis you must not miss.</p>
<pre tabindex="0"><code>::: important
This is important.
:::
</code></pre><p>IMPORTANT: This is important.</p>
<h2 id="caution">Caution</h2>
<p>Risk of confusion or bad outcome.</p>
<pre tabindex="0"><code>::: caution
This is a cautionary tale.
:::
</code></pre><p>CAUTION: This is a cautionary tale.</p>
<h2 id="warning">Warning</h2>
<p>To highlight hazard or serious consequences.</p>
<pre tabindex="0"><code>::: warning
This is a warning.
:::
</code></pre><p>WARNING: This is a warning.</p>
<h2 id="pdf-and-epub">PDF and EPUB</h2>
<p>This also works in the <a href="playing-with-admonitions.pdf">PDF</a> and <a href="playing-with-admonitions.epub">EPUB</a> outputs, too.</p>
<p><embed src="playing-with-admonitions.pdf"
width="100%" height="900px" ></p>
]]></content:encoded></item><item><title>How to Solve Webcam Shenanigans With systemd</title><link>https://akos.ma/blog/how-to-solve-webcam-shenanigans-with-systemd/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/how-to-solve-webcam-shenanigans-with-systemd/</guid><description> &lt;p>Last year I talked about &lt;a href="/blog/my-faithful-logitech-c930e-webcam/">my faithful Logitech C930e webcam&lt;/a>, and at the end of the article I mentioned that I have to manually disable the autofocus, and that sadly that setting is gone at every reboot, and even every time I unplug the webcam; my question then was, can I automate this somehow?&lt;/p></description><content:encoded><![CDATA[ <p>Last year I talked about <a href="/blog/my-faithful-logitech-c930e-webcam/">my faithful Logitech C930e webcam</a>, and at the end of the article I mentioned that I have to manually disable the autofocus, and that sadly that setting is gone at every reboot, and even every time I unplug the webcam; my question then was, can I automate this somehow?</p>
<p>And the answer is yes, of course you can. The first thing I&rsquo;ve done is to change the approach, and instead of manually typing this command:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ v4l2-ctl --device<span style="color:#ec0000">=</span>/dev/video4 --set-ctrl <span style="color:#ec0000">focus_automatic_continuous</span><span style="color:#ec0000">=</span><span style="color:#008900">0</span>
</span></span></code></pre></div><p>… I took a different approach, using the <code>v4l2ctrl</code> command instead:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ sudo dnf install v4l2ucp
</span></span><span style="display:flex;"><span>$ v4l2ctrl -d /dev/video4 -s camera-settings.txt
</span></span></code></pre></div><p>This saves all settings of the camera in a single text file, and the cool thing is that you can use the <code>-l</code> argument to load them back, and it gives you control over many different parameters of the webcam in one place and you can load them in one shot:</p>
<pre tabindex="0"><code>$ v4l2ctrl -d /dev/video4 -l camera-settings.txt
</code></pre><h2 id="loading-on-startup">Loading on Startup</h2>
<p>Now I need to wrap this command as a <a href="https://en.wikipedia.org/wiki/Systemd">systemd</a> service that is executed every time my my machine boots up, saved in a file named <code>fix-webcam.service</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-systemd" data-lang="systemd"><span style="display:flex;"><span><span style="color:#ec0000">[Unit]</span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">Description</span><span style="color:#ec0000">=</span><span style="color:#008900">Fix Logitech webcam autofocus</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">[Service]</span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">Type</span><span style="color:#ec0000">=</span><span style="color:#008900">oneshot</span>
</span></span><span style="display:flex;"><span># Wait 10 seconds for systemd-logind to grant video device permissions
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">ExecStartPre</span><span style="color:#ec0000">=</span><span style="color:#008900">/usr/bin/sleep 10</span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">ExecStart</span><span style="color:#ec0000">=</span><span style="color:#008900">/usr/bin/v4l2ctrl -d /dev/video4 -l /home/user/camera-settings.txt</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">[Install]</span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">WantedBy</span><span style="color:#ec0000">=</span><span style="color:#008900">default.target</span>
</span></span></code></pre></div><p>I installed this service it in my local systemd user folder:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ mkdir -p ~/.config/systemd/user
</span></span><span style="display:flex;"><span>$ mv fix-webcam.service ~/.config/systemd/user/fix-webcam.service
</span></span><span style="display:flex;"><span>$ systemctl --user daemon-reload
</span></span><span style="display:flex;"><span>$ systemctl --user <span style="color:#ec0000">enable</span> fix-webcam.service
</span></span></code></pre></div><p>TIP: Yes, you can also use symbolic links and store the services somewhere else.</p>
<p>Et voilà! Now this service is executed every time I start my machine. But what happens if I unplug the webcam? Then the settings would be gone again. Worry not! Turns out, there&rsquo;s a way to trigger the systemd service above whenever a USB device is plugged.</p>
<h2 id="loading-on-unplug--replug">Loading on Unplug &amp; Replug</h2>
<p>First plug your webcam, and find the ID of your USB device:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ lsusb  | grep 046d
</span></span><span style="display:flex;"><span>Bus <span style="color:#008900">003</span> Device 032: ID 046d:c548 Logitech, Inc. Logi Bolt Receiver
</span></span><span style="display:flex;"><span>Bus <span style="color:#008900">003</span> Device 034: ID 046d:0843 Logitech, Inc. Webcam C930e
</span></span></code></pre></div><p>The <code>046d</code> vendor ID corresponds to Logitech, and in the case of my C930e webcam, <code>0843</code> is the product ID. I also have a wireless Logitech MX Master 3S mouse, so the dongle appears there as well.</p>
<p>Then, as a superuser, I created a new file called <code>/etc/udev/rules.d/99-dock-fixes.rules</code> with the following contents:</p>
<pre tabindex="0"><code># Trigger the webcam script when the Logitech camera is plugged in
ACTION==&#34;bind&#34;, SUBSYSTEM==&#34;usb&#34;, ATTR{idVendor}==&#34;046d&#34;, ATTR{idProduct}==&#34;0843&#34;, TAG+=&#34;systemd&#34;, ENV{SYSTEMD_USER_WANTS}+=&#34;fix-webcam.service&#34;
</code></pre><p>Then reload the changes, and trigger the action to test it:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ sudo udevadm control --reload-rules
</span></span><span style="display:flex;"><span>$ sudo udevadm trigger --action<span style="color:#ec0000">=</span><span style="color:#ec0000">bind</span>
</span></span></code></pre></div><p>TIP: I could verify the changes with another tmux panel showing the output of the following command: <code>watch &quot;v4l2-ctl --list-ctrls --device /dev/video4 | grep focus_automatic&quot;</code></p>
<p>Et voilà (bis)! Now every time, a few seconds after I re-plug your webcam, my preferred settings apply.</p>
<p>NOTE: In the code above I&rsquo;m using the <code>bind</code> action instead of <code>add</code>, for a reason; you should know that when you plug in a USB device, <code>ACTION==&quot;add&quot;</code> fires the exact millisecond the metal connects, before Linux has actually loaded the webcam or audio drivers. Sometimes, <code>udev</code> refuses to trigger the systemd service because the underlying device nodes don&rsquo;t fully exist yet. Instead, <code>bind</code> tells <code>udev</code> to wait until the Linux drivers have actually attached to the hardware before triggering the service.</p>
]]></content:encoded></item><item><title>Birome</title><link>https://akos.ma/blog/birome/</link><pubDate>Fri, 24 Jul 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/birome/</guid><description><![CDATA[ <p>In Argentina we like to tell this urban legend that is somehow kind-of-true, that the <a href="https://en.wikipedia.org/wiki/Ballpoint_pen">ballpoint pen</a> was invented there, thanks to the idea of a Hungarian-born man called <a href="https://en.wikipedia.org/wiki/L%C3%A1szl%C3%B3_B%C3%ADr%C3%B3">László Bíró</a> who moved to Argentina at some point during World War II.</p>]]></description><content:encoded><![CDATA[ <p>In Argentina we like to tell this urban legend that is somehow kind-of-true, that the <a href="https://en.wikipedia.org/wiki/Ballpoint_pen">ballpoint pen</a> was invented there, thanks to the idea of a Hungarian-born man called <a href="https://en.wikipedia.org/wiki/L%C3%A1szl%C3%B3_B%C3%ADr%C3%B3">László Bíró</a> who moved to Argentina at some point during World War II.</p>
<p>This story is not entirely true in the sense that when Bíró came to Argentina he had <em>already</em> invented the ballpoint pen (actually, it was invented by <a href="https://en.wikipedia.org/wiki/John_J._Loud">somebody else</a>, but that early patent lapsed without commercial distribution). The first large-scale production and commercialization of the ballpoint pen started and spread from there, through the &ldquo;Bíró &amp; Meyne&rdquo; company. And yes, in Argentina we still refer to ballpoint pens as &ldquo;biromes&rdquo; (derived from the company name), and I&rsquo;m not kidding, that&rsquo;s really how we call them, and that&rsquo;s how I&rsquo;ll call them in this article.</p>
<p>Then the story goes that a French man called <a href="https://en.wikipedia.org/wiki/Marcel_Bich">Marcel Bich</a> bought Bíró&rsquo;s patent, and thus begat the <a href="https://en.wikipedia.org/wiki/Bic_Cristal">Bic Cristal</a> ballpoint pen that we all know, somewhere between the 1950s and 60s. And this is the gist of this article.</p>
<p>Because for all practical purposes, I consider the birome (specifically, the Bic Cristal) to be the most important invention of the 20th century. Yes: more important even that aeroplanes, computers, antibiotics, cell phones, tamagotchis, space travel, Walkmans, Pink Floyd, paperback books, or whatever else that came up during that time. The Bic Cristal is inexpensive (like, dirt cheap, not even 0.22 CHF per unit <a href="https://www.galaxus.ch/en/s12/product/bic-cristal-original-100x-pens-14555783">from Galaxus</a>, that&rsquo;s like 0.27 USD or 0.24 EUR), durable, lightweight, trustworthy, ubiquitous, simple, practical, reliable, and I&rsquo;m running out of positive adjectives as I write this. And no, no other brand of birome can reach the quality of the Bic Cristal, not even remotely. All other biromes just plain suck in one way or another.</p>
<p>The Bic Cristal birome is a triumph of human engineering and ingenuity. It&rsquo;s so good it hurts. Paraphrasing Steve Jobs, it&rsquo;s like a bicycle for your hand.</p>
<p>Not many technologies created by humans can claim to be so deceptively useful and trustworthy at the same time. The birome is the most important invention of the 20th century because it democratized, without pretention and without limits, writing for everyone, everywhere, at every time.</p>
<p>No matter how many other kinds of pens I try (and believe me, I love my <a href="https://pentel.ch/energel/">Pentel EnerGel</a> pens, I truly do) I <em>always</em> come back to the Bic Cristal. It&rsquo;s the pen I used throughout primary and high school (except for a short while in early primary school, when my teachers insisted we use fountain pens for some pedantic reason).</p>
<p>I simply love the ink quality (it dries in literal milliseconds and never ever fades away from paper) and how lightweight it feels on my hand. Its transparency tells you how much ink remains on it, and the cap fits perfectly on the opposite side while you write, providing a very nice counterweight on your fingers.</p>
<p>The birome feels surprisingly good in your hand. But, of course, writing is out-of-fashion these days, just like reading, or more generally, just like thinking.</p>
<p>As the Joker <a href="https://www.youtube.com/watch?v=IEuB3bAgdjY">said</a> in the 1989 &ldquo;Batman&rdquo; movie,</p>
<blockquote>
<p>The pen is truly mightier than the sword.</p>
</blockquote>
<p>And in that category, the Bic Cristal birome is the mightiest of them all.</p>
]]></content:encoded></item><item><title>Ubuntu 5.10 on QEMU</title><link>https://akos.ma/blog/ubuntu-5.10-on-qemu/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/ubuntu-5.10-on-qemu/</guid><description><![CDATA[ <p>Following the celebration of my <a href="/blog/20-years-of-linux/">20 years with Linux</a>, I wanted to recreate the moment I installed Ubuntu &ldquo;Breezy Badger&rdquo; 5.10 for the first time, and with <a href="https://www.qemu.org/">QEMU</a> it was quite easy to do on my modern Fedora 44 environment of 2026.</p>]]></description><content:encoded><![CDATA[ <p>Following the celebration of my <a href="/blog/20-years-of-linux/">20 years with Linux</a>, I wanted to recreate the moment I installed Ubuntu &ldquo;Breezy Badger&rdquo; 5.10 for the first time, and with <a href="https://www.qemu.org/">QEMU</a> it was quite easy to do on my modern Fedora 44 environment of 2026.</p>
<h2 id="install-and-run">Install and Run</h2>
<p>First of all I downloaded the <a href="https://old-releases.ubuntu.com/releases/breezy/ubuntu-5.10-install-i386.iso">install CD for i386</a> of that old release, <a href="https://old-releases.ubuntu.com/releases/breezy/">still available</a> on the Ubuntu website after all these years.</p>
<p>Once that was done, I proceeded to create a QEMU virtual hard disk to install the OS into:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ qemu-img create -f qcow2 ubuntu.qcow2 30G
</span></span></code></pre></div><p>Then, just launch a new virtual machine, booting from the virtual CD-ROM drive first, emulating a gigabyte of RAM, which is a reasonable amount for a computer of 2005:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ qemu-system-i386 <span style="color:#008900">\
</span></span></span><span style="display:flex;"><span>  -m <span style="color:#008900">1024</span> <span style="color:#008900">\
</span></span></span><span style="display:flex;"><span>  -cdrom ubuntu-5.10-install-i386.iso <span style="color:#008900">\
</span></span></span><span style="display:flex;"><span>  -drive <span style="color:#ec0000">file</span><span style="color:#ec0000">=</span>ubuntu.qcow2,format<span style="color:#ec0000">=</span>qcow2 <span style="color:#008900">\
</span></span></span><span style="display:flex;"><span>  -boot d <span style="color:#008900">\
</span></span></span><span style="display:flex;"><span>  -no-reboot
</span></span></code></pre></div><p>The VM boots, and you&rsquo;re immediately taken to the installation process. Select your locale and keyboard, and choose the defaults for the graphics card. Enter your desired hostname, timezone, and create a new user for your installation. The whole process takes a while to finish.</p>
<p>Once this is done, the VM shuts down automatically instead of rebooting (thanks to the <code>-no-reboot</code> option) and then you must start it back again, directly from the hard disk this time:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ qemu-system-i386 <span style="color:#008900">\
</span></span></span><span style="display:flex;"><span>  -m <span style="color:#008900">1024</span> <span style="color:#008900">\
</span></span></span><span style="display:flex;"><span>  -drive <span style="color:#ec0000">file</span><span style="color:#ec0000">=</span>ubuntu.qcow2,format<span style="color:#ec0000">=</span>qcow2 <span style="color:#008900">\
</span></span></span><span style="display:flex;"><span>  -boot c <span style="color:#008900">\
</span></span></span><span style="display:flex;"><span>  -full-screen
</span></span></code></pre></div><p class="shadow"><img src="ubuntu-5.10-booting.png" alt=""></p>
<p>The operating system boots into a configuration phase, and after a few minutes, voilà! Ubuntu &ldquo;Breezy Badger&rdquo; 5.10 on your screen, 21 years later:</p>
<p><img src="ubuntu-5.10-running.png" alt=""></p>
<p>The default system includes quite a lot of software, including OpenOffice, Firefox, and whatnot. It was quite an impressive offering, all for free, and immediately compatible with pretty much every personal computer available to the public in 2005. No wonder Ubuntu became so popular back in the day.</p>
<h2 id="ppc-shenanigans">PPC Shenanigans</h2>
<p>Before reaching this point, I tried unsuccessfully to run the PPC version of Ubuntu Badger, which was the one I installed on my <a href="https://en.wikipedia.org/wiki/IBook#iBook_G3_Dual_USB_(%22Snow%22,_2001-2003)">&ldquo;Snow&rdquo; iBook G3</a> 21 years ago.</p>
<p>I gave a short description of that machine <a href="/blog/xubuntu/">in an old article</a> on this blog:</p>
<blockquote>
<p>The main machine where I have Xubuntu installed is a rather old G3 iBook that I bought in 2002, with only 256 MB of RAM and 30 GB of hard disk. The machine runs like a charm, but of course with GNOME and KDE there’s a lot of swapping. As soon as I installed the Xfce desktop, things went really faster. I can only recommend using Xubuntu in old machines: the base system only takes 90 MB of RAM, and applications load faster than in GNOME or KDE.</p>
</blockquote>
<p>Let&rsquo;s all take a minute to consider that a modern operating system, running very decent software, could run in just 256 MB of RAM. <em>Le sigh</em>.</p>
<p>I first used the <code>qemu-system-ppc</code> command, with the <code>-M mac99,via=pmu</code> argument, to help this old Ubuntu recognize my mouse and keyboard. In particular, the <code>via=pmu</code> argument supposedly is meant to route the input devices through something called the &ldquo;Power Management Unit&rdquo;, expected by Ubuntu PPC kernels back in the day. I also tried using the <code>-cpu g3</code> and the <code>-vga std</code> options in various iterations.</p>
<p>After the installation (which was somewhat successful), I had to boot the VM the <code>-prom-env &quot;boot-device=hd:,\yaboot&quot;  -prom-env &quot;boot-args=conf=hd:,\yaboot.conf&quot;</code> arguments, because otherwise the VM defaults to looking for an Apple bootloader… which doesn&rsquo;t exist in this context for obvious reasons. Instead we ask the VM to boot directly from <a href="https://en.wikipedia.org/wiki/Yaboot">Yaboot</a>.</p>
<p>But the biggest drawback was that the installer failed to correctly setup the <a href="https://en.wikipedia.org/wiki/X.Org_Server">X.org</a> server, and thus no desktop would ever appear on screen. This is without mentioning that QEMU had to translate all PPC instructions to the x86_64 architecture of my laptop, which meant that the whole system was reaaaaaaally sluggish.</p>
<p>So, at the end of the day, the i386 version was perfect for the purposes of memorabilia.</p>
<p>At this point in my laptop I have emulators for <a href="/blog/word-5.5-on-dosbox/">MS-DOS</a>, <a href="/blog/word-5.1a-and-excel-4-for-mac-on-basilisk-ii/">Mac OS System 7.5</a>, <a href="/blog/freedos-on-qemu/">FreeDOS</a>, <a href="/blog/alpine-linux-in-virtualbox/">Alpine Linux</a>, and now also Ubuntu 5.10 for 32-bit i386.</p>
]]></content:encoded></item><item><title>Tango</title><link>https://akos.ma/blog/tango/</link><pubDate>Fri, 10 Jul 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/tango/</guid><description><![CDATA[ <p>Ah, so you think I&rsquo;m going to talk about the dance called <a href="https://en.wikipedia.org/wiki/Tango">Tango</a>; but no, this time I&rsquo;m going to talk about an <a href="https://es.wikipedia.org/wiki/Tango_(%C3%A1lbum_de_Charly_Garc%C3%ADa_y_Pedro_Aznar)">album called &ldquo;Tango&rdquo;</a>, by those two towering figures of <a href="/blog/the-very-quick-and-sloppy-guide-to-argentine-rock/">Argentine rock</a>, namely <a href="https://en.wikipedia.org/wiki/Charly_Garc%C3%ADa">Charly García</a> and <a href="https://es.wikipedia.org/wiki/Pedro_Aznar">Pedro Aznar</a>.</p>]]></description><content:encoded><![CDATA[ <p>Ah, so you think I&rsquo;m going to talk about the dance called <a href="https://en.wikipedia.org/wiki/Tango">Tango</a>; but no, this time I&rsquo;m going to talk about an <a href="https://es.wikipedia.org/wiki/Tango_(%C3%A1lbum_de_Charly_Garc%C3%ADa_y_Pedro_Aznar)">album called &ldquo;Tango&rdquo;</a>, by those two towering figures of <a href="/blog/the-very-quick-and-sloppy-guide-to-argentine-rock/">Argentine rock</a>, namely <a href="https://en.wikipedia.org/wiki/Charly_Garc%C3%ADa">Charly García</a> and <a href="https://es.wikipedia.org/wiki/Pedro_Aznar">Pedro Aznar</a>.</p>
<p>Now, you&rsquo;re reading this and you&rsquo;re probably asking yourself, why? As <a href="https://en.wikipedia.org/wiki/Simon_Sinek">Simon Sinek</a> once said, <a href="https://en.wikipedia.org/wiki/Start_with_Why">start with why</a>: why, why, why does this guy always talk to us without talking to us. Why, why, why.</p>
<p>Well, I&rsquo;ll tell you why. Because I am talking to you. More precisely, <a href="https://www.youtube.com/watch?v=Z7AERldCALc">I&rsquo;m talking to your heart</a>, which is exactly the title of a song by our national hero Charly García, released in &ldquo;Tango&rdquo; on March 29th, 1986, that is, a bit over 40 years ago, and I&rsquo;ll leave it up to you to Google Translate the <a href="https://genius.com/Charly-garcia-y-pedro-aznar-hablando-a-tu-corazon-lyrics">lyrics</a>, which are sublime.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/Z7AERldCALc?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Oh yeah, it does sound 1980s, huh? Those synths. THOSE SYNTHESIZERS.</p>
<p>Charly, being the superstar he still is (regardless of his substance abuse) has had <a href="https://es.wikipedia.org/wiki/Hello!_MTV_Unplugged">his own MTV Unplugged</a> album in the 1990s, because it was such a tradition, and within that Unplugged album there&rsquo;s another song I deeply love, (originally released together in the same album &ldquo;Tango&rdquo;), and that other incredible song is called <a href="https://www.youtube.com/watch?v=Fy1WuMSC7Wg">&ldquo;Pasajera en Trance&rdquo;</a>, and here it goes again because reasons. It speaks of distance, flow, travel, and so many other things.</p>
<p>&ldquo;She dances on the sea&rdquo;, says Charly.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/Fy1WuMSC7Wg?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>OK, here go the lyrics, because they are out-of-this-world. Enjoy.</p>
<blockquote>
<p>She’s about to board, <br>
maybe she’ll snag a seat on the deck. <br>
She’s about to take off, <br>
she’s leaving. <br>
She travels without paying, <br>
the old trick of walking in the shadows. <br>
She dances on the sea, <br>
she’s leaving. <br>
Passenger in a trance, <br>
passenger in perpetual transit. <br>
Passenger in a trance, <br>
passing through the right places. <br>
True love, <br>
is like sleeping and being awake. <br>
True love, <br>
is like living in an airport.</p>
</blockquote>
<p>As Charly says at the end, &ldquo;merci beaucoup&rdquo;.</p>
]]></content:encoded></item><item><title>NeXTClock</title><link>https://akos.ma/blog/nextclock/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/nextclock/</guid><description><![CDATA[ <p>So here is a new project on GitLab called <a href="https://gitlab.com/akosma/nextclock">NeXTClock</a>: a small browser-based recreation of a NeXTSTEP-style clock applet from the 1990s. This modern version renders a 64-unit clock face into a square <code>&lt;canvas&gt;</code> using JavaScript and <code>requestAnimationFrame()</code>, with optional scaling from 64 to 500 pixels, because, well, Java Applets <a href="/blog/java-applets-in-2023/">aren&rsquo;t really a thing anymore</a>.</p>]]></description><content:encoded><![CDATA[ <p>So here is a new project on GitLab called <a href="https://gitlab.com/akosma/nextclock">NeXTClock</a>: a small browser-based recreation of a NeXTSTEP-style clock applet from the 1990s. This modern version renders a 64-unit clock face into a square <code>&lt;canvas&gt;</code> using JavaScript and <code>requestAnimationFrame()</code>, with optional scaling from 64 to 500 pixels, because, well, Java Applets <a href="/blog/java-applets-in-2023/">aren&rsquo;t really a thing anymore</a>.</p>
<h2 id="background">Background</h2>
<p>The original source code of the Java applet came from the <a href="https://web.archive.org/web/19981207040305/http://the-labs.com/NeXTClock/">Internet Archive</a>. This original <code>NeXTClock</code> applet was written by <a href="https://renekmueller.com/">Rene K. Mueller</a> and is preserved in <code>original/NeXTClock.java</code> in the GitLab project. That applet used <a href="https://en.wikipedia.org/wiki/Abstract_Window_Toolkit">AWT</a>, a background GIF, and a thread that updated the displayed time. I used that Java applet in my <a href="/blog/gamma/">&ldquo;GaMMA&rdquo; home page</a> around 1999, until 2004.</p>
<p>Interestingly enough, the original Java version was written almost exactly 30 years ago at the time of this article:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>// --- NeXTClock, written by Rene K. Mueller &lt;kiwi@the-labs.com&gt;
</span></span><span style="display:flex;"><span>//     V0.20: Jul 20, 1996
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> java.awt.Graphics;
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> java.awt.Image;
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> java.awt.Color;
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> java.awt.Font;
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> java.awt.FontMetrics;
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> java.util.Date;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">public</span> <span style="color:#ec0000">class</span> <span style="color:#5f5fff">NeXTClock</span> <span style="color:#ec0000">extends</span> java.<span style="color:#5f5fff">applet</span>.<span style="color:#5f5fff">Applet</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ec0000">implements</span> Runnable {
</span></span><span style="display:flex;"><span>// ...
</span></span></code></pre></div><p>The translated source code in JavaScript in <code>src/NeXTClock.js</code> keeps the same 64 x 64 layout and the same visual idea: a NeXT-like clock tile with the weekday, day of the month, month, green seven-segment time, and blinking separator pixels. The drawing code scales that original layout to fit larger square canvases while preserving the pixelated look.</p>
<h2 id="usage">Usage</h2>
<p>Include <code>NeXTClock.js</code>, add a <code>&lt;CANVAS&gt;</code> object on your page, then create and start a clock:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#ec0000">script</span> <span style="color:#5f5fff">src</span><span style="color:#ec0000">=</span><span style="color:#008900">&#34;NeXTClock.js&#34;</span>&gt;&lt;/<span style="color:#ec0000">script</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#ec0000">canvas</span> <span style="color:#5f5fff">id</span><span style="color:#ec0000">=</span><span style="color:#008900">&#34;clock&#34;</span> <span style="color:#5f5fff">width</span><span style="color:#ec0000">=</span><span style="color:#008900">&#34;128&#34;</span> <span style="color:#5f5fff">height</span><span style="color:#ec0000">=</span><span style="color:#008900">&#34;128&#34;</span>&gt;&lt;/<span style="color:#ec0000">canvas</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#ec0000">script</span>&gt;
</span></span><span style="display:flex;"><span>  <span style="color:#ec0000">const</span> canvas <span style="color:#ec0000">=</span> <span style="color:#ec0000">document</span>.getElementById(<span style="color:#008900">&#34;clock&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#ec0000">const</span> clock <span style="color:#ec0000">=</span> <span style="color:#ec0000">new</span> NeXTClock(canvas, {
</span></span><span style="display:flex;"><span>    size<span style="color:#ec0000">:</span> <span style="color:#008900">128</span>,
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  clock.start();
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#ec0000">script</span>&gt;
</span></span></code></pre></div><p><noscript><img src="noscript.png" alt=""></noscript></p>
<p align="center">
<canvas id="clock1" width="64" height="64"></canvas>
&nbsp;
<canvas id="clock2" width="128" height="128"></canvas>
&nbsp;
<canvas id="clock3" width="256" height="256"></canvas>
</p>
<p style="text-align: center"><input type="button" id="startButton" value="Start"> <input type="button" id="stopButton" value="Stop"> <span id="timeSpan"></span><br>
<input type="range" min="0" max="1440" value="0" step="1" id="timeRange" style="width: 80%"></p>
<script src="NeXTClock.js"></script>
<script src="app.js"></script>
<p>The constructor expects an <code>HTMLCanvasElement</code>. By default, it uses the canvas display size and keeps it square. You can also pass a <code>size</code> option or call <code>setSize()</code> later; requested sizes are rounded and clamped between 64 and 500 pixels.</p>
<p>Available methods on the class:</p>
<ul>
<li><code>start()</code> begins animation using <code>requestAnimationFrame()</code>.</li>
<li><code>stop()</code> cancels the animation frame loop.</li>
<li><code>draw(date)</code> draws one frame for the supplied <code>Date</code>, or the current local time when omitted.</li>
<li><code>setSize(size)</code> updates the square canvas size and redraws the clock.</li>
</ul>
<p>You can pass the following options to the constructor:</p>
<ul>
<li><code>backgroundSrc</code>: optional path to a background image. When omitted, the built-in canvas background is used.</li>
<li><code>darkGreen</code>: color used for the seven-segment time display. Defaults to <code>#00c800</code>.</li>
<li><code>size</code>: optional square canvas size in pixels, clamped from 64 to 500.</li>
</ul>
<p>The clock automatically observes canvas resizing when <code>ResizeObserver</code> is available, so CSS-driven size changes are reflected in the next rendered frame.</p>
<p>As usual, you can grab the source code <a href="https://gitlab.com/akosma/nextclock">on my GitLab account</a>.</p>
]]></content:encoded></item><item><title>Goodbye, Cursor</title><link>https://akos.ma/blog/goodbye-cursor/</link><pubDate>Fri, 26 Jun 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/goodbye-cursor/</guid><description><![CDATA[ <p>Regular readers of this blog know that <a href="/blog/vibe-coding-with-cursor/">I&rsquo;ve been</a> quite a happy user of <a href="https://cursor.com/">Cursor</a> for the past year, but the recent news of its mother company being <a href="https://www.cnbc.com/2026/06/16/spacex-spcx-cursor-acquisition-ipo.html">purchased</a> by SpaceX has made me reconsider my decision, and cancel my current subscription altogether.</p>]]></description><content:encoded><![CDATA[ <p>Regular readers of this blog know that <a href="/blog/vibe-coding-with-cursor/">I&rsquo;ve been</a> quite a happy user of <a href="https://cursor.com/">Cursor</a> for the past year, but the recent news of its mother company being <a href="https://www.cnbc.com/2026/06/16/spacex-spcx-cursor-acquisition-ipo.html">purchased</a> by SpaceX has made me reconsider my decision, and cancel my current subscription altogether.</p>
<p>The same way I don&rsquo;t have a Twitter account anymore, or I don&rsquo;t own (nor plan to buy) a Tesla, I prefer not to associate myself with certain billionaires out there. Personal choice.</p>
<p>There are several competitors of Cursor I&rsquo;ll be checking in the next few weeks for my personal use (the market of AI-enhanced coding tools is quite booming at the moment), among which I&rsquo;ll consider (in alphabetical order):</p>
<ul>
<li><a href="https://claude.com/product/claude-code">Anthropic Claude Code</a></li>
<li><a href="https://app.emergent.sh/landing/">Emergent</a></li>
<li><a href="https://antigravity.google/">Google Antigravity 2.0</a></li>
<li><a href="https://www.jetbrains.com/junie/">JetBrains Junie</a></li>
<li><a href="https://chatgpt.com/codex/">OpenAI Codex</a></li>
<li><a href="https://replit.com/">Replit</a></li>
</ul>
<p>And I&rsquo;ll give a second shot to <a href="https://github.com/features/copilot">GitHub Copilot</a>, which I had tried in early 2023 but without good results.</p>
]]></content:encoded></item><item><title>Conway in Haskell</title><link>https://akos.ma/blog/conway-in-haskell/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/conway-in-haskell/</guid><description><![CDATA[ <p>At this point it&rsquo;s becoming increasingly clear that I can ask <a href="https://cursor.com/">Cursor</a> to read any of the implementations of my <a href="https://gitlab.com/akosma/Conway/">Conway</a> project and translate that into any other programming language that I want; this is what I&rsquo;ve done last year with <a href="/blog/conway-in-nasm/">NASM</a>, <a href="/blog/conway-in-scala/">Scala</a>, and <a href="/blog/conway-in-bash/">Bash</a>, and this year with Haskell.</p>]]></description><content:encoded><![CDATA[ <p>At this point it&rsquo;s becoming increasingly clear that I can ask <a href="https://cursor.com/">Cursor</a> to read any of the implementations of my <a href="https://gitlab.com/akosma/Conway/">Conway</a> project and translate that into any other programming language that I want; this is what I&rsquo;ve done last year with <a href="/blog/conway-in-nasm/">NASM</a>, <a href="/blog/conway-in-scala/">Scala</a>, and <a href="/blog/conway-in-bash/">Bash</a>, and this year with Haskell.</p>
<p>Yes, I have read <a href="https://nostarch.com/lyah.htm">Miran Lipovača&rsquo;s masterpiece</a> but, like, <a href="/blog/learning-one-new-language-every-year/">15 years ago</a>, and to be honest I never quite got into using Haskell beyond small experiments. So what&rsquo;s a (lazy) developer to do in 2026? You guessed it. Particularly after writing a whole issue of De Programmatica Ipsum dedicated to the subject of <a href="https://deprogrammaticaipsum.com/issue/issue-093-functional-programming/">Functional Programming</a>, including an article about <a href="https://deprogrammaticaipsum.com/philip-wadler/">Philip Wadler</a>, one of the fathers of the language.</p>
<p>And once again, just like every time I use Cursor, I just let it do its thing after a good long prompt… and the magic operated in an uncanny way. The translation worked at first try, and I have nothing else to say at this point.</p>
<p>I do not know if you&rsquo;re fluent in Haskell, but this is the code I got at the end.</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-haskell" data-lang="haskell"><span style="display:flex;"><span><span style="color:#ec0000">#!/</span>usr<span style="color:#ec0000">/</span>bin<span style="color:#ec0000">/</span>env runghc
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> Conway.Coord (<span style="color:#5f5fff">Coord</span> (<span style="color:#ec0000">..</span>))
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> Conway.World (<span style="color:#5f5fff">World</span>, <span style="color:#5f5fff">beacon</span>, <span style="color:#5f5fff">blinker</span>, <span style="color:#5f5fff">block</span>, <span style="color:#5f5fff">evolve</span>, <span style="color:#5f5fff">glider</span>, <span style="color:#5f5fff">newWorld</span>, <span style="color:#5f5fff">tub</span>)
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> Control.Concurrent (<span style="color:#5f5fff">ThreadId</span>, <span style="color:#5f5fff">myThreadId</span>, <span style="color:#5f5fff">threadDelay</span>)
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> Control.Exception (<span style="color:#5f5fff">AsyncException</span> (<span style="color:#5f5fff">UserInterrupt</span>), <span style="color:#5f5fff">catch</span>, <span style="color:#5f5fff">throwIO</span>, <span style="color:#5f5fff">throwTo</span>)
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> System.Exit (<span style="color:#5f5fff">exitSuccess</span>)
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> System.Posix.Signals (<span style="color:#5f5fff">Handler</span> (<span style="color:#5f5fff">Catch</span>), <span style="color:#5f5fff">installHandler</span>, <span style="color:#5f5fff">sigINT</span>)
</span></span><span style="display:flex;"><span><span style="color:#ec0000">import</span> System.Process (<span style="color:#5f5fff">callCommand</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">clearScreen</span> <span style="color:#ec0000">::</span> <span style="color:#5f5fff">IO</span> <span style="color:#ec0000">()</span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">clearScreen</span> <span style="color:#ec0000">=</span> callCommand <span style="color:#008900">&#34;clear&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">handleSigInt</span> <span style="color:#ec0000">::</span> <span style="color:#5f5fff">ThreadId</span> <span style="color:#ec0000">-&gt;</span> <span style="color:#5f5fff">IO</span> <span style="color:#ec0000">()</span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">handleSigInt</span> mainThreadId <span style="color:#ec0000">=</span> throwTo mainThreadId <span style="color:#5f5fff">UserInterrupt</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">nextWorld</span> <span style="color:#ec0000">::</span> <span style="color:#5f5fff">Int</span> <span style="color:#ec0000">-&gt;</span> <span style="color:#5f5fff">World</span> <span style="color:#ec0000">-&gt;</span> <span style="color:#5f5fff">IO</span> <span style="color:#ec0000">()</span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">nextWorld</span> generation world <span style="color:#ec0000">=</span> <span style="color:#ec0000">do</span>
</span></span><span style="display:flex;"><span>  clearScreen
</span></span><span style="display:flex;"><span>  <span style="color:#ec0000">let</span> currentGeneration <span style="color:#ec0000">=</span> generation <span style="color:#ec0000">+</span> <span style="color:#008900">1</span>
</span></span><span style="display:flex;"><span>  putStr (show world)
</span></span><span style="display:flex;"><span>  putStrLn (<span style="color:#008900">&#34;Generation &#34;</span> <span style="color:#ec0000">++</span> show currentGeneration)
</span></span><span style="display:flex;"><span>  threadDelay <span style="color:#008900">500000</span>
</span></span><span style="display:flex;"><span>  nextWorld currentGeneration (evolve world)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">main</span> <span style="color:#ec0000">::</span> <span style="color:#5f5fff">IO</span> <span style="color:#ec0000">()</span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">main</span> <span style="color:#ec0000">=</span> <span style="color:#ec0000">do</span>
</span></span><span style="display:flex;"><span>  mainThreadId <span style="color:#ec0000">&lt;-</span> myThreadId
</span></span><span style="display:flex;"><span>  <span style="color:#ec0000">_</span> <span style="color:#ec0000">&lt;-</span> installHandler sigINT (<span style="color:#5f5fff">Catch</span> (handleSigInt mainThreadId)) <span style="color:#5f5fff">Nothing</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ec0000">let</span> aliveCells <span style="color:#ec0000">=</span>
</span></span><span style="display:flex;"><span>        blinker (<span style="color:#5f5fff">Coord</span> <span style="color:#008900">0</span> <span style="color:#008900">1</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#ec0000">++</span> beacon (<span style="color:#5f5fff">Coord</span> <span style="color:#008900">10</span> <span style="color:#008900">10</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#ec0000">++</span> glider (<span style="color:#5f5fff">Coord</span> <span style="color:#008900">4</span> <span style="color:#008900">5</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#ec0000">++</span> block (<span style="color:#5f5fff">Coord</span> <span style="color:#008900">1</span> <span style="color:#008900">10</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#ec0000">++</span> block (<span style="color:#5f5fff">Coord</span> <span style="color:#008900">18</span> <span style="color:#008900">3</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#ec0000">++</span> tub (<span style="color:#5f5fff">Coord</span> <span style="color:#008900">6</span> <span style="color:#008900">1</span>)
</span></span><span style="display:flex;"><span>      world <span style="color:#ec0000">=</span> newWorld <span style="color:#008900">30</span> aliveCells
</span></span><span style="display:flex;"><span>  nextWorld <span style="color:#008900">0</span> world `catch` handleUserInterrupt
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">handleUserInterrupt</span> <span style="color:#ec0000">::</span> <span style="color:#5f5fff">AsyncException</span> <span style="color:#ec0000">-&gt;</span> <span style="color:#5f5fff">IO</span> <span style="color:#ec0000">()</span>
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">handleUserInterrupt</span> <span style="color:#5f5fff">UserInterrupt</span> <span style="color:#ec0000">=</span> <span style="color:#ec0000">do</span>
</span></span><span style="display:flex;"><span>  clearScreen
</span></span><span style="display:flex;"><span>  exitSuccess
</span></span><span style="display:flex;"><span><span style="color:#5f5fff">handleUserInterrupt</span> exception <span style="color:#ec0000">=</span> throwIO exception
</span></span></code></pre></div><p>The final result shares quite a few similarities with F#; it&rsquo;s easy to see that there&rsquo;s a lot of inspiration from Haskell in F#, with the obvious exceptions of .NET-specific features like attributes and such.</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-fsharp" data-lang="fsharp"><span style="display:flex;"><span><span style="color:#ec0000">open</span> System
</span></span><span style="display:flex;"><span><span style="color:#ec0000">open</span> System.Threading
</span></span><span style="display:flex;"><span><span style="color:#ec0000">open</span> ConwayLib
</span></span><span style="display:flex;"><span><span style="color:#ec0000">open</span> Coord
</span></span><span style="display:flex;"><span><span style="color:#ec0000">open</span> World
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">[&lt;</span>EntryPoint<span style="color:#ec0000">&gt;]</span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">let</span> <span style="color:#ec0000">main</span> argv <span style="color:#ec0000">=</span>
</span></span><span style="display:flex;"><span>    Console.CancelKeyPress.Add <span style="color:#ec0000">(</span><span style="color:#ec0000">fun</span> arg <span style="color:#ec0000">-&gt;</span>
</span></span><span style="display:flex;"><span>        Console.Clear<span style="color:#ec0000">()</span>
</span></span><span style="display:flex;"><span>        Environment.Exit<span style="color:#ec0000">(</span>0<span style="color:#ec0000">))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">let</span> <span style="color:#ec0000">blinker</span> <span style="color:#ec0000">=</span> blinker <span style="color:#ec0000">{</span> X <span style="color:#ec0000">=</span> 0<span style="color:#ec0000">;</span> Y <span style="color:#ec0000">=</span> 1 <span style="color:#ec0000">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">let</span> <span style="color:#ec0000">beacon</span> <span style="color:#ec0000">=</span> beacon <span style="color:#ec0000">{</span> X <span style="color:#ec0000">=</span> 10<span style="color:#ec0000">;</span> Y <span style="color:#ec0000">=</span> 10 <span style="color:#ec0000">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">let</span> <span style="color:#ec0000">glider</span> <span style="color:#ec0000">=</span> glider <span style="color:#ec0000">{</span> X <span style="color:#ec0000">=</span> 4<span style="color:#ec0000">;</span> Y <span style="color:#ec0000">=</span> 5 <span style="color:#ec0000">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">let</span> <span style="color:#ec0000">block1</span> <span style="color:#ec0000">=</span> block <span style="color:#ec0000">{</span> X <span style="color:#ec0000">=</span> 1<span style="color:#ec0000">;</span> Y <span style="color:#ec0000">=</span> 10 <span style="color:#ec0000">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">let</span> <span style="color:#ec0000">block2</span> <span style="color:#ec0000">=</span> block <span style="color:#ec0000">{</span> X <span style="color:#ec0000">=</span> 18<span style="color:#ec0000">;</span> Y <span style="color:#ec0000">=</span> 3 <span style="color:#ec0000">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">let</span> <span style="color:#ec0000">tub</span> <span style="color:#ec0000">=</span> tub <span style="color:#ec0000">{</span> X <span style="color:#ec0000">=</span> 6<span style="color:#ec0000">;</span> Y <span style="color:#ec0000">=</span> 1 <span style="color:#ec0000">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">let</span> <span style="color:#ec0000">aliveCells</span> <span style="color:#ec0000">=</span> blinker <span style="color:#ec0000">@</span> beacon <span style="color:#ec0000">@</span> glider <span style="color:#ec0000">@</span> block1 <span style="color:#ec0000">@</span> block2 <span style="color:#ec0000">@</span> tub
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">let</span> <span style="color:#ec0000">rec</span> iterate world generation <span style="color:#ec0000">=</span>
</span></span><span style="display:flex;"><span>        Console.Clear<span style="color:#ec0000">()</span>
</span></span><span style="display:flex;"><span>        printfn <span style="color:#008900">&#34;%s&#34;</span> <span style="color:#ec0000">(</span>worldDescription world<span style="color:#ec0000">)</span>
</span></span><span style="display:flex;"><span>        printfn <span style="color:#008900">&#34;Generation %d&#34;</span> <span style="color:#ec0000">(</span>generation<span style="color:#ec0000">)</span>
</span></span><span style="display:flex;"><span>        Thread.Sleep<span style="color:#ec0000">(</span>500<span style="color:#ec0000">)</span>
</span></span><span style="display:flex;"><span>        iterate <span style="color:#ec0000">(</span>evolve world<span style="color:#ec0000">)</span> <span style="color:#ec0000">(</span>generation <span style="color:#ec0000">+</span> 1<span style="color:#ec0000">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    iterate <span style="color:#ec0000">(</span>createWorld 30 aliveCells<span style="color:#ec0000">)</span> 1
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    // return an integer exit code
</span></span><span style="display:flex;"><span>    0
</span></span></code></pre></div><p>This similarity is no coincidence. <a href="https://en.wikipedia.org/wiki/Don_Syme">Don Syme</a>, designer and architect of the F# programming language at Microsoft, acknowledged this legacy in his paper <a href="https://dl.acm.org/doi/10.1145/3386325">&ldquo;The early history of F#&rdquo;</a> submitted for HOPL IV in 2020.</p>
<blockquote>
<p>The history of the F# programming language is an arc drawn from the 1970s to the present day. Somewhere, back in the early 1970s, an idea was born in the mind of Robin Milner and his colleagues Lockwood Morris and Malcolm Newey of a succinct, fully type-inferred functional programming language suitable for manipulating structured information [Gordon 2000]. Building on the tradition of LISP (and indeed using LISP as their implementation vehicle), this language became ML Meta Language and is the root of a tradition of strongly typed functional programming languages that includes Edinburgh ML, Miranda, Haskell, Standard ML, OCaml, Elm, ReasonML and PureScript. F# is part of this family. <br>
(…) <br>
Initially, in late 2000, in conjunction with Reuben Thomas, I attempted an implementation of Haskell for .NET, using a direct translation from the Core intermediate representation of the Glasgow Haskell Compiler (GHC) to the .NET bytecode. This experience was partly successful: small programs ran. However, the advice of Simon Peyton Jones led me to believe that Haskell.NET couldn&rsquo;t be successful for several technical and cultural reasons: (…)</p>
</blockquote>
]]></content:encoded></item><item><title>Neovim</title><link>https://akos.ma/blog/neovim/</link><pubDate>Fri, 12 Jun 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/neovim/</guid><description><![CDATA[ <p>Call me late to the party if you want, but I recently adopted <a href="https://en.wikipedia.org/wiki/Neovim">Neovim</a> as my new full-time <a href="/blog/text-editors-for-work/">text editor for work</a>, and I have to say I&rsquo;m very pleased of having done so.</p>]]></description><content:encoded><![CDATA[ <p>Call me late to the party if you want, but I recently adopted <a href="https://en.wikipedia.org/wiki/Neovim">Neovim</a> as my new full-time <a href="/blog/text-editors-for-work/">text editor for work</a>, and I have to say I&rsquo;m very pleased of having done so.</p>
<p>In case you don&rsquo;t know (which given my audience, I&rsquo;d be surprised to hear) Neovim is a successor of Vim, built since 2014 on a fork of version 7 point something of Vim.</p>
<p>The good news is that it is almost 100% compatible with Vim, and that means I could reuse my good old <code>.vimrc</code>, using the following initialization file in <code>~/.config/nvim/init.vim</code></p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-viml" data-lang="viml"><span style="display:flex;"><span><span style="color:#ec0000">set</span> runtimepath^=~<span style="color:#008900">/.vim runtimepath+=~/</span>.vim/after
</span></span><span style="display:flex;"><span><span style="color:#ec0000">let</span> &amp;packpath = &amp;runtimepath
</span></span><span style="display:flex;"><span>source ~/.vimrc
</span></span></code></pre></div><p>And that&rsquo;s it. The only line in my <code>.vimrc</code> that didn&rsquo;t work in Neovim was the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-viml" data-lang="viml"><span style="display:flex;"><span><span style="color:#ec0000">set</span> guitablabel=%t
</span></span></code></pre></div><p>Which to be honest, I don&rsquo;t remember why it was there.</p>
<p>Most of the Vim plugins I had installed with Pathogen worked off-the-box, with the exception of Gundo, which had trouble accessing my Python environment; so I had to install a separate Python package for that:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ pip3 install --user --upgrade pynvim
</span></span></code></pre></div><p>In my Fedora 44, I also installed the <code>neovim-qt</code> package, which provides a similar experience to Gvim on my desktop; this app uses its own configuration file, <code>~/.config/nvim/ginit.vim</code>, which in my case ended up looking like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#757575;background-color:#000;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-viml" data-lang="viml"><span style="display:flex;"><span>&#34; Define your default font name and size
</span></span><span style="display:flex;"><span><span style="color:#ec0000">let</span> s:default_fontsize = <span style="color:#008900">15</span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">let</span> s:current_fontsize = s:default_fontsize
</span></span><span style="display:flex;"><span><span style="color:#ec0000">let</span> s:font_name = <span style="color:#008900">&#34;BlexMono Nerd Font&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">function</span>! AdjustFontSize(amount)
</span></span><span style="display:flex;"><span>  <span style="color:#ec0000">let</span> s:current_fontsize = s:current_fontsize + a:amount
</span></span><span style="display:flex;"><span>  <span style="color:#ec0000">execute</span> <span style="color:#008900">&#34;GuiFont! &#34;</span> . s:font_name . <span style="color:#008900">&#34;:h&#34;</span> . s:current_fontsize
</span></span><span style="display:flex;"><span><span style="color:#ec0000">endfunction</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">function</span>! ResetFontSize()
</span></span><span style="display:flex;"><span>  <span style="color:#ec0000">let</span> s:current_fontsize = s:default_fontsize
</span></span><span style="display:flex;"><span>  <span style="color:#ec0000">execute</span> <span style="color:#008900">&#34;GuiFont! &#34;</span> . s:font_name . <span style="color:#008900">&#34;:h&#34;</span> . s:current_fontsize
</span></span><span style="display:flex;"><span><span style="color:#ec0000">endfunction</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&#34; Mappings for Increasing Size (Ctrl + Plus / Ctrl + Equals)
</span></span><span style="display:flex;"><span>nnoremap &lt;C-+&gt; :call AdjustFontSize(<span style="color:#008900">1</span>)&lt;CR&gt;
</span></span><span style="display:flex;"><span>nnoremap &lt;C-=&gt; :call AdjustFontSize(<span style="color:#008900">1</span>)&lt;CR&gt;
</span></span><span style="display:flex;"><span>nnoremap &lt;C-kPlus&gt; :call AdjustFontSize(<span style="color:#008900">1</span>)&lt;CR&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&#34; Mappings for Decreasing Size (Ctrl + Minus)
</span></span><span style="display:flex;"><span>nnoremap &lt;C--&gt; :call AdjustFontSize(<span style="color:#008900">-1</span>)&lt;CR&gt;
</span></span><span style="display:flex;"><span>nnoremap &lt;C-kMinus&gt; :call AdjustFontSize(<span style="color:#008900">-1</span>)&lt;CR&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&#34; Mapping for Reset (Ctrl + Zero)
</span></span><span style="display:flex;"><span>nnoremap &lt;C<span style="color:#008900">-0</span>&gt; :call ResetFontSize()&lt;CR&gt;
</span></span><span style="display:flex;"><span>nnoremap &lt;C-k0&gt; :call ResetFontSize()&lt;CR&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&#34; Toggle Fullscreen with F11
</span></span><span style="display:flex;"><span><span style="color:#ec0000">let</span> s:my_fs_state = <span style="color:#008900">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">function</span>! ToggleFullScreen()
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">if</span> s:my_fs_state == <span style="color:#008900">0</span>
</span></span><span style="display:flex;"><span>        call GuiWindowFullScreen(<span style="color:#008900">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#ec0000">let</span> s:my_fs_state = <span style="color:#008900">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">else</span>
</span></span><span style="display:flex;"><span>        call GuiWindowFullScreen(<span style="color:#008900">0</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#ec0000">let</span> s:my_fs_state = <span style="color:#008900">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ec0000">endif</span>
</span></span><span style="display:flex;"><span><span style="color:#ec0000">endfunction</span>
</span></span><span style="display:flex;"><span>nnoremap &lt;silent&gt; &lt;F11&gt; :call ToggleFullScreen()&lt;CR&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&#34; Enable standard GUI clipboard shortcuts
</span></span><span style="display:flex;"><span>nnoremap &lt;C-S-v&gt; &#34;+p
</span></span><span style="display:flex;"><span>vnoremap &lt;C-S-v&gt; &#34;+p
</span></span><span style="display:flex;"><span>inoremap &lt;C-S-v&gt; &lt;C-r&gt;+
</span></span><span style="display:flex;"><span>cnoremap &lt;C-S-v&gt; &lt;C-r&gt;+
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>call ResetFontSize()
</span></span></code></pre></div><p>Neovim feels nice, snappy, fast, very convenient, and with a much more interesting roadmap ahead.</p>
<p><img src="neovim.png" alt=""></p>
]]></content:encoded></item><item><title>Conselho</title><link>https://akos.ma/blog/conselho/</link><pubDate>Fri, 05 Jun 2026 00:00:00 +0200</pubDate><guid>https://akos.ma/blog/conselho/</guid><description> &lt;p>I always share stuff from Argentina, but after listening to &lt;a href="https://www.youtube.com/watch?v=sdvZkPP0aUc">this song&lt;/a> you might want to head towards Brazil instead.&lt;/p></description><content:encoded><![CDATA[ <p>I always share stuff from Argentina, but after listening to <a href="https://www.youtube.com/watch?v=sdvZkPP0aUc">this song</a> you might want to head towards Brazil instead.</p>
<p>I&rsquo;ve been listening to it in a loop for the last few days. It&rsquo;s like having a streaming service of sunny weather in your ears. I thought you could use some sun, too. It struck a chord in me. It made me feel good. It made me smile. I felt thankful.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/sdvZkPP0aUc?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Here goes a good translation of the lyrics in English via some IA over there:</p>
<blockquote>
<p>(Let it go) <br>
Let go of that low mood <br>
Raise your head and face evil <br>
Acting this way will be vital for your heart <br>
Because in every experience you learn a lesson <br>
I&rsquo;ve already suffered for loving like this <br>
I dedicated myself but it was all in vain <br>
<br>
(Why?) <br>
Why lament? <br>
If in your life you can find <br>
Someone who loves you with all their strength and passion <br>
Then the pain will subside <br>
<br>
(You have to fight) <br>
You have to fight <br>
Don&rsquo;t get discouraged <br>
Just give yourself <br>
To someone who deserves you <br>
I&rsquo;m not giving or selling <br>
As the saying goes <br>
My advice is to see you happy <br>
<br>
(repeat) <br>
<br>
Lala-laia <br>
Lala-laia laia, lala-laia laia, lala-laia, laia-laia-laia <br>
I&rsquo;m not giving or selling <br>
As the saying goes <br>
My advice is to see you happy <br>
My advice is to see you happy <br>
My advice is to see you happy</p>
</blockquote>
<p>I&rsquo;ve never learned Portuguese, so it&rsquo;s uncanny how much I could understand (70%, easily) of the lyrics just on the first try. The original goes below, just for the sake of beauty. Brazilian Portuguese is such a beautifully sounding language, it&rsquo;s almost obscene.</p>
<blockquote>
<p>(Deixe de lado) <br>
Deixe de lado esse baixo astral <br>
Ergua a cabeça enfrente o mal <br>
Agindo assim será vital para o seu coração <br>
É que em cada experiência se aprende uma lição <br>
Eu já sofri por amar assim <br>
Me dediquei mas foi tudo em vão <br>
<br>
(Pra que?) <br>
Pra que se lamentar? <br>
Se em sua vida pode encontrar <br>
Quem te ame com toda força e ardor <br>
Assim sucumbirá a dor <br>
<br>
(Tem que lutar) <br>
Tem que lutar <br>
Não se abater <br>
Só se entregar <br>
A quem te merecer <br>
Não estou dando nem vendendo <br>
Como o ditado diz <br>
O meu conselho é pra te ver feliz <br>
<br>
(…) <br>
<br>
Lala-laia <br>
Lala-laia laia, lala-laia laia, lala-laia, laia-laia-laia <br>
Não estou dando e nem vendendo <br>
Como o ditado diz <br>
O meu conselho é pra te ver feliz <br>
O meu conselho é pra te ver feliz <br>
O meu conselho é pra te ver feliz</p>
</blockquote>
]]></content:encoded></item></channel></rss>